aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy3
-rw-r--r--CMakeLists.txt29
-rw-r--r--addons/game.controller.default/addon.xml2
-rw-r--r--addons/game.controller.default/resources/language/resource.language.my_mm/strings.po8
-rw-r--r--addons/game.controller.keyboard/addon.xml2
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.af_za/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.am_et/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ar_sa/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ast_es/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.az_az/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.be_by/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.bg_bg/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.bs_ba/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ca_es/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.cs_cz/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.cy_gb/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.da_dk/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.de_de/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.el_gr/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.en_au/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.en_gb/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.en_nz/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.en_us/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.eo/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.es_ar/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.es_es/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.es_mx/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.et_ee/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.eu_es/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.fa_af/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.fa_ir/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.fi_fi/strings.po12
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.fo_fo/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.fr_ca/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.fr_fr/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.gl_es/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.he_il/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.hi_in/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.hr_hr/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.hu_hu/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.hy_am/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.id_id/strings.po12
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.is_is/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.it_it/strings.po20
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ja_jp/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.kn_in/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ko_kr/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.lt_lt/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.lv_lv/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.mi/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.mk_mk/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ml_in/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.mn_mn/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ms_my/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.mt_mt/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.my_mm/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.nb_no/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.nl_nl/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.oc_fr/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.os_os/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.pl_pl/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.pt_br/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.pt_pt/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ro_ro/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ru_ru/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.si_lk/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.sk_sk/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.sl_si/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.sq_al/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.sr_rs/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.sr_rs@latin/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.sv_se/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.szl/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.ta_in/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.te_in/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.tg_tj/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.th_th/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.tr_tr/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.uk_ua/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.uz_uz/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.vi_vn/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.zh_cn/strings.po6
-rw-r--r--addons/game.controller.keyboard/resources/language/resource.language.zh_tw/strings.po4
-rw-r--r--addons/game.controller.keyboard/resources/layout.xml1
-rw-r--r--addons/game.controller.mouse/addon.xml2
-rw-r--r--addons/game.controller.mouse/resources/language/resource.language.ca_es/strings.po11
-rw-r--r--addons/game.controller.mouse/resources/language/resource.language.da_dk/strings.po6
-rw-r--r--addons/game.controller.mouse/resources/language/resource.language.it_it/strings.po6
-rw-r--r--addons/metadata.generic.artists/addon.xml4
-rw-r--r--addons/metadata.generic.artists/changelog.txt14
-rw-r--r--addons/metadata.generic.artists/lib/scraper.py37
-rw-r--r--addons/metadata.generic.artists/lib/theaudiodb.py30
-rw-r--r--addons/metadata.generic.artists/lib/utils.py1
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.af_za/strings.po77
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.am_et/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ar_sa/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ast_es/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.az_az/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.be_by/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.bg_bg/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.bs_ba/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ca_es/strings.po82
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.cs_cz/strings.po75
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.cy_gb/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.da_dk/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.de_de/strings.po84
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.el_gr/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.en_au/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.en_gb/strings.po18
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.en_nz/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.en_us/strings.po77
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.eo/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.es_ar/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.es_es/strings.po91
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.es_mx/strings.po82
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.et_ee/strings.po95
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.eu_es/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fa_af/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fa_ir/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fi_fi/strings.po83
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fil/strings.po85
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fo_fo/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fr_ca/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.fr_fr/strings.po99
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.gl_es/strings.po82
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.he_il/strings.po68
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.hi_in/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.hr_hr/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.hu_hu/strings.po68
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.hy_am/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.id_id/strings.po78
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.is_is/strings.po82
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.it_it/strings.po94
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ja_jp/strings.po64
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.kn_in/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ko_kr/strings.po57
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.lt_lt/strings.po81
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.lv_lv/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.mi/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.mk_mk/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ml_in/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.mn_mn/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ms_my/strings.po54
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.mt_mt/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.my_mm/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.nb_no/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.nl_nl/strings.po82
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.pl_pl/strings.po71
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.pt_br/strings.po89
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.pt_pt/strings.po57
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ro_ro/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ru_ru/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.si_lk/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.sk_sk/strings.po77
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.sl_si/strings.po47
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.sq_al/strings.po75
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.sr_rs/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.sr_rs@latin/strings.po49
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.sv_se/strings.po78
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.szl/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.ta_in/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.te_in/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.tg_tj/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.th_th/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.tr_tr/strings.po80
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.uk_ua/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.uz_uz/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.vi_vn/strings.po44
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.zh_cn/strings.po57
-rw-r--r--addons/metadata.generic.artists/resources/language/resource.language.zh_tw/strings.po57
-rw-r--r--addons/repository.xbmc.org/addon.xml2
-rw-r--r--addons/repository.xbmc.org/resources/language/resource.language.et_ee/strings.po8
-rw-r--r--addons/repository.xbmc.org/resources/language/resource.language.hu_hu/strings.po6
-rw-r--r--addons/resource.language.en_gb/resources/strings.po697
-rw-r--r--addons/screensaver.xbmc.builtin.dim/addon.xml2
-rw-r--r--addons/screensaver.xbmc.builtin.dim/resources/language/resource.language.de_de/strings.po10
-rw-r--r--addons/skin.estouchy/addon.xml4
-rw-r--r--addons/skin.estouchy/language/resource.language.en_us/strings.po8
-rw-r--r--addons/skin.estouchy/language/resource.language.hu_hu/strings.po4
-rw-r--r--addons/skin.estouchy/language/resource.language.sk_sk/strings.po12
-rw-r--r--addons/skin.estouchy/language/resource.language.sv_se/strings.po12
-rw-r--r--addons/skin.estouchy/language/resource.language.vi_vn/strings.po10
-rw-r--r--addons/skin.estouchy/media/DefaultFavourites.pngbin0 -> 2107 bytes
-rw-r--r--addons/skin.estouchy/media/flagging/video/theora.pngbin0 -> 1515 bytes
-rw-r--r--addons/skin.estouchy/media/icon_breadcrumb_favourites.pngbin0 -> 828 bytes
-rw-r--r--addons/skin.estouchy/xml/DialogFavourites.xml148
-rw-r--r--addons/skin.estouchy/xml/FileManager.xml2
-rw-r--r--addons/skin.estouchy/xml/Includes.xml3
-rw-r--r--addons/skin.estouchy/xml/MyFavourites.xml46
-rw-r--r--addons/skin.estouchy/xml/MyWeather.xml2
-rw-r--r--addons/skin.estouchy/xml/Settings.xml2
-rw-r--r--addons/skin.estouchy/xml/SettingsCategory.xml2
-rw-r--r--addons/skin.estouchy/xml/SettingsProfile.xml2
-rw-r--r--addons/skin.estouchy/xml/SkinSettings.xml2
-rw-r--r--addons/skin.estouchy/xml/ViewsList.xml4
-rw-r--r--addons/skin.estouchy/xml/ViewsThumbnail.xml74
-rw-r--r--addons/skin.estuary/addon.xml4
-rw-r--r--addons/skin.estuary/colors/brown.xml3
-rw-r--r--addons/skin.estuary/colors/charcoal.xml1
-rw-r--r--addons/skin.estuary/colors/chartreuse.xml1
-rw-r--r--addons/skin.estuary/colors/concrete.xml1
-rw-r--r--addons/skin.estuary/colors/defaults.xml1
-rw-r--r--addons/skin.estuary/colors/gold.xml1
-rw-r--r--addons/skin.estuary/colors/green.xml1
-rw-r--r--addons/skin.estuary/colors/maroon.xml1
-rw-r--r--addons/skin.estuary/colors/midnight.xml1
-rw-r--r--addons/skin.estuary/colors/orange.xml1
-rw-r--r--addons/skin.estuary/colors/pink.xml1
-rw-r--r--addons/skin.estuary/colors/rose.xml1
-rw-r--r--addons/skin.estuary/colors/teal.xml1
-rw-r--r--addons/skin.estuary/colors/violet.xml1
-rw-r--r--addons/skin.estuary/language/resource.language.af_za/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.am_et/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.ar_sa/strings.po38
-rw-r--r--addons/skin.estuary/language/resource.language.ast_es/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.az_az/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.be_by/strings.po36
-rw-r--r--addons/skin.estuary/language/resource.language.bg_bg/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.bs_ba/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.ca_es/strings.po44
-rw-r--r--addons/skin.estuary/language/resource.language.cs_cz/strings.po62
-rw-r--r--addons/skin.estuary/language/resource.language.cy_gb/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.da_dk/strings.po40
-rw-r--r--addons/skin.estuary/language/resource.language.de_de/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.el_gr/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.en_au/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.en_gb/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.en_nz/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.en_us/strings.po52
-rw-r--r--addons/skin.estuary/language/resource.language.eo/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.es_ar/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.es_es/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.es_mx/strings.po58
-rw-r--r--addons/skin.estuary/language/resource.language.et_ee/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.eu_es/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.fa_af/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.fa_ir/strings.po36
-rw-r--r--addons/skin.estuary/language/resource.language.fi_fi/strings.po48
-rw-r--r--addons/skin.estuary/language/resource.language.fil/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.fo_fo/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.fr_ca/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.fr_fr/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.gl_es/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.he_il/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.hi_in/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.hr_hr/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.hu_hu/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.hy_am/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.id_id/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.is_is/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.it_it/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.ja_jp/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.kn_in/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.ko_kr/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.lt_lt/strings.po36
-rw-r--r--addons/skin.estuary/language/resource.language.lv_lv/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.mi/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.mk_mk/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.ml_in/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.mn_mn/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.ms_my/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.mt_mt/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.my_mm/strings.po36
-rw-r--r--addons/skin.estuary/language/resource.language.nb_no/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.nl_nl/strings.po44
-rw-r--r--addons/skin.estuary/language/resource.language.pl_pl/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.pt_br/strings.po50
-rw-r--r--addons/skin.estuary/language/resource.language.pt_pt/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.ro_ro/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.ru_ru/strings.po38
-rw-r--r--addons/skin.estuary/language/resource.language.si_lk/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.sk_sk/strings.po60
-rw-r--r--addons/skin.estuary/language/resource.language.sl_si/strings.po36
-rw-r--r--addons/skin.estuary/language/resource.language.sq_al/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.sr_rs/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.sr_rs@latin/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.sv_se/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.szl/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.ta_in/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.te_in/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.tg_tj/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.th_th/strings.po34
-rw-r--r--addons/skin.estuary/language/resource.language.tr_tr/strings.po38
-rw-r--r--addons/skin.estuary/language/resource.language.uk_ua/strings.po36
-rw-r--r--addons/skin.estuary/language/resource.language.uz_uz/strings.po28
-rw-r--r--addons/skin.estuary/language/resource.language.vi_vn/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.zh_cn/strings.po42
-rw-r--r--addons/skin.estuary/language/resource.language.zh_tw/strings.po42
-rw-r--r--addons/skin.estuary/media/DefaultVideoVersions.pngbin0 -> 11759 bytes
-rw-r--r--addons/skin.estuary/media/flags/videocodec/theora.pngbin0 -> 1515 bytes
-rw-r--r--addons/skin.estuary/media/icons/infodialogs/versions.pngbin0 -> 1125 bytes
-rw-r--r--addons/skin.estuary/media/overlays/versions.pngbin0 -> 868 bytes
-rw-r--r--addons/skin.estuary/xml/Custom_1101_SettingsList.xml2
-rw-r--r--addons/skin.estuary/xml/DialogFavourites.xml76
-rw-r--r--addons/skin.estuary/xml/DialogMusicInfo.xml7
-rw-r--r--addons/skin.estuary/xml/DialogSeekBar.xml10
-rw-r--r--addons/skin.estuary/xml/DialogVideoInfo.xml6
-rw-r--r--addons/skin.estuary/xml/DialogVideoVersion.xml10
-rw-r--r--addons/skin.estuary/xml/FileManager.xml6
-rw-r--r--addons/skin.estuary/xml/GameOSD.xml4
-rw-r--r--addons/skin.estuary/xml/Includes.xml110
-rw-r--r--addons/skin.estuary/xml/Includes_Buttons.xml32
-rw-r--r--addons/skin.estuary/xml/Includes_DialogVideoVersion.xml185
-rw-r--r--addons/skin.estuary/xml/Includes_Games.xml138
-rw-r--r--addons/skin.estuary/xml/Includes_Home.xml2
-rw-r--r--addons/skin.estuary/xml/MyMusicNav.xml8
-rw-r--r--addons/skin.estuary/xml/MyPVRRecordings.xml2
-rw-r--r--addons/skin.estuary/xml/MyWeather.xml37
-rw-r--r--addons/skin.estuary/xml/SettingsCategory.xml2
-rw-r--r--addons/skin.estuary/xml/Variables.xml17
-rw-r--r--addons/skin.estuary/xml/View_53_Shift.xml289
-rw-r--r--addons/xbmc.gui/addon.xml2
-rw-r--r--cmake/modules/FindAcbAPI.cmake42
-rw-r--r--cmake/modules/FindCEC.cmake168
-rw-r--r--cmake/modules/FindEGL.cmake5
-rw-r--r--cmake/modules/FindFmt.cmake186
-rw-r--r--cmake/modules/FindGLX.cmake3
-rw-r--r--cmake/modules/FindIconv.cmake14
-rw-r--r--cmake/modules/FindOpenGLES.cmake10
-rw-r--r--cmake/modules/FindP8Platform.cmake113
-rw-r--r--cmake/modules/FindPCRE.cmake59
-rw-r--r--cmake/modules/FindPulseAudio.cmake2
-rw-r--r--cmake/modules/FindPython.cmake12
-rw-r--r--cmake/modules/FindRapidJSON.cmake86
-rw-r--r--cmake/modules/FindSdl.cmake29
-rw-r--r--cmake/modules/FindSmctemp.cmake30
-rw-r--r--cmake/modules/buildtools/FindPythonInterpreter.cmake56
-rw-r--r--cmake/platform/android/android.cmake1
-rw-r--r--cmake/platform/darwin_embedded/ios.cmake1
-rw-r--r--cmake/platform/darwin_embedded/tvos.cmake2
-rw-r--r--cmake/platform/linux/webos.cmake3
-rw-r--r--cmake/platform/osx/osx.cmake15
-rw-r--r--cmake/platform/windows/windows.cmake2
-rw-r--r--cmake/platform/windowsstore/windowsstore.cmake3
-rw-r--r--cmake/scripts/android/ArchSetup.cmake1
-rw-r--r--cmake/scripts/common/Macros.cmake27
-rw-r--r--cmake/scripts/common/ModuleHelpers.cmake22
-rw-r--r--cmake/scripts/darwin_embedded/ArchSetup.cmake3
-rw-r--r--cmake/scripts/darwin_embedded/Install.cmake2
-rw-r--r--cmake/scripts/freebsd/ArchSetup.cmake2
-rw-r--r--cmake/scripts/linux/Install.cmake11
-rw-r--r--cmake/scripts/osx/Install.cmake5
-rw-r--r--cmake/scripts/webos/Install.cmake5
-rw-r--r--cmake/treedata/common/subdirs.txt1
-rw-r--r--cmake/treedata/osx/subdirs.txt2
-rw-r--r--docs/CODE_GUIDELINES.md25
-rw-r--r--docs/README.Android.md21
-rw-r--r--docs/README.Linux.md4
-rw-r--r--docs/README.macOS.md15
-rw-r--r--media/applaunch_screen.pngbin0 -> 10687 bytes
-rw-r--r--media/splash.jpgbin380672 -> 379662 bytes
-rw-r--r--media/splash_webOS.pngbin146198 -> 0 bytes
-rw-r--r--project/BuildDependencies/scripts/0_package.target-win32.list1
-rw-r--r--project/BuildDependencies/scripts/0_package.target-x64.list1
-rw-r--r--system/keymaps/keyboard.xml9
-rw-r--r--system/keymaps/remote.xml7
-rw-r--r--system/library/video/movies/versions.xml7
-rwxr-xr-xsystem/settings/settings.xml124
-rw-r--r--tools/Linux/kodi.metainfo.xml.in3
-rw-r--r--tools/Linux/kodi.sh.in16
-rw-r--r--tools/android/packaging/Makefile.in8
-rw-r--r--tools/android/packaging/xbmc/res/layout/activity_splash.xml2
-rw-r--r--tools/android/packaging/xbmc/res/values-ar-rsa/strings.xml3
-rwxr-xr-xtools/buildsteps/osx-arm64/configure-xbmc2
-rwxr-xr-xtools/buildsteps/osx64/configure-xbmc2
-rw-r--r--tools/buildsteps/windows/patches/0005-ffmpeg-windows-dxva2-check-nullptr-surface.patch12
-rwxr-xr-xtools/darwin/Support/copyframeworks-darwin_embedded.command23
-rwxr-xr-xtools/darwin/packaging/osx/mkdmg-osx.sh.in2
-rwxr-xr-xtools/darwin/packaging/osx/notarize.sh71
-rw-r--r--tools/depends/Makefile.include.in1
-rw-r--r--tools/depends/configure.ac25
-rw-r--r--tools/depends/native/Makefile2
-rw-r--r--tools/depends/native/openssl/Makefile12
-rw-r--r--tools/depends/native/openssl/OPENSSL-VERSION4
-rw-r--r--tools/depends/native/python3/PYTHON3-VERSION4
-rw-r--r--tools/depends/native/wayland-scanner/Makefile37
-rw-r--r--tools/depends/native/wayland-scanner/WAYLAND-SCANNER-VERSION4
-rw-r--r--tools/depends/native/waylandpp-scanner/Makefile12
-rw-r--r--tools/depends/native/waylandpp-scanner/WAYLANDPP-SCANNER-VERSION4
-rw-r--r--tools/depends/target/Makefile18
-rw-r--r--tools/depends/target/Toolchain.cmake.in2
-rw-r--r--tools/depends/target/boblight/01-fix_fpermissive.patch11
-rw-r--r--tools/depends/target/boblight/02-fixandroid.patch35
-rw-r--r--tools/depends/target/boblight/03-fixtvos.patch42
-rw-r--r--tools/depends/target/boblight/Makefile57
-rw-r--r--tools/depends/target/cec/001-all-cmakelists.patch40
-rw-r--r--tools/depends/target/cec/002-all-libceccmakelists.patch42
-rw-r--r--tools/depends/target/cec/003-all-remove_git_info.patch (renamed from tools/depends/target/libcec/remove_git_info.patch)2
-rw-r--r--tools/depends/target/cec/004-win-remove_32bit_timet.patch10
-rw-r--r--tools/depends/target/cec/005-win-pdbstatic.patch58
-rw-r--r--tools/depends/target/cec/CEC-VERSION6
-rw-r--r--tools/depends/target/cec/Makefile47
-rw-r--r--tools/depends/target/cmakebuildsys/Makefile10
-rw-r--r--tools/depends/target/curl/CURL-VERSION4
-rw-r--r--tools/depends/target/curl/Makefile29
-rw-r--r--tools/depends/target/ffmpeg/FFMPEG-VERSION4
-rw-r--r--tools/depends/target/ffmpeg/Makefile6
-rw-r--r--tools/depends/target/libcec/Makefile45
-rw-r--r--tools/depends/target/libsdl/01-SDL_SetWidthHeight.patch28
-rw-r--r--tools/depends/target/libsdl/02-OSX_interpretKeyEvents.patch15
-rw-r--r--tools/depends/target/libsdl/03-mavericks-compile.patch12
-rw-r--r--tools/depends/target/libsdl/04-fix_external_screen_crash.patch11
-rw-r--r--tools/depends/target/libsdl/Makefile45
-rw-r--r--tools/depends/target/openssl/Makefile13
-rw-r--r--tools/depends/target/openssl/OPENSSL-VERSION4
-rw-r--r--tools/depends/target/p8-platform/001-all-fix-c++17-support.patch (renamed from tools/depends/target/p8-platform/0001-fix-c++17-support.patch)0
-rw-r--r--tools/depends/target/p8-platform/002-all-fixcmakeinstall.patch29
-rw-r--r--tools/depends/target/p8-platform/003-all-cmake_tweakversion.patch21
-rw-r--r--tools/depends/target/p8-platform/Makefile23
-rw-r--r--tools/depends/target/p8-platform/P8-PLATFORM-VERSION6
-rw-r--r--tools/depends/target/pcre/004-win-pdb.patch4
-rw-r--r--tools/depends/target/python3/PYTHON3-VERSION4
-rw-r--r--tools/depends/target/pythonmodule-pil/Makefile4
-rw-r--r--tools/depends/target/pythonmodule-pycryptodome/Makefile1
-rw-r--r--tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch15
-rw-r--r--tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch (renamed from tools/depends/target/rapidjson/002-cmake-removedocs-examples.patch)0
-rw-r--r--tools/depends/target/rapidjson/004-win-arm64.patch (renamed from tools/depends/target/rapidjson/003-win-arm64.patch)0
-rw-r--r--tools/depends/target/rapidjson/Makefile10
-rw-r--r--tools/depends/target/smctemp/Makefile30
-rw-r--r--tools/depends/target/smctemp/SMCTEMP-VERSION5
-rw-r--r--tools/depends/target/wayland-protocols/Makefile4
-rw-r--r--tools/depends/target/wayland/Makefile50
-rw-r--r--tools/depends/target/wayland/WAYLAND-VERSION4
-rw-r--r--tools/depends/target/waylandpp/Makefile11
-rw-r--r--tools/depends/target/waylandpp/WAYLANDPP-VERSION4
-rw-r--r--tools/webOS/packaging/appinfo.json.in5
-rw-r--r--version.txt6
-rw-r--r--xbmc/CompileInfo.cpp.in5
-rw-r--r--xbmc/CompileInfo.h1
-rw-r--r--xbmc/ContextMenuManager.cpp57
-rw-r--r--xbmc/ContextMenuManager.h58
-rw-r--r--xbmc/ContextMenus.cpp4
-rw-r--r--xbmc/DatabaseManager.cpp14
-rw-r--r--xbmc/DatabaseManager.h2
-rw-r--r--xbmc/DbUrl.h3
-rw-r--r--xbmc/FileItem.cpp269
-rw-r--r--xbmc/FileItem.h10
-rw-r--r--xbmc/GUIInfoManager.cpp73
-rw-r--r--xbmc/LangInfo.cpp2
-rw-r--r--xbmc/ServiceManager.cpp77
-rw-r--r--xbmc/ServiceManager.h21
-rw-r--r--xbmc/TextureCache.cpp5
-rw-r--r--xbmc/TextureCacheJob.cpp3
-rw-r--r--xbmc/TextureCacheJob.h20
-rw-r--r--xbmc/Util.cpp107
-rw-r--r--xbmc/Util.h20
-rw-r--r--xbmc/addons/AddonInstaller.cpp9
-rw-r--r--xbmc/addons/AddonUpdateRules.cpp1
-rw-r--r--xbmc/addons/Scraper.cpp60
-rw-r--r--xbmc/addons/Scraper.h7
-rw-r--r--xbmc/addons/Skin.cpp5
-rw-r--r--xbmc/addons/addoninfo/AddonExtensions.cpp6
-rw-r--r--xbmc/addons/binary-addons/AddonDll.cpp1
-rw-r--r--xbmc/addons/gui/GUIDialogAddonInfo.cpp4
-rw-r--r--xbmc/addons/kodi-dev-kit/doxygen/Doxyfile2
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h26
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h2
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h1
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/versions.h2
-rw-r--r--xbmc/addons/settings/AddonSettings.cpp54
-rw-r--r--xbmc/addons/settings/AddonSettings.h12
-rw-r--r--xbmc/application/AppParams.h4
-rw-r--r--xbmc/application/Application.cpp258
-rw-r--r--xbmc/application/ApplicationPlayerCallback.cpp6
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp12
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp4
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp2
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp3
-rw-r--r--xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp4
-rw-r--r--xbmc/cores/RetroPlayer/RetroPlayer.cpp11
-rw-r--r--xbmc/cores/RetroPlayer/guicontrols/GUIGameControl.dox52
-rw-r--r--xbmc/cores/RetroPlayer/playback/ReversiblePlayback.cpp3
-rw-r--r--xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp4
-rw-r--r--xbmc/cores/RetroPlayer/savestates/SavestateFlatBuffer.cpp17
-rw-r--r--xbmc/cores/VideoPlayer/DVDClock.cpp3
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h3
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp78
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h1
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp9
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.cpp71
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.h4
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp22
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.h5
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp10
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.h1
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp19
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.h2
-rw-r--r--xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp10
-rw-r--r--xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp20
-rw-r--r--xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxVobsub.cpp4
-rw-r--r--xbmc/cores/VideoPlayer/DVDDemuxers/DemuxMultiSource.cpp8
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/DVDFactoryInputStream.cpp39
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.cpp6
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.h2
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp31
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp18
-rw-r--r--xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitleParser.h2
-rw-r--r--xbmc/cores/VideoPlayer/Edl.cpp36
-rw-r--r--xbmc/cores/VideoPlayer/Process/ProcessInfo.cpp4
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayer.cpp65
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayer.h12
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp3
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayerTeletext.cpp13
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp4
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp8
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h4
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp4
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp15
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h1
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp4
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.cpp44
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.h1
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.cpp6
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.cpp8
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVDPAU.cpp3
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp4
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp2
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp30
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp30
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp10
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/CMakeLists.txt8
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.cpp54
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.h23
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp42
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h1
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp42
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h1
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp42
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h1
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/WinRenderer.cpp19
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp18
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h3
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp14
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h1
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.cpp19
-rw-r--r--xbmc/cores/paplayer/PAPlayer.cpp3
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreFactory.cpp27
-rw-r--r--xbmc/cores/playercorefactory/PlayerCoreFactory.h1
-rw-r--r--xbmc/dbwrappers/Database.cpp30
-rw-r--r--xbmc/dbwrappers/Database.h16
-rw-r--r--xbmc/dialogs/GUIDialogBusy.cpp2
-rw-r--r--xbmc/dialogs/GUIDialogBusyNoCancel.cpp2
-rw-r--r--xbmc/dialogs/GUIDialogColorPicker.cpp3
-rw-r--r--xbmc/dialogs/GUIDialogContextMenu.h5
-rw-r--r--xbmc/dialogs/GUIDialogMediaFilter.cpp6
-rw-r--r--xbmc/dialogs/GUIDialogSelect.cpp6
-rw-r--r--xbmc/events/EventLogManager.cpp3
-rw-r--r--xbmc/favourites/CMakeLists.txt2
-rw-r--r--xbmc/favourites/ContextMenus.cpp227
-rw-r--r--xbmc/favourites/ContextMenus.h42
-rw-r--r--xbmc/favourites/FavouritesService.cpp97
-rw-r--r--xbmc/favourites/FavouritesService.h8
-rw-r--r--xbmc/favourites/FavouritesUtils.cpp41
-rw-r--r--xbmc/favourites/FavouritesUtils.h5
-rw-r--r--xbmc/favourites/GUIDialogFavourites.cpp213
-rw-r--r--xbmc/favourites/GUIDialogFavourites.h43
-rw-r--r--xbmc/favourites/GUIWindowFavourites.cpp146
-rw-r--r--xbmc/filesystem/BlurayDirectory.cpp5
-rw-r--r--xbmc/filesystem/Directory.cpp35
-rw-r--r--xbmc/filesystem/Directory.h11
-rw-r--r--xbmc/filesystem/DirectoryCache.cpp2
-rw-r--r--xbmc/filesystem/FTPParse.cpp521
-rw-r--r--xbmc/filesystem/FTPParse.h1
-rw-r--r--xbmc/filesystem/File.cpp18
-rw-r--r--xbmc/filesystem/FileCache.cpp21
-rw-r--r--xbmc/filesystem/IFileTypes.h10
-rw-r--r--xbmc/filesystem/NFSFile.cpp32
-rw-r--r--xbmc/filesystem/NptXbmcFile.cpp17
-rw-r--r--xbmc/filesystem/ShoutcastFile.cpp7
-rw-r--r--xbmc/filesystem/SmartPlaylistDirectory.cpp3
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory.cpp17
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.cpp1
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.h5
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeGrouped.cpp2
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp21
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory/QueryParams.cpp3
-rw-r--r--xbmc/filesystem/VideoDatabaseDirectory/QueryParams.h2
-rw-r--r--xbmc/filesystem/XbtDirectory.cpp2
-rw-r--r--xbmc/filesystem/ZipDirectory.cpp2
-rw-r--r--xbmc/games/GameServices.cpp6
-rw-r--r--xbmc/games/GameServices.h14
-rw-r--r--xbmc/games/GameSettings.h3
-rw-r--r--xbmc/games/GameTypes.h51
-rw-r--r--xbmc/games/GameUtils.h1
-rw-r--r--xbmc/games/addons/GameClient.cpp3
-rw-r--r--xbmc/games/addons/GameClient.h1
-rw-r--r--xbmc/games/addons/GameClientCallbacks.h2
-rw-r--r--xbmc/games/addons/GameClientInGameSaves.h2
-rw-r--r--xbmc/games/addons/GameClientProperties.h1
-rw-r--r--xbmc/games/addons/GameClientSubsystem.cpp9
-rw-r--r--xbmc/games/addons/GameClientSubsystem.h2
-rw-r--r--xbmc/games/addons/GameClientTranslator.h1
-rw-r--r--xbmc/games/addons/cheevos/GameClientCheevos.h3
-rw-r--r--xbmc/games/addons/input/GameClientController.h2
-rw-r--r--xbmc/games/addons/input/GameClientDevice.h1
-rw-r--r--xbmc/games/addons/input/GameClientHardware.h1
-rw-r--r--xbmc/games/addons/input/GameClientInput.cpp11
-rw-r--r--xbmc/games/addons/input/GameClientInput.h3
-rw-r--r--xbmc/games/addons/input/GameClientJoystick.h1
-rw-r--r--xbmc/games/addons/input/GameClientKeyboard.h1
-rw-r--r--xbmc/games/addons/input/GameClientMouse.h1
-rw-r--r--xbmc/games/addons/input/GameClientPort.h1
-rw-r--r--xbmc/games/addons/input/GameClientTopology.h5
-rw-r--r--xbmc/games/addons/streams/GameClientStreamAudio.h3
-rw-r--r--xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h3
-rw-r--r--xbmc/games/addons/streams/GameClientStreamVideo.h3
-rw-r--r--xbmc/games/addons/streams/GameClientStreams.cpp6
-rw-r--r--xbmc/games/addons/streams/GameClientStreams.h3
-rw-r--r--xbmc/games/addons/streams/IGameClientStream.h3
-rw-r--r--xbmc/games/agents/GameAgent.h8
-rw-r--r--xbmc/games/agents/windows/GUIAgentList.h3
-rw-r--r--xbmc/games/agents/windows/GUIAgentWindow.h3
-rw-r--r--xbmc/games/agents/windows/IAgentList.h19
-rw-r--r--xbmc/games/controllers/Controller.h3
-rw-r--r--xbmc/games/controllers/ControllerLayout.h3
-rw-r--r--xbmc/games/controllers/ControllerManager.cpp20
-rw-r--r--xbmc/games/controllers/ControllerManager.h14
-rw-r--r--xbmc/games/controllers/ControllerTranslator.cpp4
-rw-r--r--xbmc/games/controllers/ControllerTranslator.h3
-rw-r--r--xbmc/games/controllers/ControllerTypes.h14
-rw-r--r--xbmc/games/controllers/dialogs/ControllerInstaller.h3
-rw-r--r--xbmc/games/controllers/dialogs/ControllerSelect.h4
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h3
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h3
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUICardinalFeatureButton.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIControlTypes.h2
-rw-r--r--xbmc/games/controllers/guicontrols/GUIControllerButton.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIFeatureButton.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIFeatureControls.h6
-rw-r--r--xbmc/games/controllers/guicontrols/GUIFeatureFactory.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIFeatureTranslator.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIGameController.cpp15
-rw-r--r--xbmc/games/controllers/guicontrols/GUIGameController.dox120
-rw-r--r--xbmc/games/controllers/guicontrols/GUIGameController.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIGameControllerList.dox77
-rw-r--r--xbmc/games/controllers/guicontrols/GUIGameControllerList.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIScalarFeatureButton.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUISelectKeyButton.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIThrottleButton.h3
-rw-r--r--xbmc/games/controllers/guicontrols/GUIWheelButton.h3
-rw-r--r--xbmc/games/controllers/input/InputSink.h3
-rw-r--r--xbmc/games/controllers/input/PhysicalFeature.h3
-rw-r--r--xbmc/games/controllers/input/PhysicalTopology.h2
-rw-r--r--xbmc/games/controllers/listproviders/GUIGameControllerProvider.h29
-rw-r--r--xbmc/games/controllers/types/ControllerGrid.h8
-rw-r--r--xbmc/games/controllers/types/ControllerHub.h2
-rw-r--r--xbmc/games/controllers/types/ControllerNode.cpp7
-rw-r--r--xbmc/games/controllers/types/ControllerNode.h2
-rw-r--r--xbmc/games/controllers/types/ControllerTree.h4
-rw-r--r--xbmc/games/controllers/windows/GUIConfigurationWizard.h3
-rw-r--r--xbmc/games/controllers/windows/GUIControllerList.h3
-rw-r--r--xbmc/games/controllers/windows/GUIControllerWindow.h3
-rw-r--r--xbmc/games/controllers/windows/GUIFeatureList.h3
-rw-r--r--xbmc/games/controllers/windows/IConfigurationWindow.h49
-rw-r--r--xbmc/games/dialogs/GUIDialogSelectGameClient.h3
-rw-r--r--xbmc/games/dialogs/GUIDialogSelectSavestate.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameAdvancedSettings.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameOSD.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameOSDHelp.cpp9
-rw-r--r--xbmc/games/dialogs/osd/DialogGameSaves.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameStretchMode.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameVideoFilter.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameVideoRotation.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameVideoSelect.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogGameVolume.h3
-rw-r--r--xbmc/games/dialogs/osd/DialogInGameSaves.h3
-rw-r--r--xbmc/games/ports/guicontrols/GUIActivePortList.h3
-rw-r--r--xbmc/games/ports/guicontrols/IActivePortList.h5
-rw-r--r--xbmc/games/ports/input/PhysicalPort.h3
-rw-r--r--xbmc/games/ports/input/PortInput.cpp4
-rw-r--r--xbmc/games/ports/input/PortInput.h3
-rw-r--r--xbmc/games/ports/input/PortManager.h3
-rw-r--r--xbmc/games/ports/types/PortNode.h2
-rw-r--r--xbmc/games/ports/windows/GUIPortDefines.h6
-rw-r--r--xbmc/games/ports/windows/GUIPortList.h3
-rw-r--r--xbmc/games/ports/windows/GUIPortWindow.h3
-rw-r--r--xbmc/games/ports/windows/IPortList.h18
-rw-r--r--xbmc/games/tags/GameInfoTag.h3
-rw-r--r--xbmc/games/windows/GUIViewStateWindowGames.h3
-rw-r--r--xbmc/games/windows/GUIWindowGames.h3
-rw-r--r--xbmc/guilib/GUIBaseContainer.cpp2
-rw-r--r--xbmc/guilib/GUIColorManager.cpp5
-rw-r--r--xbmc/guilib/GUIComponent.cpp16
-rw-r--r--xbmc/guilib/GUIControlFactory.cpp697
-rw-r--r--xbmc/guilib/GUIControlFactory.h104
-rw-r--r--xbmc/guilib/GUIDialog.cpp2
-rw-r--r--xbmc/guilib/GUIEditControl.cpp88
-rw-r--r--xbmc/guilib/GUIEditControl.h21
-rw-r--r--xbmc/guilib/GUIRangesControl.cpp6
-rw-r--r--xbmc/guilib/GUIStaticItem.cpp14
-rw-r--r--xbmc/guilib/GUIVisualisationControl.cpp4
-rw-r--r--xbmc/guilib/GUIWindowManager.cpp10
-rw-r--r--xbmc/guilib/WindowIDs.dox30
-rw-r--r--xbmc/guilib/WindowIDs.h3
-rw-r--r--xbmc/guilib/_Controls.dox2
-rw-r--r--xbmc/guilib/guiinfo/GUIInfoLabel.cpp22
-rw-r--r--xbmc/guilib/guiinfo/GUIInfoLabel.h10
-rw-r--r--xbmc/guilib/guiinfo/GUIInfoLabels.h7
-rw-r--r--xbmc/guilib/guiinfo/LibraryGUIInfo.cpp2
-rw-r--r--xbmc/guilib/guiinfo/PicturesGUIInfo.cpp3
-rw-r--r--xbmc/guilib/guiinfo/PlayerGUIInfo.cpp11
-rw-r--r--xbmc/guilib/guiinfo/SystemGUIInfo.cpp27
-rw-r--r--xbmc/guilib/guiinfo/VideoGUIInfo.cpp6
-rw-r--r--xbmc/imagefiles/SpecialImageLoaderFactory.cpp2
-rw-r--r--xbmc/imagefiles/SpecialImageLoaderFactory.h2
-rw-r--r--xbmc/input/IRTranslator.cpp5
-rw-r--r--xbmc/input/InputManager.cpp20
-rw-r--r--xbmc/input/JoystickMapper.cpp3
-rw-r--r--xbmc/input/WindowTranslator.cpp4
-rw-r--r--xbmc/input/XBMC_keysym.h48
-rw-r--r--xbmc/input/actions/Action.cpp5
-rw-r--r--xbmc/input/actions/Action.h3
-rw-r--r--xbmc/input/actions/ActionIDs.h7
-rw-r--r--xbmc/input/joysticks/generic/ButtonMapping.cpp3
-rw-r--r--xbmc/input/joysticks/keymaps/KeymapHandler.cpp3
-rw-r--r--xbmc/interfaces/AnnouncementManager.cpp3
-rw-r--r--xbmc/interfaces/builtins/PlayerBuiltins.cpp101
-rw-r--r--xbmc/interfaces/generic/ScriptInvocationManager.cpp4
-rw-r--r--xbmc/interfaces/json-rpc/AudioLibrary.cpp8
-rw-r--r--xbmc/interfaces/json-rpc/FileItemHandler.cpp3
-rw-r--r--xbmc/interfaces/json-rpc/FileOperations.cpp6
-rw-r--r--xbmc/interfaces/json-rpc/JSONServiceDescription.cpp22
-rw-r--r--xbmc/interfaces/json-rpc/PVROperations.cpp16
-rw-r--r--xbmc/interfaces/json-rpc/PVROperations.h6
-rw-r--r--xbmc/interfaces/json-rpc/PlayerOperations.cpp24
-rw-r--r--xbmc/interfaces/json-rpc/TextureOperations.cpp2
-rw-r--r--xbmc/interfaces/json-rpc/VideoLibrary.cpp29
-rw-r--r--xbmc/interfaces/json-rpc/schema/version.txt2
-rw-r--r--xbmc/interfaces/legacy/Dialog.cpp4
-rw-r--r--xbmc/interfaces/legacy/InfoTagMusic.cpp17
-rw-r--r--xbmc/interfaces/legacy/InfoTagMusic.h36
-rw-r--r--xbmc/interfaces/legacy/InfoTagVideo.cpp10
-rw-r--r--xbmc/interfaces/legacy/InfoTagVideo.h18
-rw-r--r--xbmc/interfaces/legacy/ListItem.cpp5
-rw-r--r--xbmc/interfaces/legacy/ListItem.h5
-rw-r--r--xbmc/interfaces/legacy/ModuleXbmc.cpp9
-rw-r--r--xbmc/interfaces/legacy/ModuleXbmc.h6
-rw-r--r--xbmc/interfaces/legacy/Window.cpp16
-rw-r--r--xbmc/interfaces/python/ContextItemAddonInvoker.cpp10
-rw-r--r--xbmc/interfaces/python/typemaps/python.buffer.intm4
-rw-r--r--xbmc/listproviders/DirectoryProvider.cpp154
-rw-r--r--xbmc/media/MediaType.cpp1
-rw-r--r--xbmc/media/MediaType.h8
-rw-r--r--xbmc/messaging/ApplicationMessenger.cpp2
-rw-r--r--xbmc/music/ContextMenus.cpp70
-rw-r--r--xbmc/music/ContextMenus.h7
-rw-r--r--xbmc/music/MusicDatabase.cpp6
-rw-r--r--xbmc/music/MusicUtils.cpp38
-rw-r--r--xbmc/music/MusicUtils.h2
-rw-r--r--xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp10
-rw-r--r--xbmc/music/dialogs/GUIDialogMusicInfo.cpp2
-rw-r--r--xbmc/music/dialogs/GUIDialogSongInfo.cpp2
-rw-r--r--xbmc/music/windows/GUIWindowMusicBase.cpp65
-rw-r--r--xbmc/music/windows/GUIWindowMusicPlaylist.cpp189
-rw-r--r--xbmc/music/windows/GUIWindowMusicPlaylistEditor.cpp18
-rw-r--r--xbmc/music/windows/GUIWindowMusicPlaylistEditor.h3
-rw-r--r--xbmc/music/windows/MusicFileItemListModifier.cpp8
-rw-r--r--xbmc/network/DNSNameCache.cpp15
-rw-r--r--xbmc/network/EventClient.cpp2
-rw-r--r--xbmc/network/GUIDialogNetworkSetup.cpp3
-rw-r--r--xbmc/network/TCPServer.cpp3
-rw-r--r--xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp5
-rw-r--r--xbmc/network/test/CMakeLists.txt8
-rw-r--r--xbmc/network/test/TestNetwork.cpp40
-rw-r--r--xbmc/network/upnp/UPnPInternal.cpp1756
-rw-r--r--xbmc/network/upnp/UPnPPlayer.cpp111
-rw-r--r--xbmc/network/upnp/UPnPPlayer.h3
-rw-r--r--xbmc/network/upnp/UPnPServer.cpp14
-rw-r--r--xbmc/network/websocket/WebSocket.cpp2
-rw-r--r--xbmc/network/websocket/WebSocket.h2
-rw-r--r--xbmc/network/websocket/WebSocketV8.h5
-rw-r--r--xbmc/peripherals/addons/AddonButtonMapping.cpp6
-rw-r--r--xbmc/peripherals/addons/AddonInputHandling.cpp17
-rw-r--r--xbmc/peripherals/devices/PeripheralJoystick.cpp9
-rw-r--r--xbmc/pictures/GUIWindowSlideShow.cpp13
-rw-r--r--xbmc/pictures/Picture.cpp189
-rw-r--r--xbmc/pictures/Picture.h22
-rw-r--r--xbmc/pictures/SlideShowPicture.cpp4
-rw-r--r--xbmc/platform/android/activity/JNIXBMCFile.cpp4
-rw-r--r--xbmc/platform/android/activity/XBMCApp.cpp69
-rw-r--r--xbmc/platform/darwin/ios-common/network/NetworkIOS.mm2
-rw-r--r--xbmc/platform/darwin/ios/LaunchScreen.storyboard4
-rw-r--r--xbmc/platform/darwin/osx/CMakeLists.txt6
-rw-r--r--xbmc/platform/darwin/osx/CPUInfoOsx.cpp17
-rw-r--r--xbmc/platform/darwin/osx/CocoaInterface.h5
-rw-r--r--xbmc/platform/darwin/osx/CocoaInterface.mm36
-rw-r--r--xbmc/platform/darwin/osx/GPUInfoMacOS.cpp6
-rw-r--r--xbmc/platform/darwin/osx/SDL/CMakeLists.txt9
-rw-r--r--xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.h19
-rw-r--r--xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.mm185
-rw-r--r--xbmc/platform/darwin/osx/SDL/SDLMain.mm580
-rw-r--r--xbmc/platform/darwin/osx/smc.c148
-rw-r--r--xbmc/platform/darwin/osx/smc.h101
-rw-r--r--xbmc/platform/darwin/osx/storage/OSXStorageProvider.cpp4
-rw-r--r--xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/.gitignore2
-rw-r--r--xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json32
l---------xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/applaunch_screen.png1
l---------xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/splash.jpg1
-rw-r--r--xbmc/platform/freebsd/PlatformFreebsd.cpp16
-rw-r--r--xbmc/platform/linux/AppParamParserLinux.cpp29
-rw-r--r--xbmc/platform/linux/CPUInfoLinux.cpp8
-rw-r--r--xbmc/platform/linux/PlatformLinux.cpp6
-rw-r--r--xbmc/platform/linux/input/LibInputHandler.cpp9
-rw-r--r--xbmc/platform/linux/input/LibInputKeyboard.cpp530
-rw-r--r--xbmc/platform/linux/input/LibInputKeyboard.h52
-rw-r--r--xbmc/platform/linux/input/LibInputSettings.cpp2
-rw-r--r--xbmc/platform/linux/network/NetworkLinux.cpp151
-rw-r--r--xbmc/platform/posix/PosixTimezone.cpp96
-rw-r--r--xbmc/platform/posix/PosixTimezone.h14
-rw-r--r--xbmc/platform/posix/filesystem/SMBFile.cpp6
-rw-r--r--xbmc/platform/posix/filesystem/SMBFile.h2
-rw-r--r--xbmc/platform/posix/main.cpp7
-rw-r--r--xbmc/platform/win32/PlatformDefs.h1
-rw-r--r--xbmc/platform/win32/WIN32Util.cpp35
-rw-r--r--xbmc/platform/win32/WinMain.cpp96
-rw-r--r--xbmc/platform/win32/filesystem/Win32File.cpp12
-rw-r--r--xbmc/playlists/PlayList.cpp2
-rw-r--r--xbmc/playlists/PlayListFactory.cpp4
-rw-r--r--xbmc/powermanagement/PowerManager.cpp2
-rw-r--r--xbmc/profiles/ProfileManager.cpp37
-rw-r--r--xbmc/profiles/dialogs/GUIDialogLockSettings.cpp10
-rw-r--r--xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp8
-rw-r--r--xbmc/pvr/CMakeLists.txt2
-rw-r--r--xbmc/pvr/PVRChannelGroupImageFileLoader.cpp47
-rw-r--r--xbmc/pvr/PVRChannelGroupImageFileLoader.h31
-rw-r--r--xbmc/pvr/PVRContextMenus.cpp74
-rw-r--r--xbmc/pvr/PVRDatabase.cpp36
-rw-r--r--xbmc/pvr/PVRDatabase.h9
-rw-r--r--xbmc/pvr/PVREventLogJob.cpp2
-rw-r--r--xbmc/pvr/PVRItem.cpp4
-rw-r--r--xbmc/pvr/PVRManager.cpp26
-rw-r--r--xbmc/pvr/PVRManager.h6
-rw-r--r--xbmc/pvr/PVRPlaybackState.cpp119
-rw-r--r--xbmc/pvr/PVRPlaybackState.h6
-rw-r--r--xbmc/pvr/PVRThumbLoader.cpp37
-rw-r--r--xbmc/pvr/addons/PVRClient.cpp92
-rw-r--r--xbmc/pvr/addons/PVRClient.h85
-rw-r--r--xbmc/pvr/addons/PVRClientCapabilities.cpp7
-rw-r--r--xbmc/pvr/addons/PVRClientMenuHooks.cpp4
-rw-r--r--xbmc/pvr/addons/PVRClients.cpp146
-rw-r--r--xbmc/pvr/addons/PVRClients.h14
-rw-r--r--xbmc/pvr/channels/PVRChannel.cpp40
-rw-r--r--xbmc/pvr/channels/PVRChannel.h16
-rw-r--r--xbmc/pvr/channels/PVRChannelGroup.cpp54
-rw-r--r--xbmc/pvr/channels/PVRChannelGroup.h21
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupAllChannels.cpp6
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupAllChannels.h7
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupFromClient.cpp4
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupFromClient.h4
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupFromUser.cpp2
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupFromUser.h2
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupMember.cpp2
-rw-r--r--xbmc/pvr/channels/PVRChannelGroups.cpp15
-rw-r--r--xbmc/pvr/channels/PVRChannelGroups.h6
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupsContainer.cpp6
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupsContainer.h3
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp2
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h4
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp16
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp2
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp3
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp23
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp2
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp10
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp10
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp6
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp17
-rw-r--r--xbmc/pvr/epg/Epg.cpp7
-rw-r--r--xbmc/pvr/epg/Epg.h2
-rw-r--r--xbmc/pvr/epg/EpgContainer.cpp27
-rw-r--r--xbmc/pvr/epg/EpgContainer.h5
-rw-r--r--xbmc/pvr/epg/EpgDatabase.cpp48
-rw-r--r--xbmc/pvr/epg/EpgDatabase.h50
-rw-r--r--xbmc/pvr/epg/EpgInfoTag.cpp8
-rw-r--r--xbmc/pvr/epg/EpgSearchFilter.cpp45
-rw-r--r--xbmc/pvr/epg/EpgSearchFilter.h20
-rw-r--r--xbmc/pvr/epg/EpgTagsCache.cpp2
-rw-r--r--xbmc/pvr/filesystem/PVRGUIDirectory.cpp194
-rw-r--r--xbmc/pvr/guilib/CMakeLists.txt3
-rw-r--r--xbmc/pvr/guilib/GUIEPGGridContainer.cpp2
-rw-r--r--xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp24
-rw-r--r--xbmc/pvr/guilib/GUIEPGGridContainerModel.h6
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionListener.cpp4
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsChannels.cpp69
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsChannels.h2
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsEPG.cpp4
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsParentalControl.cpp2
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsParentalControl.h2
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsPlayback.cpp22
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp2
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsRecordings.cpp2
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsTimers.cpp22
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsTimers.h5
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsUtils.cpp67
-rw-r--r--xbmc/pvr/guilib/PVRGUIActionsUtils.h17
-rw-r--r--xbmc/pvr/guilib/PVRGUIChannelIconUpdater.cpp5
-rw-r--r--xbmc/pvr/guilib/PVRGUIChannelNavigator.cpp7
-rw-r--r--xbmc/pvr/guilib/PVRGUIRecordingsPlayActionProcessor.h48
-rw-r--r--xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp103
-rw-r--r--xbmc/pvr/guilib/guiinfo/PVRGUITimerInfo.cpp4
-rw-r--r--xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp32
-rw-r--r--xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.h22
-rw-r--r--xbmc/pvr/providers/PVRProviders.cpp9
-rw-r--r--xbmc/pvr/recordings/PVRRecording.cpp13
-rw-r--r--xbmc/pvr/recordings/PVRRecordings.cpp4
-rw-r--r--xbmc/pvr/recordings/PVRRecordings.h2
-rw-r--r--xbmc/pvr/recordings/PVRRecordingsPath.cpp22
-rw-r--r--xbmc/pvr/timers/PVRTimerInfoTag.cpp14
-rw-r--r--xbmc/pvr/timers/PVRTimerInfoTag.h4
-rw-r--r--xbmc/pvr/timers/PVRTimerRuleMatcher.cpp24
-rw-r--r--xbmc/pvr/timers/PVRTimerRuleMatcher.h16
-rw-r--r--xbmc/pvr/timers/PVRTimerType.cpp3
-rw-r--r--xbmc/pvr/timers/PVRTimerType.h3
-rw-r--r--xbmc/pvr/timers/PVRTimers.cpp55
-rw-r--r--xbmc/pvr/timers/PVRTimers.h16
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRBase.cpp23
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRChannels.cpp4
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRGuide.cpp21
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRGuide.h2
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRRecordings.cpp73
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRSearch.cpp2
-rw-r--r--xbmc/rendering/RenderSystem.cpp11
-rw-r--r--xbmc/rendering/dx/DeviceResources.cpp47
-rw-r--r--xbmc/rendering/dx/DeviceResources.h8
-rw-r--r--xbmc/rendering/gl/ScreenshotSurfaceGL.cpp3
-rw-r--r--xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp3
-rw-r--r--xbmc/settings/AdvancedSettings.cpp58
-rw-r--r--xbmc/settings/AdvancedSettings.h12
-rw-r--r--xbmc/settings/CMakeLists.txt2
-rw-r--r--xbmc/settings/ServicesSettings.cpp115
-rw-r--r--xbmc/settings/ServicesSettings.h39
-rw-r--r--xbmc/settings/Settings.cpp16
-rw-r--r--xbmc/settings/Settings.h12
-rw-r--r--xbmc/settings/dialogs/GUIDialogContentSettings.cpp25
-rw-r--r--xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp20
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsBase.cpp37
-rw-r--r--xbmc/settings/windows/GUIControlSettings.cpp7
-rw-r--r--xbmc/test/TestUtil.cpp17
-rw-r--r--xbmc/threads/Event.cpp3
-rw-r--r--xbmc/threads/test/TestEvent.cpp6
-rw-r--r--xbmc/utils/BooleanLogic.cpp4
-rw-r--r--xbmc/utils/DatabaseUtils.h1
-rw-r--r--xbmc/utils/EGLFence.cpp70
-rw-r--r--xbmc/utils/EGLFence.h19
-rw-r--r--xbmc/utils/EGLImage.cpp1
-rw-r--r--xbmc/utils/ExecString.cpp19
-rw-r--r--xbmc/utils/ExecString.h1
-rw-r--r--xbmc/utils/FileOperationJob.cpp8
-rw-r--r--xbmc/utils/HttpHeader.cpp4
-rw-r--r--xbmc/utils/HttpRangeUtils.cpp2
-rw-r--r--xbmc/utils/JobManager.cpp4
-rw-r--r--xbmc/utils/LangCodeExpander.cpp3
-rw-r--r--xbmc/utils/LangCodeExpander.h2
-rw-r--r--xbmc/utils/StringUtils.cpp33
-rw-r--r--xbmc/utils/StringUtils.h10
-rw-r--r--xbmc/utils/SystemInfo.cpp6
-rw-r--r--xbmc/utils/URIUtils.cpp13
-rw-r--r--xbmc/utils/URIUtils.h1
-rw-r--r--xbmc/utils/XBMCTinyXML2.cpp4
-rw-r--r--xbmc/utils/guilib/CMakeLists.txt5
-rw-r--r--xbmc/utils/guilib/GUIContentUtils.cpp58
-rw-r--r--xbmc/utils/guilib/GUIContentUtils.h33
-rw-r--r--xbmc/utils/test/TestCPUInfo.cpp1
-rw-r--r--xbmc/utils/test/TestGPUInfo.cpp2
-rw-r--r--xbmc/utils/test/TestURIUtils.cpp9
-rw-r--r--xbmc/utils/test/TestVariant.cpp2
-rw-r--r--xbmc/video/ContextMenus.cpp261
-rw-r--r--xbmc/video/ContextMenus.h14
-rw-r--r--xbmc/video/GUIViewStateVideo.cpp16
-rw-r--r--xbmc/video/Teletext.cpp33
-rw-r--r--xbmc/video/Teletext.h7
-rw-r--r--xbmc/video/VideoChapterImageFileLoader.cpp2
-rw-r--r--xbmc/video/VideoDatabase.cpp1107
-rw-r--r--xbmc/video/VideoDatabase.h86
-rw-r--r--xbmc/video/VideoDbUrl.cpp6
-rw-r--r--xbmc/video/VideoGeneratedImageFileLoader.cpp9
-rw-r--r--xbmc/video/VideoInfoDownloader.cpp14
-rw-r--r--xbmc/video/VideoInfoDownloader.h6
-rw-r--r--xbmc/video/VideoInfoScanner.cpp246
-rw-r--r--xbmc/video/VideoInfoScanner.h12
-rw-r--r--xbmc/video/VideoInfoTag.cpp46
-rw-r--r--xbmc/video/VideoInfoTag.h7
-rw-r--r--xbmc/video/VideoThumbLoader.cpp64
-rw-r--r--xbmc/video/VideoUtils.cpp74
-rw-r--r--xbmc/video/VideoUtils.h2
-rw-r--r--xbmc/video/dialogs/CMakeLists.txt2
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSettings.cpp8
-rw-r--r--xbmc/video/dialogs/GUIDialogCMSSettings.cpp57
-rw-r--r--xbmc/video/dialogs/GUIDialogTeletext.cpp4
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.cpp122
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.h5
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoSettings.cpp87
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoVersion.cpp1030
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoVersion.h81
-rw-r--r--xbmc/video/guilib/CMakeLists.txt10
-rw-r--r--xbmc/video/guilib/VideoAction.h29
-rw-r--r--xbmc/video/guilib/VideoActionProcessorHelper.cpp161
-rw-r--r--xbmc/video/guilib/VideoActionProcessorHelper.h44
-rw-r--r--xbmc/video/guilib/VideoPlayActionProcessor.cpp95
-rw-r--r--xbmc/video/guilib/VideoPlayActionProcessor.h55
-rw-r--r--xbmc/video/guilib/VideoSelectAction.h29
-rw-r--r--xbmc/video/guilib/VideoSelectActionProcessor.cpp88
-rw-r--r--xbmc/video/guilib/VideoSelectActionProcessor.h32
-rw-r--r--xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp12
-rw-r--r--xbmc/video/jobs/VideoLibraryRefreshingJob.cpp5
-rw-r--r--xbmc/video/tags/VideoTagLoaderFFmpeg.cpp10
-rw-r--r--xbmc/video/tags/VideoTagLoaderPlugin.cpp8
-rw-r--r--xbmc/video/test/TestVideoInfoScanner.cpp2
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.cpp193
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.h9
-rw-r--r--xbmc/video/windows/GUIWindowVideoNav.cpp58
-rw-r--r--xbmc/video/windows/GUIWindowVideoPlaylist.cpp199
-rw-r--r--xbmc/video/windows/VideoFileItemListModifier.cpp6
-rw-r--r--xbmc/windowing/GraphicContext.cpp6
-rw-r--r--xbmc/windowing/WinSystem.cpp9
-rw-r--r--xbmc/windowing/WindowSystemFactory.cpp2
-rw-r--r--xbmc/windowing/X11/WinSystemX11.cpp3
-rw-r--r--xbmc/windowing/X11/WinSystemX11GLContext.cpp14
-rw-r--r--xbmc/windowing/XBMC_events.h3
-rw-r--r--xbmc/windowing/android/WinSystemAndroid.cpp6
-rw-r--r--xbmc/windowing/android/WinSystemAndroidGLESContext.cpp10
-rw-r--r--xbmc/windowing/gbm/WinSystemGbm.cpp8
-rw-r--r--xbmc/windowing/gbm/WinSystemGbm.h2
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp11
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmEGLContext.h3
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmGLContext.cpp26
-rw-r--r--xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp27
-rw-r--r--xbmc/windowing/gbm/drm/DRMAtomic.cpp19
-rw-r--r--xbmc/windowing/gbm/drm/DRMAtomic.h2
-rw-r--r--xbmc/windowing/gbm/drm/DRMConnector.cpp1
-rw-r--r--xbmc/windowing/gbm/drm/DRMLegacy.cpp2
-rw-r--r--xbmc/windowing/gbm/drm/DRMLegacy.h2
-rw-r--r--xbmc/windowing/gbm/drm/DRMPlane.cpp2
-rw-r--r--xbmc/windowing/gbm/drm/DRMUtils.h13
-rw-r--r--xbmc/windowing/gbm/drm/OffScreenModeSetting.h2
-rw-r--r--xbmc/windowing/ios/WinSystemIOS.mm3
-rw-r--r--xbmc/windowing/osx/CMakeLists.txt19
-rw-r--r--xbmc/windowing/osx/OpenGL/CMakeLists.txt15
-rw-r--r--xbmc/windowing/osx/OpenGL/WinSystemOSXGL.h6
-rw-r--r--xbmc/windowing/osx/SDL/CMakeLists.txt10
-rw-r--r--xbmc/windowing/osx/SDL/WinEventsSDL.cpp241
-rw-r--r--xbmc/windowing/osx/SDL/WinEventsSDL.h24
-rw-r--r--xbmc/windowing/osx/SDL/WinSystemOSXSDL.h111
-rw-r--r--xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm1848
-rw-r--r--xbmc/windowing/osx/WinEventsOSXImpl.mm1
-rw-r--r--xbmc/windowing/osx/WinSystemOSX.mm7
-rw-r--r--xbmc/windowing/tvos/WinSystemTVOS.mm2
-rw-r--r--xbmc/windowing/wayland/InputProcessorKeyboard.cpp61
-rw-r--r--xbmc/windowing/wayland/InputProcessorKeyboard.h7
-rw-r--r--xbmc/windowing/wayland/SeatInputProcessing.cpp7
-rw-r--r--xbmc/windowing/wayland/SeatInputProcessing.h2
-rw-r--r--xbmc/windowing/wayland/ShellSurfaceWebOSShell.cpp1
-rw-r--r--xbmc/windowing/wayland/WinEventsWayland.cpp2
-rw-r--r--xbmc/windowing/wayland/WinSystemWayland.cpp15
-rw-r--r--xbmc/windowing/wayland/WinSystemWayland.h12
-rw-r--r--xbmc/windowing/wayland/WinSystemWaylandWebOS.cpp41
-rw-r--r--xbmc/windowing/wayland/WinSystemWaylandWebOS.h5
-rw-r--r--xbmc/windowing/wayland/WindowDecorator.cpp3
-rw-r--r--xbmc/windowing/wayland/XkbcommonKeymap.cpp215
-rw-r--r--xbmc/windowing/wayland/XkbcommonKeymap.h62
-rw-r--r--xbmc/windowing/win10/WinEventsWin10.cpp6
-rw-r--r--xbmc/windowing/win10/WinSystemWin10.cpp21
-rw-r--r--xbmc/windowing/win10/WinSystemWin10DX.cpp2
-rw-r--r--xbmc/windowing/windows/VideoSyncD3D.cpp82
-rw-r--r--xbmc/windowing/windows/VideoSyncD3D.h7
-rw-r--r--xbmc/windowing/windows/WinEventsWin32.cpp19
-rw-r--r--xbmc/windows/GUIMediaWindow.cpp28
-rw-r--r--xbmc/windows/GUIMediaWindow.h4
-rw-r--r--xbmc/windows/GUIWindowSplash.cpp8
1070 files changed, 20263 insertions, 13927 deletions
diff --git a/.clang-tidy b/.clang-tidy
index b5865a5de9..38c1b93bb6 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,5 +1,8 @@
Checks: "\
+ modernize-make-shared,\
+ modernize-make-unique,\
modernize-use-default-member-init,\
+ modernize-use-emplace,\
performance-faster-string-find,\
performance-for-range-copy,\
performance-implicit-conversion-in-loop,\
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a7acd26853..160d69f548 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,7 +24,11 @@ endif()
# Variable to indicate if the project is targeting a Multi Config Generator (VS/Xcode primarily)
get_property(_multiconfig_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_multiconfig_generator)
+ # Target to encompass an optional way to build all internal dependencies
+ # Must set EXCLUDE_FROM_ALL property so the ALL target doesnt include this, and therefore
+ # build all internal dependencies
add_custom_target(build_internal_depends)
+ set_target_properties(build_internal_depends PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
# Set CORE_BUILD_DIR
@@ -73,18 +77,28 @@ option(ENABLE_TESTING "Enable testing support?" ON)
# These are required enabled for all CI platforms, and recommended for all builds
option(ENABLE_INTERNAL_CROSSGUID "Enable internal crossguid?" ON)
-option(ENABLE_INTERNAL_RapidJSON "Enable internal rapidjson?" ON)
# use ffmpeg from depends or system
option(ENABLE_INTERNAL_FFMPEG "Enable internal ffmpeg?" OFF)
# These are built for all platforms not using system libs or disabled by user
+dependent_option(ENABLE_INTERNAL_CEC "Enable internal libcec?")
dependent_option(ENABLE_INTERNAL_FLATBUFFERS "Enable internal flatbuffers?")
dependent_option(ENABLE_INTERNAL_FMT "Enable internal fmt?")
dependent_option(ENABLE_INTERNAL_NFS "Enable internal libnfs?")
dependent_option(ENABLE_INTERNAL_PCRE "Enable internal pcre?")
-dependent_option(ENABLE_INTERNAL_SPDLOG "Enable internal spdlog?")
+dependent_option(ENABLE_INTERNAL_RapidJSON "Enable internal rapidjson?")
+
+# If ENABLE_INTERNAL_FMT is ON, we force ENABLE_INTERNAL_SPDLOG ON as it has a hard
+# dependency on fmt
+if(ENABLE_INTERNAL_FMT)
+ option(ENABLE_INTERNAL_SPDLOG "Enable internal spdlog Forced" ON)
+else()
+ dependent_option(ENABLE_INTERNAL_SPDLOG "Enable internal spdlog?")
+endif()
+
dependent_option(ENABLE_INTERNAL_TAGLIB "Enable internal taglib?")
+
if(KODI_DEPENDSBUILD OR WIN32 OR WINDOWS_STORE)
dependent_option(ENABLE_INTERNAL_TINYXML2 "Enable internal TinyXML2?")
endif()
@@ -122,6 +136,7 @@ endif()
core_find_git_rev(APP_SCMID FULL)
set(AUDIO_BACKENDS_LIST "" CACHE STRING "Available audio backends")
+set(GL_INTERFACES_LIST "" CACHE STRING "Available GL interfaces")
# Dynamically loaded libraries built with the project
add_custom_target(${APP_NAME_LC}-libraries)
@@ -195,7 +210,7 @@ set(required_deps ASS>=0.15.0
Lzo2
OpenSSL>=1.1.0
PCRE
- RapidJSON
+ RapidJSON>=1.0.2
Spdlog
Sqlite3
TagLib
@@ -210,7 +225,7 @@ set(optional_deps Alsa
Bluetooth
Bluray
CAP
- CEC
+ CEC>=4.0.0
Dav1d
DBus
Iso9660pp
@@ -230,6 +245,11 @@ set(optional_deps Alsa
XSLT
${PLATFORM_OPTIONAL_DEPS})
+# Remove excluded platform specific optional_deps
+foreach(excludedep ${PLATFORM_OPTIONAL_DEPS_EXCLUDE})
+ list(FILTER optional_deps EXCLUDE REGEX ${excludedep})
+endforeach()
+
# Check optional deps first, since their availability can influence required ones, e.g. ffmpeg
core_optional_dep(${optional_deps})
core_require_dep(${required_deps})
@@ -280,6 +300,7 @@ add_custom_command(OUTPUT ${CORE_BUILD_DIR}/xbmc/CompileInfo.cpp
-DCORE_SYSTEM_NAME=${CORE_SYSTEM_NAME}
-DCORE_PLATFORM_NAME_LC="${CORE_PLATFORM_NAME_LC}"
-DAUDIO_BACKENDS="${AUDIO_BACKENDS_LIST}"
+ -DGL_INTERFACES="${GL_INTERFACES_LIST}"
-DCORE_BUILD_DIR=${CORE_BUILD_DIR}
-DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
-DARCH_DEFINES="${ARCH_DEFINES}"
diff --git a/addons/game.controller.default/addon.xml b/addons/game.controller.default/addon.xml
index 7f63157ea4..202881b477 100644
--- a/addons/game.controller.default/addon.xml
+++ b/addons/game.controller.default/addon.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="game.controller.default"
name="Default Controller"
- version="1.0.40"
+ version="1.0.41"
provider-name="Team Kodi">
<extension point="kodi.game.controller" library="resources/layout.xml"/>
<extension point="xbmc.addon.metadata">
diff --git a/addons/game.controller.default/resources/language/resource.language.my_mm/strings.po b/addons/game.controller.default/resources/language/resource.language.my_mm/strings.po
index 209df3741b..d05b92c81f 100644
--- a/addons/game.controller.default/resources/language/resource.language.my_mm/strings.po
+++ b/addons/game.controller.default/resources/language/resource.language.my_mm/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2021-05-20 16:49+0000\n"
+"PO-Revision-Date: 2023-10-23 01:11+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Burmese <https://kodi.weblate.cloud/projects/kodi-core/game-controller-default/my_mm/>\n"
"Language: my_mm\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.6.2\n"
+"X-Generator: Weblate 5.0.2\n"
msgctxt "Addon Summary"
msgid "Default Controller"
@@ -31,7 +31,7 @@ msgstr ""
msgctxt "#30000"
msgid "Kodi"
-msgstr ""
+msgstr "ကိုဒီ"
msgctxt "#30001"
msgid "A"
diff --git a/addons/game.controller.keyboard/addon.xml b/addons/game.controller.keyboard/addon.xml
index ce6640f90d..eed3e7cd1d 100644
--- a/addons/game.controller.keyboard/addon.xml
+++ b/addons/game.controller.keyboard/addon.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="game.controller.keyboard"
name="IBM Model M Keyboard"
- version="1.1.30"
+ version="1.1.33"
provider-name="Team Kodi">
<extension point="kodi.game.controller" library="resources/layout.xml"/>
<extension point="xbmc.addon.metadata">
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.af_za/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.af_za/strings.po
index 7f2c063a64..5937a4dd94 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.af_za/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.af_za/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.am_et/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.am_et/strings.po
index 3ddaf9947d..b706bc5107 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.am_et/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.am_et/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ar_sa/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ar_sa/strings.po
index 2831dfbde1..e2aaf23704 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ar_sa/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ar_sa/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ast_es/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ast_es/strings.po
index a806b8d2b4..44f29fa6b4 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ast_es/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ast_es/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-04-14 22:45+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.az_az/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.az_az/strings.po
index 201ff4b66e..769af455b5 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.az_az/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.az_az/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.be_by/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.be_by/strings.po
index 1aaa54b63d..2005f8bae0 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.be_by/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.be_by/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.bg_bg/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.bg_bg/strings.po
index fb8d3f1669..1fc61c8557 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.bg_bg/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.bg_bg/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.bs_ba/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.bs_ba/strings.po
index bae6398288..bf1db31f95 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.bs_ba/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.bs_ba/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ca_es/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ca_es/strings.po
index 36b026e9a6..4e32574309 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ca_es/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ca_es/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:14+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.cs_cz/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.cs_cz/strings.po
index e364b2d01f..9901e6770d 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.cs_cz/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.cs_cz/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:14+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.cy_gb/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.cy_gb/strings.po
index 364cf1be52..0d748ae9e7 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.cy_gb/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.cy_gb/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.da_dk/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.da_dk/strings.po
index 1365a402fd..66c6129ce6 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.da_dk/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.da_dk/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2021-08-18 16:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr "Num Pad ="
msgctxt "#30140"
msgid "Clear"
msgstr "Ryd"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.de_de/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.de_de/strings.po
index 76a169b19e..481978e361 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.de_de/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.de_de/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-02-01 20:55+0000\n"
"Last-Translator: Demian <Demian@gmx.co.uk>\n"
@@ -589,3 +589,7 @@ msgstr "Ziffernblock ="
msgctxt "#30140"
msgid "Clear"
msgstr "Löschen"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.el_gr/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.el_gr/strings.po
index faee582405..9254b722c8 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.el_gr/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.el_gr/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.en_au/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.en_au/strings.po
index 635656f8d6..6f0c463af1 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.en_au/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.en_au/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.en_gb/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.en_gb/strings.po
index 66ffb5db8d..1346477145 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.en_gb/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.en_gb/strings.po
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.en_nz/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.en_nz/strings.po
index 48bd6bab4d..f087ad15f5 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.en_nz/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.en_nz/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.en_us/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.en_us/strings.po
index 6b8e723cd4..f2506cf98f 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.en_us/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.en_us/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.eo/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.eo/strings.po
index d7f15eca33..277c52bda0 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.eo/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.eo/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.es_ar/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.es_ar/strings.po
index ca92d90c4f..b8f7085312 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.es_ar/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.es_ar/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.es_es/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.es_es/strings.po
index 16f766bf2d..d27decdc7c 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.es_es/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.es_es/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-01-16 22:15+0000\n"
"Last-Translator: José Antonio Alvarado <jalvarado0.eses@gmail.com>\n"
@@ -589,3 +589,7 @@ msgstr "Bloq Num ="
msgctxt "#30140"
msgid "Clear"
msgstr "Limpiar"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.es_mx/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.es_mx/strings.po
index 5ebe3efc57..18cc097109 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.es_mx/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.es_mx/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-09-05 17:37+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.et_ee/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.et_ee/strings.po
index 746d863f1c..b7a03342e7 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.et_ee/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.et_ee/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:14+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.eu_es/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.eu_es/strings.po
index 85bc54c7d3..6d2eb561df 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.eu_es/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.eu_es/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-02-17 22:58+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.fa_af/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.fa_af/strings.po
index 0f48ef1be1..36b6689cc5 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.fa_af/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.fa_af/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.fa_ir/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.fa_ir/strings.po
index e5453a6ca2..89236c7122 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.fa_ir/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.fa_ir/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.fi_fi/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.fi_fi/strings.po
index ca17033831..0d7eaf0df5 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.fi_fi/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.fi_fi/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2022-07-24 08:15+0000\n"
+"PO-Revision-Date: 2023-09-10 18:37+0000\n"
"Last-Translator: Oskari Lavinto <olavinto@protonmail.com>\n"
"Language-Team: Finnish <https://kodi.weblate.cloud/projects/kodi-add-ons-game/game-controller-keyboard/fi_fi/>\n"
"Language: fi_fi\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.13\n"
+"X-Generator: Weblate 4.18.2\n"
msgctxt "Addon Summary"
msgid "IBM Model M keyboard"
@@ -588,4 +588,8 @@ msgstr "Numeronäppäimistön on yhtä kuin"
msgctxt "#30140"
msgid "Clear"
-msgstr "Tyhjennä"
+msgstr "Clear"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.fo_fo/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.fo_fo/strings.po
index ac27bc5721..df1447794e 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.fo_fo/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.fo_fo/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.fr_ca/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.fr_ca/strings.po
index 770794365a..2d958c5776 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.fr_ca/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.fr_ca/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.fr_fr/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.fr_fr/strings.po
index e10d4a10fd..32a861d89f 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.fr_fr/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.fr_fr/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-01-30 18:04+0000\n"
"Last-Translator: skypichat <skypichat@hotmail.fr>\n"
@@ -589,3 +589,7 @@ msgstr "Pavé Num. Equals"
msgctxt "#30140"
msgid "Clear"
msgstr "Clear"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.gl_es/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.gl_es/strings.po
index eaaa5c2645..6feb20da2f 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.gl_es/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.gl_es/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.he_il/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.he_il/strings.po
index fd165f1fb1..3a0810c031 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.he_il/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.he_il/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.hi_in/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.hi_in/strings.po
index 1cc6d4bb17..e1e04dfa0c 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.hi_in/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.hi_in/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.hr_hr/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.hr_hr/strings.po
index a685c88515..d0dde31546 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.hr_hr/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.hr_hr/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.hu_hu/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.hu_hu/strings.po
index 32dcb13fc4..3c704c4f26 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.hu_hu/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.hu_hu/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.hy_am/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.hy_am/strings.po
index 7fe2378b9b..5ce37ac2f9 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.hy_am/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.hy_am/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.id_id/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.id_id/strings.po
index 22bfe8c122..39f11f5f1d 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.id_id/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.id_id/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2022-07-24 08:15+0000\n"
+"PO-Revision-Date: 2023-10-23 01:11+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Indonesian <https://kodi.weblate.cloud/projects/kodi-add-ons-game/game-controller-keyboard/id_id/>\n"
"Language: id_id\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.13\n"
+"X-Generator: Weblate 5.0.2\n"
msgctxt "Addon Summary"
msgid "IBM Model M keyboard"
@@ -328,7 +328,7 @@ msgstr ""
msgctxt "#30075"
msgid "Pause"
-msgstr ""
+msgstr "Jeda"
msgctxt "#30076"
msgid "Insert"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.is_is/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.is_is/strings.po
index 9bad1794ab..087a456492 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.is_is/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.is_is/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.it_it/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.it_it/strings.po
index c0ce1295af..bc22b3a094 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.it_it/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.it_it/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2023-03-18 04:16+0000\n"
+"PO-Revision-Date: 2023-11-29 13:11+0000\n"
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
"Language-Team: Italian <https://kodi.weblate.cloud/projects/kodi-add-ons-game/game-controller-keyboard/it_it/>\n"
"Language: it_it\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "IBM Model M keyboard"
@@ -452,19 +452,19 @@ msgstr "F15"
msgctxt "#30106"
msgid "Left Meta"
-msgstr "Meta sinistra"
+msgstr "Meta sinistro"
msgctxt "#30107"
msgid "Right Meta"
-msgstr "Meta destra"
+msgstr "Meta destro"
msgctxt "#30108"
msgid "Left Super"
-msgstr "Sinistra super"
+msgstr "Super sinistro"
msgctxt "#30109"
msgid "Right Super"
-msgstr "Destra super"
+msgstr "Super destro"
msgctxt "#30110"
msgid "!"
@@ -580,7 +580,7 @@ msgstr "€"
msgctxt "#30138"
msgid "Undo"
-msgstr "Annullare"
+msgstr "Annulla"
msgctxt "#30139"
msgid "Num Pad Equals"
@@ -589,3 +589,7 @@ msgstr "Tastierino numerico uguale"
msgctxt "#30140"
msgid "Clear"
msgstr "Pulisci"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ja_jp/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ja_jp/strings.po
index 91726c8c79..65f9ee50b1 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ja_jp/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ja_jp/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.kn_in/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.kn_in/strings.po
index 87388ee850..05fad666a3 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.kn_in/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.kn_in/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ko_kr/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ko_kr/strings.po
index 221e875d4b..d19ae3534a 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ko_kr/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ko_kr/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.lt_lt/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.lt_lt/strings.po
index 6d7bea5473..3271199feb 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.lt_lt/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.lt_lt/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.lv_lv/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.lv_lv/strings.po
index 7502620e01..287c14a927 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.lv_lv/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.lv_lv/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.mi/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.mi/strings.po
index 5f0b6b3d74..e03dd69cfb 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.mi/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.mi/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.mk_mk/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.mk_mk/strings.po
index 7d61c29a5f..a3392b9cc8 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.mk_mk/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.mk_mk/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ml_in/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ml_in/strings.po
index a6bcb586ef..d5759d9a3e 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ml_in/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ml_in/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.mn_mn/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.mn_mn/strings.po
index ed87eb964c..d5b2f68dc2 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.mn_mn/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.mn_mn/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ms_my/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ms_my/strings.po
index 1470da2f6c..f8164d04e8 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ms_my/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ms_my/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.mt_mt/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.mt_mt/strings.po
index 50b9a5c6c9..f256e66916 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.mt_mt/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.mt_mt/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.my_mm/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.my_mm/strings.po
index 68cfd1d26c..a96fb0f7d0 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.my_mm/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.my_mm/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.nb_no/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.nb_no/strings.po
index b7f6f291f7..96b30e0efe 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.nb_no/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.nb_no/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.nl_nl/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.nl_nl/strings.po
index dc09784df2..5f9caf3f1c 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.nl_nl/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.nl_nl/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.oc_fr/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.oc_fr/strings.po
index 18c48b6482..e2375b3d18 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.oc_fr/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.oc_fr/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.os_os/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.os_os/strings.po
index 8666972e96..ccdb474c28 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.os_os/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.os_os/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.pl_pl/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.pl_pl/strings.po
index 0e0481e47d..50165b7580 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.pl_pl/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.pl_pl/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-03-23 23:23+0000\n"
"Last-Translator: Marek Adamski <fevbew@wp.pl>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.pt_br/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.pt_br/strings.po
index 8a756b522a..42547f5d03 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.pt_br/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.pt_br/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.pt_pt/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.pt_pt/strings.po
index 4488c44561..f611be5d2f 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.pt_pt/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.pt_pt/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-08-08 05:15+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ro_ro/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ro_ro/strings.po
index 575f2091e2..4541484c50 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ro_ro/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ro_ro/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-07-24 08:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ru_ru/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ru_ru/strings.po
index 9e494150f7..d84b02b3eb 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ru_ru/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ru_ru/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2021-08-15 09:52+0000\n"
"Last-Translator: Dmitry Petrov <dimakrm361@gmail.com>\n"
@@ -589,3 +589,7 @@ msgstr "Num Pad ="
msgctxt "#30140"
msgid "Clear"
msgstr "Clear"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.si_lk/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.si_lk/strings.po
index fae09b8b8d..7316bf7235 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.si_lk/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.si_lk/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.sk_sk/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.sk_sk/strings.po
index 16991c8ded..d2037c9515 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.sk_sk/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.sk_sk/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-03-12 01:16+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
@@ -589,3 +589,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.sl_si/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.sl_si/strings.po
index bc327689fb..ab04aaaf64 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.sl_si/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.sl_si/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.sq_al/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.sq_al/strings.po
index e562400be2..6356d60e35 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.sq_al/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.sq_al/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.sr_rs/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.sr_rs/strings.po
index 13ae9cfb2e..232b0d7b9d 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.sr_rs/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.sr_rs/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.sr_rs@latin/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.sr_rs@latin/strings.po
index 2975f60b85..0b8d62d277 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.sr_rs@latin/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.sr_rs@latin/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.sv_se/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.sv_se/strings.po
index 6f6f8bb5b7..a8967e2e7f 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.sv_se/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.sv_se/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.szl/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.szl/strings.po
index a2122d6604..8a4d0916af 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.szl/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.szl/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.ta_in/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.ta_in/strings.po
index 315e526e63..0d1cd037bb 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.ta_in/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.ta_in/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.te_in/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.te_in/strings.po
index 884f43fad5..1b98da4ea9 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.te_in/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.te_in/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.tg_tj/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.tg_tj/strings.po
index 3ddda30014..baeb5bf0e9 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.tg_tj/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.tg_tj/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.th_th/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.th_th/strings.po
index 354c919b54..9adb655a3c 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.th_th/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.th_th/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.tr_tr/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.tr_tr/strings.po
index 372fd8ebbd..147bdb7be2 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.tr_tr/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.tr_tr/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.uk_ua/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.uk_ua/strings.po
index 47ab7ac4d3..5a43e03e71 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.uk_ua/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.uk_ua/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2023-08-06 08:11+0000\n"
"Last-Translator: Did Kokos <did.kokos@gmail.com>\n"
@@ -589,3 +589,7 @@ msgstr "Num Pad ="
msgctxt "#30140"
msgid "Clear"
msgstr "Clear"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.uz_uz/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.uz_uz/strings.po
index fe96f94c00..6a4f5cd503 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.uz_uz/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.uz_uz/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.vi_vn/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.vi_vn/strings.po
index 16796a9ac2..a735c4b017 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.vi_vn/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.vi_vn/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.zh_cn/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.zh_cn/strings.po
index 15170bf44b..b4d29d13c7 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.zh_cn/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.zh_cn/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Addons\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
"PO-Revision-Date: 2022-02-10 11:38+0000\n"
"Last-Translator: Louis Hsu <louishsu86@gmail.com>\n"
@@ -589,3 +589,7 @@ msgstr "数字键盘 Equals"
msgctxt "#30140"
msgid "Clear"
msgstr "清除"
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/language/resource.language.zh_tw/strings.po b/addons/game.controller.keyboard/resources/language/resource.language.zh_tw/strings.po
index 1f31947a18..31c924995f 100644
--- a/addons/game.controller.keyboard/resources/language/resource.language.zh_tw/strings.po
+++ b/addons/game.controller.keyboard/resources/language/resource.language.zh_tw/strings.po
@@ -588,3 +588,7 @@ msgstr ""
msgctxt "#30140"
msgid "Clear"
msgstr ""
+
+msgctxt "#30141"
+msgid "OEM 102nd Key"
+msgstr ""
diff --git a/addons/game.controller.keyboard/resources/layout.xml b/addons/game.controller.keyboard/resources/layout.xml
index 3bdc76f260..9cb549d504 100644
--- a/addons/game.controller.keyboard/resources/layout.xml
+++ b/addons/game.controller.keyboard/resources/layout.xml
@@ -140,5 +140,6 @@
<key name="power" symbol="power" label="30136"/>
<key name="euro" symbol="euro" label="30137"/>
<key name="undo" symbol="undo" label="30138"/>
+ <key name="oem102" symbol="oem102" label="30141"/>
</category>
</layout>
diff --git a/addons/game.controller.mouse/addon.xml b/addons/game.controller.mouse/addon.xml
index 77aa31229f..3c17fe8dc2 100644
--- a/addons/game.controller.mouse/addon.xml
+++ b/addons/game.controller.mouse/addon.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="game.controller.mouse"
name="Computer Mouse"
- version="1.0.23"
+ version="1.0.26"
provider-name="Team Kodi">
<extension point="kodi.game.controller" library="resources/layout.xml"/>
<extension point="xbmc.addon.metadata">
diff --git a/addons/game.controller.mouse/resources/language/resource.language.ca_es/strings.po b/addons/game.controller.mouse/resources/language/resource.language.ca_es/strings.po
index b5c163f33f..278ec0d4ec 100644
--- a/addons/game.controller.mouse/resources/language/resource.language.ca_es/strings.po
+++ b/addons/game.controller.mouse/resources/language/resource.language.ca_es/strings.po
@@ -5,16 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: game.controller.mouse\n"
-"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2014-05-30 17:00+8\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
+"PO-Revision-Date: 2023-10-29 12:07+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"Language-Team: Catalan (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-game/game-controller-mouse/ca_es/>\n"
"Language: ca_es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 5.1\n"
msgctxt "Addon Summary"
msgid "Computer Mouse"
@@ -62,7 +63,7 @@ msgstr ""
msgctxt "#30009"
msgid "Button 4"
-msgstr ""
+msgstr "Botó 4"
msgctxt "#30010"
msgid "Button 5"
diff --git a/addons/game.controller.mouse/resources/language/resource.language.da_dk/strings.po b/addons/game.controller.mouse/resources/language/resource.language.da_dk/strings.po
index 8af6cb2bdf..12364bf89e 100644
--- a/addons/game.controller.mouse/resources/language/resource.language.da_dk/strings.po
+++ b/addons/game.controller.mouse/resources/language/resource.language.da_dk/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: game.controller.mouse\n"
-"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2021-08-18 16:16+0000\n"
+"PO-Revision-Date: 2023-12-07 21:41+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Danish <https://kodi.weblate.cloud/projects/kodi-add-ons-game/game-controller-mouse/da_dk/>\n"
"Language: da_dk\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.7.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Computer Mouse"
diff --git a/addons/game.controller.mouse/resources/language/resource.language.it_it/strings.po b/addons/game.controller.mouse/resources/language/resource.language.it_it/strings.po
index 4c783a65fa..379482903b 100644
--- a/addons/game.controller.mouse/resources/language/resource.language.it_it/strings.po
+++ b/addons/game.controller.mouse/resources/language/resource.language.it_it/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: game.controller.mouse\n"
-"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: 2014-05-30 17:00+8\n"
-"PO-Revision-Date: 2022-09-22 10:15+0000\n"
+"PO-Revision-Date: 2023-11-29 13:11+0000\n"
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
"Language-Team: Italian <https://kodi.weblate.cloud/projects/kodi-add-ons-game/game-controller-mouse/it_it/>\n"
"Language: it_it\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.14.1\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Computer Mouse"
diff --git a/addons/metadata.generic.artists/addon.xml b/addons/metadata.generic.artists/addon.xml
index 9c5d08f9fd..4059a35142 100644
--- a/addons/metadata.generic.artists/addon.xml
+++ b/addons/metadata.generic.artists/addon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="metadata.generic.artists" name="Generic Artist Scraper" version="1.0.14" provider-name="Team Kodi">
+<addon id="metadata.generic.artists" name="Generic Artist Scraper" version="1.0.18" provider-name="Team Kodi">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="xbmc.metadata" version="2.1.0"/>
@@ -16,6 +16,6 @@
<assets>
<icon>resources/icon.png</icon>
</assets>
- <news>- add wikipedia support</news>
+ <news>- add artist cutout image support</news>
</extension>
</addon>
diff --git a/addons/metadata.generic.artists/changelog.txt b/addons/metadata.generic.artists/changelog.txt
index 01e561895e..d8166a3240 100644
--- a/addons/metadata.generic.artists/changelog.txt
+++ b/addons/metadata.generic.artists/changelog.txt
@@ -1,3 +1,17 @@
+v1.0.18
+- add video links from theaudiodb
+
+v1.0.17
+- add artist cutout image support
+
+v1.0.16
+- fix extra art sorting
+- language updates
+
+v1.0.15
+- sort extra art
+- language updates
+
v1.0.14
- add translations
diff --git a/addons/metadata.generic.artists/lib/scraper.py b/addons/metadata.generic.artists/lib/scraper.py
index 0979ffbdb6..8d007a5f4c 100644
--- a/addons/metadata.generic.artists/lib/scraper.py
+++ b/addons/metadata.generic.artists/lib/scraper.py
@@ -26,6 +26,7 @@ from .musicbrainz import musicbrainz_artistdetails
from .nfo import nfo_geturl
from .theaudiodb import theaudiodb_artistdetails
from .theaudiodb import theaudiodb_artistalbums
+from .theaudiodb import theaudiodb_mvids
from .wikipedia import wikipedia_artistdetails
from .utils import *
@@ -110,6 +111,7 @@ class Scraper():
elif action == 'getdetails':
details = {}
discography = {}
+ mvids = {}
url = json.loads(url)
artist = url.get('artist')
mbartistid = url.get('mbartistid')
@@ -128,6 +130,10 @@ class Scraper():
thread = Thread(target = self.get_discography, args = (mbartistid, 'theaudiodb', discography))
threads.append(thread)
thread.start()
+ # theaudiodb mvids
+ thread = Thread(target = self.get_videolinks, args = (mbartistid, 'theaudiodb', mvids))
+ threads.append(thread)
+ thread.start()
# wait for musicbrainz to finish
threads[0].join()
# check if we have a result:
@@ -182,6 +188,12 @@ class Scraper():
else:
details[site] = {}
details[site]['albums'] = albumlist
+ for site, mvidlist in mvids.items():
+ if site in details:
+ details[site]['mvids'] = mvidlist
+ else:
+ details[site] = {}
+ details[site]['mvids'] = mvidlist
result = self.compile_results(details)
if result:
self.return_details(result)
@@ -327,6 +339,21 @@ class Scraper():
discography[site] = albumresults
return discography
+ def get_videolinks(self, param, site, videolinks):
+ json = True
+ if site == 'theaudiodb':
+ #theaudiodb mvid links
+ mvidurl = AUDIODBURL % (AUDIODBKEY, AUDIODBMVIDS % param)
+ scraper = theaudiodb_mvids
+ mviddata = get_data(mvidurl, json)
+ if not mviddata:
+ return
+ mvidresults = scraper(mviddata)
+ if not mvidresults:
+ return
+ videolinks[site] = mvidresults
+ return videolinks
+
def compile_results(self, details):
result = {}
thumbs = []
@@ -380,7 +407,8 @@ class Scraper():
thumbnails = []
fanart.reverse()
fanarts = []
- # the order for extra art does not matter
+ # extra art from most accurate sources first
+ extras.reverse()
extraart = []
for thumblist in thumbs:
for item in thumblist:
@@ -500,4 +528,11 @@ class Scraper():
listitem.setProperty('artist.album%i.year' % (count + 1), album['year'])
if 'musicbrainzreleasegroupid' in album:
listitem.setProperty('artist.album%i.musicbrainzreleasegroupid' % (count + 1), album['musicbrainzreleasegroupid'])
+ if 'mvids' in item:
+ listitem.setProperty('artist.videolinks', str(len(item['mvids'])))
+ for count, mvid in enumerate(item['mvids']):
+ listitem.setProperty('artist.videolink%i.title' % (count + 1), mvid['title'])
+ listitem.setProperty('artist.videolink%i.mbtrackid' % (count + 1), mvid['mbtrackid'])
+ listitem.setProperty('artist.videolink%i.url' % (count + 1), mvid['url'])
+ listitem.setProperty('artist.videolink%i.thumb' % (count + 1), mvid['thumb'])
xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=listitem)
diff --git a/addons/metadata.generic.artists/lib/theaudiodb.py b/addons/metadata.generic.artists/lib/theaudiodb.py
index c5c9cb264d..0f6de9eade 100644
--- a/addons/metadata.generic.artists/lib/theaudiodb.py
+++ b/addons/metadata.generic.artists/lib/theaudiodb.py
@@ -107,6 +107,12 @@ def theaudiodb_artistdetails(data):
extradata['preview'] = item['strArtistBanner'] + '/preview'
extradata['aspect'] = 'banner'
extras.append(extradata)
+ if item.get('strArtistCutout',''):
+ extradata = {}
+ extradata['image'] = item['strArtistCutout']
+ extradata['preview'] = item['strArtistCutout'] + '/preview'
+ extradata['aspect'] = 'cutout'
+ extras.append(extradata)
if extras:
artistdata['extras'] = extras
return artistdata
@@ -121,3 +127,27 @@ def theaudiodb_artistalbums(data):
albumdata['year'] = item.get('intYearReleased', '')
albums.append(albumdata)
return albums
+
+def theaudiodb_mvids(data):
+ mvids = []
+ mvidlist = data.get('mvids', [])
+ if mvidlist:
+ for item in mvidlist:
+ mviddata = {}
+ mviddata['title'] = item['strTrack']
+ mviddata['mbtrackid'] = item.get('strMusicBrainzID')
+ tempurl = item.get('strMusicVid', '')
+ # Find and remove extraneous data in the URL leaving just the video ID
+ index = tempurl.find('=')
+ vid_id = tempurl[index+1:]
+ http_index = vid_id.find('//youtu.be/')
+ if http_index != -1:
+ vid_id = vid_id[http_index+11:]
+ check1 = vid_id.find('/www.youtube.com/embed/')
+ if check1 != -1:
+ vid_id = vid_id[check1 + 23:check1 + 34]
+ mviddata['url'] = \
+ 'plugin://plugin.video.youtube/play/?video_id=%s' % vid_id
+ mviddata['thumb'] = item.get('strTrackThumb', '')
+ mvids.append(mviddata)
+ return mvids
diff --git a/addons/metadata.generic.artists/lib/utils.py b/addons/metadata.generic.artists/lib/utils.py
index 1958707454..335b4fc4a0 100644
--- a/addons/metadata.generic.artists/lib/utils.py
+++ b/addons/metadata.generic.artists/lib/utils.py
@@ -5,6 +5,7 @@ AUDIODBURL = 'https://www.theaudiodb.com/api/v1/json/%s/%s'
AUDIODBSEARCH = 'search.php?s=%s'
AUDIODBDETAILS = 'artist-mb.php?i=%s'
AUDIODBDISCOGRAPHY = 'discography-mb.php?s=%s'
+AUDIODBMVIDS = 'mvid-mb.php?i=%s'
MUSICBRAINZURL = 'https://musicbrainz.org/ws/2/artist/%s'
MUSICBRAINZSEARCH = '?query="%s"&fmt=json'
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.af_za/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.af_za/strings.po
index c3b1c33897..4b2af28ef7 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.af_za/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.af_za/strings.po
@@ -2,95 +2,84 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Afrikaans (South Africa) (https://www.transifex.com/teamxbmc/teams/40581/af_ZA/)\n"
+"Language: af_ZA\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: af_ZA\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Voorkeure"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Verkose taal vir kunstenaar biografie"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Verkies biografie vanaf"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Verkies diskografie vanaf"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Verkies genres vanaf"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Verkies style vanaf"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Verkies buie vanaf"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Laat minder akkurate soek resultate toe"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Indien beskikbaar, sal die kunstenaar biografie afgelaai word in die geselekteerde taal. Dit sal terugval na Engels."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer om die kunstenaar biografie te kry deur die geselekteerde skraper te gebruik. Ander skrapers sal gebruik word as die verkose skraper geen resultate oplewer nie."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer om die kunstenaar diskografie te kry deur die geselekteerde skraper te gebruik. Ander skrapers sal gebruik word as die verkose skraper geen resultate oplewer nie."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer om genre inligting te kry deur die geselekteerde skraper te gebruik. Ander skrapers sal gebruik word as die verkose skraper geen resultate oplewer nie."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer om styl inligting te kry deur die geselekteerde skraper te gebruik. Ander skrapers sal gebruik word as die verkose skraper geen resultate oplewer nie."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer om bui inligting te kry deur die geselekteerde skraper te gebruik. Ander skrapers sal gebruik word as die verkose skraper geen resultate oplewer nie."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "As Musicbrainz nie 'n skakel verskaf na allmusic en/of discogs, kan ons daardie werwe deursoek gebasseer op artistname. Aangesien kunstenaar name nie uniek is nie, kan dit potensieel volg tot 'n mengsel van metadata vanaf veelvuldige kunstenaars met dieselfde naam."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.am_et/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.am_et/strings.po
index a99b27e3af..c71204400d 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.am_et/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.am_et/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# samson <sambelet@yahoo.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: samson <sambelet@yahoo.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Amharic (Ethiopia) (https://www.transifex.com/teamxbmc/teams/40581/am_ET/)\n"
+"Language: am_ET\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: am_ET\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "ምርጫዎች"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ar_sa/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ar_sa/strings.po
index 79c6fd3da0..8ee32d1044 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ar_sa/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ar_sa/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Arabic (Saudi Arabia) (https://www.transifex.com/teamxbmc/teams/40581/ar_SA/)\n"
+"Language: ar_SA\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ar_SA\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ast_es/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ast_es/strings.po
index 269c8e37ac..5127bd3c07 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ast_es/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ast_es/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# enolp <enolp@softastur.org>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: enolp <enolp@softastur.org>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Asturian (Spain) (https://www.transifex.com/teamxbmc/teams/40581/ast_ES/)\n"
+"Language: ast_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ast_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Preferences"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.az_az/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.az_az/strings.po
index 12b48a84d0..e20e414b1a 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.az_az/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.az_az/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Azerbaijani (Azerbaijan) (https://www.transifex.com/teamxbmc/teams/40581/az_AZ/)\n"
+"Language: az_AZ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: az_AZ\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.be_by/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.be_by/strings.po
index 393261ee14..610711a484 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.be_by/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.be_by/strings.po
@@ -5,88 +5,82 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Language-Team: Belarusian (Belarus) (https://www.transifex.com/teamxbmc/teams/40581/be_BY/)\n"
+"PO-Revision-Date: 2022-03-16 12:13+0000\n"
+"Last-Translator: Antikruk <zmicerturok@gmail.com>\n"
+"Language-Team: Belarusian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/be_by/>\n"
+"Language: be_by\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: be_BY\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Weblate 4.11.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Сродак для пошуку звестак пра выканаўцаў"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Пошук звестак пра выканаўцаў і іх фота на розных сайтах."
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "Налады"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Пераважная мова для біяграфіі выканаўцы"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Аддаваць перавагу біяграфіі з"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Аддаваць перавагу дыскаграфіі з"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Аддаваць перавагу жанрам з"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Аддаваць перавагу стылям з"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Аддаваць перавагу настроям з"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Дазволіць атрымліваць менш дакладныя вынікі пошуку"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Калі даступна, біяграфія выканаўцы будзе спампаваная на абранай мове. Калі не, то на англійскай."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Медыяцэнтр будзе спрабаваць атрымаць біяграфію выканаўцы з дапамогай абранага сродку. Іншыя сродкі будуць выкарыстоўвацца, калі гэты нічога не знойдзе."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Медыяцэнтр будзе спрабаваць атрымаць дыскаграфію выканаўцы з дапамогай абранага сродку. Іншыя сродкі будуць выкарыстоўвацца, калі гэты нічога не знойдзе."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Медыяцэнтр будзе спрабаваць атрымаць звесткі пра жанр з дапамогай гэтага сродку. Іншыя сродкі будуць выкарыстоўвацца, калі гэты нічога не знойдзе."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Медыяцэнтр будзе спрабаваць атрымаць звесткі пра стыль з дапамогай абранага сродку. Іншыя сродкі будуць выкарыстоўвацца, калі гэты нічога не знойдзе."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Медыяцэнтр будзе спрабаваць атрымаць звесткі пра настрой з дапамогай абранага сродку. Іншыя сродкі будуць выкарыстоўвацца, калі гэты нічога не знойдзе."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Калі Musicbrainz не падае спасылкі на allmusic або discogs, мы можам выканаць пошук на гэтых сайтах па выканаўцы. Калі назвы (імёны) выканаўцаў аднолькавыя, то метаданыя розных выканаўцаў могуць змяшацца."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.bg_bg/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.bg_bg/strings.po
index 9ac44d6e89..2d54f02db6 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.bg_bg/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.bg_bg/strings.po
@@ -2,24 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Любомир Василев, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Любомир Василев, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Bulgarian (Bulgaria) (https://www.transifex.com/teamxbmc/teams/40581/bg_BG/)\n"
+"Language: bg_BG\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: bg_BG\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Предпочитания"
@@ -53,61 +57,29 @@ msgid "Allow less accurate search results"
msgstr "Разрешаване на по-неточни резултати при търсене"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"При възможност, биографията на изпълнителя ще бъде свалена на избрания език."
-" Иначе ще се показва на английски."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "При възможност, биографията на изпълнителя ще бъде свалена на избрания език. Иначе ще се показва на английски."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Биографията на изпълнителя по възможност ще се получава чрез избрания "
-"скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Биографията на изпълнителя по възможност ще се получава чрез избрания скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Дискографията на изпълнителя по възможност ще се получава чрез избрания "
-"скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Дискографията на изпълнителя по възможност ще се получава чрез избрания скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Информацията за жанра по възможност ще се получава чрез избрания скрипт. Ако"
-" избраният скрипт не върне резултат, ще бъдат използвани други."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Информацията за жанра по възможност ще се получава чрез избрания скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Информацията за стила по възможност ще се получава чрез избрания скрипт. Ако"
-" избраният скрипт не върне резултат, ще бъдат използвани други."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Информацията за стила по възможност ще се получава чрез избрания скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
-"Информацията за настроението по възможност ще се получава чрез избрания "
-"скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Информацията за настроението по възможност ще се получава чрез избрания скрипт. Ако избраният скрипт не върне резултат, ще бъдат използвани други."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Ако „Musicbrainz“ не предоставя връзка към „allmusic“ и/или „discogs“, можем"
-" да потърсим в тези уеб сайтове по името на изпълнителя. Тъй като имената на"
-" изпълнителите не са уникални, то данните може да са объркани между "
-"различните изпълнители."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Ако „Musicbrainz“ не предоставя връзка към „allmusic“ и/или „discogs“, можем да потърсим в тези уеб сайтове по името на изпълнителя. Тъй като имената на изпълнителите не са уникални, то данните може да са объркани между различните изпълнители."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.bs_ba/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.bs_ba/strings.po
index 725cb24341..c3a4ad0e42 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.bs_ba/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.bs_ba/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Bosnian (Bosnia and Herzegovina) (https://www.transifex.com/teamxbmc/teams/40581/bs_BA/)\n"
+"Language: bs_BA\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: bs_BA\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ca_es/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ca_es/strings.po
index 0df2364562..c0dee3532c 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ca_es/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ca_es/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Catalan (Spain) (https://www.transifex.com/teamxbmc/teams/40581/ca_ES/)\n"
+"PO-Revision-Date: 2022-08-09 18:14+0000\n"
+"Last-Translator: Xean <xeanhort007@gmail.com>\n"
+"Language-Team: Catalan (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/ca_es/>\n"
+"Language: ca_es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ca_ES\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.13\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Raspador musical genèric per a artistes"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Cerca informació sobre artistes i obres d'art a diversos llocs web."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Preferències"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Idioma preferit per a la biografia de l'artista"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Prefereix la biografia de"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Prefereix la discografia de"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Prefereix els gèneres de"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Prefereix estils de"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Prefereix els estats d'ànim de"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Permet resultats de cerca menys precisos"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Si està disponible, la biografia de l'artista es baixarà en l'idioma seleccionat. Sinó es baixarà en anglès."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtenir la biografia de l'artista utilitzant el rascador seleccionat. S'utilitzaran altres rascadors si el preferit no aconsegueix cap resultat."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtenir discografia de l'artista utilitzant el rascador seleccionat. S'utilitzaran altres rascadors si el preferit no aconsegueix cap resultat."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtenir la informació de gènere utilitzant el rascador seleccionat. S'utilitzaran altres rascadors si el preferit no aconsegueix cap resultat."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtenir la informació d'estil utilitzant el rascador seleccionat. S'utilitzaran altres rascadors si el preferit no aconsegueix cap resultat."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtenir la informació de l'estat d'ànim utilitzant el rascador seleccionat. S'utilitzaran altres rascadors si el preferit no aconsegueix cap resultat."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Si Musicbrainz no proporciona un enllaç a allmusic i/o discogs, podem cercar aquests llocs en funció del nom de l'artista. Com que els noms dels artistes no són únics, això podria donar lloc a una barreja de metadades de diversos artistes amb el mateix nom."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.cs_cz/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.cs_cz/strings.po
index ca07a3c998..bf65b0de07 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.cs_cz/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.cs_cz/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Jan Krejčí, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Jan Krejčí, 2020\n"
-"Language-Team: Czech (Czech Republic) (https://www.transifex.com/teamxbmc/teams/40581/cs_CZ/)\n"
+"PO-Revision-Date: 2022-03-28 14:13+0000\n"
+"Last-Translator: Kryštof Černý <cleverline1mc@gmail.com>\n"
+"Language-Team: Czech <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/cs_cz/>\n"
+"Language: cs_cz\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: cs_CZ\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
+"X-Generator: Weblate 4.11.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Obecný scraper hudby pro umělce"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Vyhledává informace o umělcích a grafiku napříč různými weby."
msgctxt "#30000"
msgid "Preferences"
@@ -26,7 +31,7 @@ msgstr "Předvolby"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Preferovaný jazyk pro biografii umělce"
msgctxt "#30002"
msgid "Prefer biography from"
@@ -53,51 +58,29 @@ msgid "Allow less accurate search results"
msgstr "Povolit méně přesné výsledky vyhledávání"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Biografie umělce se stáhne ve vybraném jazyce, pokud je dostupná. V opačném "
-"případě se stáhne v angličtině."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Biografie umělce se stáhne ve vybraném jazyce, pokud je dostupná. V opačném případě se stáhne v angličtině."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokuste se získat biografii umělce pomocí vybraného zdroje. Pokud preferovaný zdroj nevrátí žádné výsledky, bude použit jiný zdroj."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokuste se získat diskografii umělce pomocí vybraného zdroje. Pokud preferovaný zdroj nevrátí žádné výsledky, bude použit jiný zdroj."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokuste se získat informace o žánru pomocí vybraného zdroje. Pokud preferovaný zdroj nevrátí žádné výsledky, bude použit jiný zdroj."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokuste se získat informace o stylu pomocí vybraného zdroje. Pokud preferovaný zdroj nevrátí žádné výsledky, bude použit jiný zdroj."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokuste se získat informace o náladě pomocí vybraného zdroje. Pokud preferovaný zdroj nevrátí žádné výsledky, bude použit jiný zdroj."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Pokud Musicbrainz neposkytne odkaz na allmusic a /nebo discogs, můžeme tyto "
-"stránky prohledat podle jména umělce. Protože jména umělců nejsou jedinečná,"
-" může to potenciálně způsobit smíchání metadat od několika umělců se stejným"
-" jménem."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Pokud Musicbrainz neposkytne odkaz na allmusic a /nebo discogs, můžeme tyto stránky prohledat podle jména umělce. Protože jména umělců nejsou jedinečná, může to potenciálně způsobit smíchání metadat od několika umělců se stejným jménem."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.cy_gb/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.cy_gb/strings.po
index 7799399c5d..3040d62f19 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.cy_gb/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.cy_gb/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Welsh (United Kingdom) (https://www.transifex.com/teamxbmc/teams/40581/cy_GB/)\n"
+"Language: cy_GB\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: cy_GB\n"
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.da_dk/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.da_dk/strings.po
index a49ac8bea0..069de0a506 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.da_dk/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.da_dk/strings.po
@@ -5,88 +5,82 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Language-Team: Danish (Denmark) (https://www.transifex.com/teamxbmc/teams/40581/da_DK/)\n"
+"PO-Revision-Date: 2021-07-26 20:39+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"Language-Team: Danish <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/da_dk/>\n"
+"Language: da_dk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: da_DK\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.7.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Generisk musik-scraper til kunstnere"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Søger efter kunstneroplysninger og illustrationer på tværs af flere websteder."
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "Præferencer"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Foretrukket sprog til kunstnerbiografi"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Foretræk biografi fra"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Foretræk diskografi fra"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Foretræk genrer fra"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Foretræk stilarter fra"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Foretræk stemninger fra"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Tillad mindre nøjagtige søgeresultater"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Hvis det er tilgængeligt, downloades kunstnerens biografi på det valgte sprog. Ellers hentes den på engelsk."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Forsøg at få kunstnerens biografi ved hjælp af den valgte scraper. Andre scrapere vil blive benyttet, hvis den foretrukne skraber ikke giver noget resultat."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Forsøg at få kunstnerens diskografi ved hjælp af den valgte scraper. Andre scrapere vil blive benyttet, hvis den foretrukne scraper ikke giver noget resultat."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Forsøg at få genreinformation ved hjælp af den valgte scraper. Andre scrapere vil blive benyttet, hvis den foretrukne scraper ikke giver noget resultat."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Forsøg at få information om stilart ved hjælp af den valgte scraper. Andre scrapere vil blive benyttet, hvis den foretrukne scraper ikke giver noget resultat."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Forsøg at få information om stemning ved hjælp af den valgte scraper. Andre scrapere vil blive benyttet, hvis den foretrukne scraper ikke giver noget resultat."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Hvis Musicbrainz ikke giver et link til allmusic og/eller discogs, kan vi søge på disse sider ved hjælp af kunstnernavn. Da kunstnernavne ikke er unikke, kan dette potentielt resultere i en blanding af metadata fra flere kunstnere med samme navn."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.de_de/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.de_de/strings.po
index cd31daeb30..bd864c952f 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.de_de/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.de_de/strings.po
@@ -2,95 +2,85 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: German (Germany) (https://www.transifex.com/teamxbmc/teams/40581/de_DE/)\n"
+"PO-Revision-Date: 2021-07-13 18:29+0000\n"
+"Last-Translator: Kai Sommerfeld <kai.sommerfeld@gmx.com>\n"
+"Language-Team: German <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/de_de/>\n"
+"Language: de_de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: de_DE\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.7.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Generischer Musik-Scraper für Interpreten"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Sucht nach Interpreten-Informationen und Artwork auf verschiedenen Websites."
msgctxt "#30000"
msgid "Preferences"
-msgstr "Präferenzen"
+msgstr "Einstellungen"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Bevorzugte Sprache für Interpreten-Biografien"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Biografien bevorzugen von"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Diskographien bevorzugen von"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Genres bevorzugen von"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Styles bevorzugen von"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Stimmungen bevorzugen von"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Weniger genaue Suchergebnisse erlauben"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Wenn verfügbar, werden Interpreten-Biografien in der ausgewählten Sprache heruntergeladen. Fallback ist englisch."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Versuchen, Interpreten-Biografien mit dem ausgewählten Scraper zu erhalten. Wenn der bevorzugte Scraper keine Ergebnisse liefert, werden andere Scraper verwendet."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Versuchen, Interpreten-Diskographien mit dem ausgewählten Scraper zu erhalten. Wenn der bevorzugte Scraper keine Ergebnisse liefert, werden andere Scraper verwendet."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Versuchen, Genre-Informationen mit dem ausgewählten Scraper zu erhalten. Wenn der bevorzugte Scraper keine Ergebnisse liefert, werden andere Scraper verwendet."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Versuchen, Style-Informationen mit dem ausgewählten Scraper zu erhalten. Wenn der bevorzugte Scraper keine Ergebnisse liefert, werden andere Scraper verwendet."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Versuchen, Stimmungs-Informationen mit dem ausgewählten Scraper zu erhalten. Wenn der bevorzugte Scraper keine Ergebnisse liefert, werden andere Scraper verwendet."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Wenn MusicBrainz weder einen Link zu allmusic noch zu discogs liefert, können diese Sites mit dem Interpretennamen durchsucht werden. Da Interpretennamen nicht eindeutig sind, kann es dabei zu einer fälschlichen Vermischung von Metadaten von verschiedenen Interpreten mit dem gleichen Namen kommen."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.el_gr/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.el_gr/strings.po
index c91c09c5c5..1bb4f39e83 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.el_gr/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.el_gr/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Greek (Greece) (https://www.transifex.com/teamxbmc/teams/40581/el_GR/)\n"
+"Language: el_GR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: el_GR\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.en_au/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.en_au/strings.po
index 3527a96a91..181902bccd 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.en_au/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.en_au/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: English (Australia) (https://www.transifex.com/teamxbmc/teams/40581/en_AU/)\n"
+"Language: en_AU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: en_AU\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.en_gb/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.en_gb/strings.po
index 4b953e73e6..0b1707e398 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.en_gb/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.en_gb/strings.po
@@ -4,18 +4,26 @@
# Addon Provider: Team Kodi
msgid ""
msgstr ""
-"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Project-Id-Version: Kodi add-ons\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Kodi Translation Team\n"
-"Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/kodi-main/language/en_GB/)\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: en_GB\n"
+"Language: en_gb\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.en_nz/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.en_nz/strings.po
index 7c249db137..3962aaffe7 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.en_nz/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.en_nz/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: English (New Zealand) (https://www.transifex.com/teamxbmc/teams/40581/en_NZ/)\n"
+"Language: en_NZ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: en_NZ\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Preferences"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.en_us/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.en_us/strings.po
index c0a1b90d08..dfa4beb960 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.en_us/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.en_us/strings.po
@@ -2,95 +2,84 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: English (United States) (https://www.transifex.com/teamxbmc/teams/40581/en_US/)\n"
+"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: en_US\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Preferences"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Preferred language for artist biography"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Prefer biography from"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Prefer discography from"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Prefer genres from"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Prefer styles from"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Prefer moods from"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Allow less accurate search results"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.eo/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.eo/strings.po
index 6d68088b2f..ec2d01f5ed 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.eo/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.eo/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Esperanto (https://www.transifex.com/teamxbmc/teams/40581/eo/)\n"
+"Language: eo\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: eo\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.es_ar/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.es_ar/strings.po
index d417c90936..ec8e8b2cdd 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.es_ar/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.es_ar/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Spanish (Argentina) (https://www.transifex.com/teamxbmc/teams/40581/es_AR/)\n"
+"Language: es_AR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: es_AR\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.es_es/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.es_es/strings.po
index 272c8bc293..fcf06ab7a5 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.es_es/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.es_es/strings.po
@@ -2,24 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Victor Vinuela <vinuela.victor@dissectio.com>, 2020
-# Rafa Oliveros, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Rafa Oliveros, 2020\n"
-"Language-Team: Spanish (Spain) (https://www.transifex.com/teamxbmc/teams/40581/es_ES/)\n"
+"PO-Revision-Date: 2022-02-23 10:13+0000\n"
+"Last-Translator: Alfonso Cachero <alfonso.cachero@gmail.com>\n"
+"Language-Team: Spanish (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/es_es/>\n"
+"Language: es_es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: es_ES\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.10.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Scraper de música genérico para los intérpretes"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Busca información e imágenes sobre el intérprete en varios sitios web."
msgctxt "#30000"
msgid "Preferences"
@@ -27,7 +31,7 @@ msgstr "Preferencias"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr "Idioma preferido para la biografía del artista"
+msgstr "Idioma preferido para la biografía del intérprete"
msgctxt "#30002"
msgid "Prefer biography from"
@@ -54,64 +58,29 @@ msgid "Allow less accurate search results"
msgstr "Permitir resultados de búsqueda menos precisos"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Si está disponible, la biografía del artista se descargará en el idioma "
-"seleccionado. La segunda opción será el inglés."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Si está disponible, la biografía del intérprete se descargará en el idioma seleccionado. La segunda opción será el inglés."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Intenta obtener la biografía del artista utilizando el raspador "
-"seleccionado. Se utilizarán otros raspadores si el raspador preferido no "
-"arroja resultados."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtener la biografía del intérprete utilizando el scraper seleccionado. Se utilizarán otros scrapers si el scraper preferido no obtiene resultados."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Intenta obtener la discografía del artista utilizando el raspador "
-"seleccionado. Se utilizarán otros raspadores si el raspador preferido no "
-"arroja resultados."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtener la discografía del intérprete utilizando el scraper seleccionado. Se utilizarán otros scrapers si el scraper preferido no obtiene resultados."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Intenta obtener información de género con el raspador seleccionado. Se "
-"utilizarán otros raspadores si el raspador preferido no arroja resultados."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtener información de género con el scraper seleccionado. Se utilizarán otros scrapers si el scraper preferido no obtiene resultados."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Intenta obtener información de estilo con el raspador seleccionado. Se "
-"utilizarán otros raspadores si el raspador preferido no arroja resultados."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtener información de estilo con el scraper seleccionado. Se utilizarán otros scrapers si el scraper preferido no obtiene resultados."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
-"Intenta obtener información sobre el estado de ánimo con el raspador "
-"seleccionado. Se utilizarán otros raspadores si el raspador preferido no "
-"arroja resultados."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intenta obtener información sobre el estado de ánimo con el scraper seleccionado. Se utilizarán otros scrapers si el scraper preferido no obtiene resultados."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Si Musicbrainz no proporciona un enlace a allmusic y/o discogs, podemos "
-"buscar en esos sitios según el nombre del artista. Dado que los nombres de "
-"los artistas no son únicos, esto podría dar como resultado una mezcla de "
-"metadatos de varios artistas con el mismo nombre."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Si Musicbrainz no proporciona un enlace a allmusic y/o discogs, podemos buscar en esos sitios según el nombre del intérprete. Dado que los nombres de los intérpretes no son únicos, esto podría dar como resultado una mezcla de metadatos de varios intérpretes con el mismo nombre."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.es_mx/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.es_mx/strings.po
index bc88b60cec..61b0806713 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.es_mx/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.es_mx/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Spanish (Mexico) (https://www.transifex.com/teamxbmc/teams/40581/es_MX/)\n"
+"PO-Revision-Date: 2021-10-09 02:30+0000\n"
+"Last-Translator: Edson Armando <edsonarmando78@outlook.com>\n"
+"Language-Team: Spanish (Mexico) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/es_mx/>\n"
+"Language: es_mx\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: es_MX\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.8\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Scraper de música genérico para artistas"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Busca arte e información de artista en múltiples sitios web."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Preferencias"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Idioma preferido para la biografía del artista"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Preferir biografía de"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Preferir discografía de"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Preferir géneros de"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Preferir estilos de"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Preferir ambientes de"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Permitir resultados de búsqueda menos precisos"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Si está disponible, la biografía del artista se descargará en el idioma seleccionado. Alternativamente se descargará en inglés."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intentar obtener la biografía del artista usando el scraper seleccionado. Otros scrapers serán usados si el preferido no devuelve resultados.."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intentar obtener la discografía del artista usando el scraper seleccionado. Otros scrapers serán usados si el preferido no devuelve resultados.."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intentar obtener información de género usando el scraper seleccionado. Otros scrapers serán usados si el preferido no devuelve resultados.."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intentar obtener información de estilo usando el scraper seleccionado. Otros scrapers serán usados si el preferido no devuelve resultados.."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Intentar obtener información de ambiente usando el scraper seleccionado. Otros scrapers serán usados si el preferido no devuelve resultados.."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Si Musicbrainz no provee un enlace de allmusic y/o discogs, se puede buscar basado en el nombre del artista. Como los nombres de artista no son únicos, esto podría resultar en una mezcla de metadatos de diferentes artistas de un mismo nombre."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.et_ee/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.et_ee/strings.po
index ddf5ede29b..48d2b33451 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.et_ee/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.et_ee/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# Olav <olav.magi@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Olav <olav.magi@gmail.com>, 2020\n"
-"Language-Team: Estonian (Estonia) (https://www.transifex.com/teamxbmc/teams/40581/et_EE/)\n"
+"PO-Revision-Date: 2022-01-24 13:13+0000\n"
+"Last-Translator: rimasx <riks_12@hot.ee>\n"
+"Language-Team: Estonian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/et_ee/>\n"
+"Language: et_ee\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: et_EE\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.10.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Geneeriline esitajate muusikakaabits"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Otsib esitajate teavet ja fännipilte mitmest allikast."
msgctxt "#30000"
msgid "Preferences"
@@ -25,87 +31,56 @@ msgstr "Eelistused"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr "Eelistatud keel esitaja andmete puhul"
+msgstr "Esitaja andme eelistatud keel"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr "Eelista andmeid kohast"
+msgstr "Eelista andmeid allikast"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr "Eelista muusikalisi andmeid kohast"
+msgstr "Eelista diskograafiat allikast"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr "Eelista žanreid kohast"
+msgstr "Eelista žanreid allikast"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr "Eelista stiile kohast"
+msgstr "Eelista stiile allikast"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr "Eelista tujusid kohast"
+msgstr "Eelista tujusid allikast"
msgctxt "#30101"
msgid "Allow less accurate search results"
msgstr "Luba vähemtäpsemad otsingutulemused"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Olemasolul laaditakse esitaja elulugu alla valitud keeles. Selle puudumisel "
-"kasutatakse inglise keelt."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Võimalusel laaditakse esitaja elulugu alla valitud keeles. Selle puudumisel kasutatakse inglise keelt."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Ürita hankida esitaja elulugu valitud kaabitsaga. Teisi kaabitsaid "
-"kasutatakse, kui eelistatud kaabits ei leia otsitut."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Ürita hankida esitaja elulugu valitud kaabitsaga. Teisi kaabitsaid kasutatakse, kui eelistatud kaabits ei leia otsitut."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Ürita hankida esitaja musikaalset ajalugu valitud kaabitsaga. Teisi "
-"kaabitsaid kasutatakse, kui eelistatud kaabits ei leia otsitut."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Ürita hankida esitaja diskograafia valitud kaabitsaga. Teisi kaabitsaid kasutatakse, kui eelistatud kaabits ei leia otsitut."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Ürita hankida žanri teavet valitud kaabitsaga. Teisi kaabitsaid kasutatakse,"
-" kui eelistatud kaabits ei leia otsitut."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Ürita hankida žanri teavet valitud kaabitsaga. Teisi kaabitsaid kasutatakse, kui eelistatud kaabits ei leia otsitut."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Ürita hankida stiili teavet valitud kaabitsaga. Teisi kaabitsaid "
-"kasutatakse, kui eelistatud kaabits ei leia otsitut."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Ürita hankida stiili teavet valitud kaabitsaga. Teisi kaabitsaid kasutatakse, kui eelistatud kaabits ei leia otsitut."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
-"Ürita hankida tuju teavet valitud kaabitsaga. Teisi kaabitsaid kasutatakse, "
-"kui eelistatud kaabits ei leia otsitut."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Ürita hankida tuju teavet valitud kaabitsaga. Teisi kaabitsaid kasutatakse, kui eelistatud kaabits ei leia otsitut."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Kui Musicbrainz ei paku allmusicu ja/või discogsi linki, saame neist "
-"lehtedest otsida esitaja ja albumi nime järgi. Kuna on olemas samanimelised "
-"esitajad, võivad nende metaandmed seguneda."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Kui Musicbrainz ei paku allmusicu ja/või discogsi linki, saame neist lehtedest otsida esitaja ja albumi nime järgi. Kuna on olemas samanimelised esitajad, võivad nende metaandmed seguneda."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.eu_es/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.eu_es/strings.po
index 8877536b0b..f3cbbceb29 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.eu_es/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.eu_es/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Basque (Spain) (https://www.transifex.com/teamxbmc/teams/40581/eu_ES/)\n"
+"Language: eu_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: eu_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fa_af/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fa_af/strings.po
index 701cbc4040..93cc71be74 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.fa_af/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fa_af/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Persian (Afghanistan) (https://www.transifex.com/teamxbmc/teams/40581/fa_AF/)\n"
+"Language: fa_AF\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fa_AF\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fa_ir/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fa_ir/strings.po
index 20cc2e37d2..5178c41e33 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.fa_ir/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fa_ir/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Persian (Iran) (https://www.transifex.com/teamxbmc/teams/40581/fa_IR/)\n"
+"Language: fa_IR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fa_IR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fi_fi/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fi_fi/strings.po
index 45b5f919d7..811fc918fc 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.fi_fi/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fi_fi/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Oskari Lavinto <oskari.lavinto@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Oskari Lavinto <oskari.lavinto@gmail.com>, 2020\n"
-"Language-Team: Finnish (Finland) (https://www.transifex.com/teamxbmc/teams/40581/fi_FI/)\n"
+"PO-Revision-Date: 2022-07-07 19:14+0000\n"
+"Last-Translator: Oskari Lavinto <olavinto@protonmail.com>\n"
+"Language-Team: Finnish <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/fi_fi/>\n"
+"Language: fi_fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fi_FI\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.13\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Yleinen musiikkitietolähde esittäjille"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Mahdollistaa esittäjien tietojen ja mediakuvitusten noudon eri verkkopalveluista."
msgctxt "#30000"
msgid "Preferences"
@@ -26,72 +31,56 @@ msgstr "Asetukset"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Ensisijainen kieli esittäjän biografialle"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Suosi biografiaa lähteestä"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Suosi diskografiaa lähteestä"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Suosi lajityyppejä lähteestä"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Suosi tyylejä lähteestä"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Suosi tunnelmia lähteestä"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr "Salli epätarkemmat hakutulokset"
+msgstr "Salli heikommat hakutulokset"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Jos mahdollista, esittäjän biografia noudetaan valitun kielisenä. Muutoin se noudetaan englanninkielisenä."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pyri noutamaan esittäjien biografiat valitusta tietolähteestä. Muita lähteitä käytetään, jollei ensisijainen lähde tuota tuloksia."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pyri noutamaan esittäjien diskografiat valitulla tietolähteellä. Muita lähteitä käytetään, jollei ensisijainen lähde tuota tuloksia."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pyri noutamaan lajityypit valitulla tietolähteellä. Muita lähteitä käytetään, jollei ensisijainen lähde tuota tuloksia."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pyri noutamaan tyylit valitulla tietolähteellä. Muita lähteitä käytetään, jollei ensisijainen lähde tuota tuloksia."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pyri noutamaan tunnelmat valitulla tietolähteellä. Muita lähteitä käytetään, jollei ensisijainen lähde tuota tuloksia."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Jollei MusicBrainz tarjoa linkkiä AllMusic- ja/tai Discogs-palveluun, voidaan sivustoilta etsiä esittäjien nimillä. Koska esittäjien nimet eivät ole ainutlaatuisia, tämä voi aiheuttaa tietojen sekoittumisen saman nimisten esittäjien kesken."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fil/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fil/strings.po
new file mode 100644
index 0000000000..1f64721866
--- /dev/null
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fil/strings.po
@@ -0,0 +1,85 @@
+# Kodi Media Center language file
+# Addon Name: Generic Artist Scraper
+# Addon id: metadata.generic.artists
+# Addon Provider: Team Kodi
+msgid ""
+msgstr ""
+"Project-Id-Version: Kodi add-ons\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: fil\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9);\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
+msgctxt "#30000"
+msgid "Preferences"
+msgstr ""
+
+msgctxt "#30001"
+msgid "Preferred language for artist biography"
+msgstr ""
+
+msgctxt "#30002"
+msgid "Prefer biography from"
+msgstr ""
+
+msgctxt "#30003"
+msgid "Prefer discography from"
+msgstr ""
+
+msgctxt "#30004"
+msgid "Prefer genres from"
+msgstr ""
+
+msgctxt "#30005"
+msgid "Prefer styles from"
+msgstr ""
+
+msgctxt "#30006"
+msgid "Prefer moods from"
+msgstr ""
+
+msgctxt "#30101"
+msgid "Allow less accurate search results"
+msgstr ""
+
+msgctxt "#30201"
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr ""
+
+msgctxt "#30202"
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr ""
+
+msgctxt "#30203"
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr ""
+
+msgctxt "#30204"
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr ""
+
+msgctxt "#30205"
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr ""
+
+msgctxt "#30206"
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr ""
+
+msgctxt "#30301"
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fo_fo/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fo_fo/strings.po
index 8d0d5b3223..7186a4a9e0 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.fo_fo/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fo_fo/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Faroese (Faroe Islands) (https://www.transifex.com/teamxbmc/teams/40581/fo_FO/)\n"
+"Language: fo_FO\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fo_FO\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fr_ca/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fr_ca/strings.po
index e83be30f35..68aafc00c9 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.fr_ca/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fr_ca/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: French (Canada) (https://www.transifex.com/teamxbmc/teams/40581/fr_CA/)\n"
+"Language: fr_CA\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fr_CA\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Préférences"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.fr_fr/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.fr_fr/strings.po
index 453f8bd1b8..abc7771a82 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.fr_fr/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.fr_fr/strings.po
@@ -2,24 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Ptit Prince <leporello1791@gmail.com>, 2020
-# tmtisfree <tmtisfree@free.fr>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: tmtisfree <tmtisfree@free.fr>, 2020\n"
-"Language-Team: French (France) (https://www.transifex.com/teamxbmc/teams/40581/fr_FR/)\n"
+"PO-Revision-Date: 2022-12-21 20:15+0000\n"
+"Last-Translator: skypichat <skypichat@hotmail.fr>\n"
+"Language-Team: French (France) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/fr_fr/>\n"
+"Language: fr_fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fr_FR\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 4.15\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Scraper de musique générique pour les artistes"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Recherche les informations sur les artistes et les artworks sur plusieurs sites Web."
msgctxt "#30000"
msgid "Preferences"
@@ -31,19 +35,19 @@ msgstr "Préférence de langue pour la biographie d'artiste"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr "Récupérer la biographie avec"
+msgstr "Récupérer la biographie depuis"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr "Récupérer la discographie avec"
+msgstr "Récupérer la discographie depuis"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr "Récupérer les genres avec"
+msgstr "Récupérer les genres depuis"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr "Récupérer les styles avec"
+msgstr "Récupérer les styles depuis"
msgctxt "#30006"
msgid "Prefer moods from"
@@ -54,66 +58,29 @@ msgid "Allow less accurate search results"
msgstr "Permettre des résultats de recherche moins précis"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Si disponible, la biographie de l'artiste sera téléchargée dans la langue "
-"sélectionnée. Sinon elle le sera en anglais."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Si disponible, la biographie de l'artiste sera téléchargée dans la langue sélectionnée. Sinon elle le sera en anglais."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Tente de récupérer de récupérer la biographie de l'artiste avec le scraper "
-"sélectionné. D'autres scrapers seront utilisés si le scraper préféré ne "
-"renvoie aucun résultat."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tenter de récupérer de récupérer la biographie de l'artiste avec le collecteur sélectionné. D'autres collecteurs seront utilisés si le collecteur préféré ne renvoie aucun résultat."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Tente de récupérer la discographie de l'artiste avec le scraper sélectionné."
-" D'autres scrapers seront utilisés si le scraper préféré ne renvoie aucun "
-"résultat."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tenter de récupérer la discographie de l'artiste avec le collecteur sélectionné. D'autres collecteurs seront utilisés si le collecteur préféré ne renvoie aucun résultat."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Tente de récupérer l'information de genre avec le scraper sélectionné. "
-"D'autres scrapers seront utilisés si le scraper préféré ne renvoie aucun "
-"résultat."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tenter de récupérer l'information de genre avec le collecteur sélectionné. D'autres collecteurs seront utilisés si le collecteur préféré ne renvoie aucun résultat."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Tente de récupérer l'information de style avec le scraper sélectionné. "
-"D'autres scrapers seront utilisés si le scraper préféré ne renvoie aucun "
-"résultat."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tenter de récupérer l'information de style avec le collecteur sélectionné. D'autres collecteurs seront utilisés si le collecteur préféré ne renvoie aucun résultat."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
-"Tente de récupérer l'information d'ambiance avec le scraper sélectionné. "
-"D'autres scrapers seront utilisés si le scraper préféré ne renvoie aucun "
-"résultat."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tenter de récupérer l'information d'ambiance avec le collecteur sélectionné. D'autres collecteurs seront utilisés si le collecteur préféré ne renvoie aucun résultat."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Si Musicbrainz ne fourni pas de lien vers « allmusic » et/ou « discogs », "
-"Kodi cherchera sur ces sites à partir de « artistname ». Les noms d'artiste "
-"n'étant pas uniques, le résultat pourrait être un mélange des métadonnées de"
-" l'ensemble des artistes ayant le même nom."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Si Musicbrainz ne fourni pas de lien vers « allmusic » et/ou « discogs », Kodi cherchera sur ces sites à partir de « artistname ». Les noms d'artiste n'étant pas uniques, le résultat pourrait être un mélange des métadonnées de l'ensemble des artistes ayant le même nom."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.gl_es/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.gl_es/strings.po
index 1d096b4cf6..ee0a7f660b 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.gl_es/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.gl_es/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Galician (Spain) (https://www.transifex.com/teamxbmc/teams/40581/gl_ES/)\n"
+"PO-Revision-Date: 2021-07-05 21:29+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"Language-Team: Galician (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/gl_es/>\n"
+"Language: gl_es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: gl_ES\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.7.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Preferencias"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Idioma preferido para biografías de autor"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Preferir biografía de"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Preferir discografía de"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Preferir xéneros de"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Preferir estilos de"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Preferir estados de ánimo de"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Permitir resultados menos precisos nas buscas"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Se está dispoñíbel, a biografía do artista descargarase no idioma seleccionado, se non en inglés."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter a biografía do artista empregando o scraper seleccionado. Empregaranse outros scrapers se o preferido non devolve resultados."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter a discografía do artista empregando o scraper seleccionado. Empregaranse outros scrapers se o preferido non devolve resultados."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter información do xénero a través o scraper seleccionado. Empregaranse outros scrapers se o preferido non devolve resultados."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter información do estilo a través o scraper seleccionado. Empregaranse outros scrapers se o preferido non devolve resultados."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter información dos estados de ánimo a través o scraper seleccionado. Empregaranse outros scrapers se o preferido non devolve resultados."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Se Musicbrainz non fornece dunha ligazón a allmusic e/ou discogs, podemos pescudar eses sitios baseados no nome de artista e álbum. Como os nomes de artista non son únicos, isto pode resultar nunha potencial mestura de metadatos dos multiples artista polo mesmo nome."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.he_il/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.he_il/strings.po
index a74c9ec8fa..69fff05359 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.he_il/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.he_il/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Hebrew (Israel) (https://www.transifex.com/teamxbmc/teams/40581/he_IL/)\n"
+"PO-Revision-Date: 2022-03-24 11:13+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"Language-Team: Hebrew (Israel) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/he_il/>\n"
+"Language: he_il\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: he_IL\n"
-"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
+"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && n % 10 == 0) ? 2 : 3));\n"
+"X-Generator: Weblate 4.11.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
msgctxt "#30000"
msgid "Preferences"
@@ -37,60 +43,44 @@ msgstr ""
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "להעדיף סוגים מבין"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "להעדיף סגנונות מבין"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "להעדיף מצבי רוח מבין"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "מאפשר תוצאות חיפוש פחות מדויקות"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "לנסות לקבל את פרטי הסוג באמצעות המחלץ הנבחר. מחלצים אחרים יופעלו אם המחלץ המועדף לא יחזיר תוצאות."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "לנסות לקבל את פרטי הסגנון באמצעות המחלץ הנבחר. מחלצים אחרים יופעלו אם המחלץ המועדף לא יחזיר תוצאות."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "לנסות לקבל את פרטי מצב הרוח באמצעות המחלץ הנבחר. מחלצים אחרים יופעלו אם המחלץ המועדף לא יחזיר תוצאות."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.hi_in/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.hi_in/strings.po
index 5e9cace654..8e4d8da557 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.hi_in/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.hi_in/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Hindi (India) (https://www.transifex.com/teamxbmc/teams/40581/hi_IN/)\n"
+"Language: hi_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: hi_IN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.hr_hr/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.hr_hr/strings.po
index 58d449d90a..a249a2c27d 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.hr_hr/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.hr_hr/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Croatian (Croatia) (https://www.transifex.com/teamxbmc/teams/40581/hr_HR/)\n"
+"PO-Revision-Date: 2023-01-07 12:15+0000\n"
+"Last-Translator: gogogogi <trebelnik2@gmail.com>\n"
+"Language-Team: Croatian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/hr_hr/>\n"
+"Language: hr_hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: hr_HR\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 4.15\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Izvorni glazbeni sakupljač za izvođače"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Pretražuje informacije izvođača i slike omota s mnogo web stranica."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Osobitosti"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Željeni jezik za životopis izvođača"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Željeni životopis s"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Željene diskografija s"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Željeni žanrovi s"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Željeni stil s"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Željeno raspoloženje s"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Dopusti manje točne rezultate pretrage"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Ako je dostupno, životopis izvođača biti će preuzet u odabranom jeziku. U suprtnome preuzet će se na engleskom."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokušaj preuzeti životopis izvođača koristeći odabrani sakupljač. Drugi sakupljači će se koristiti ako zadani sakupljač nema rezultata."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokušaj preuzeti diskografiju izvođača koristeći odabrani sakupljač. Drugi sakupljači će se koristiti ako zadani sakupljač nema rezultata."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokušaj preuzeti informaciju žanra koristeći odabrani sakupljač. Drugi sakupljači će se koristiti ako zadani sakupljač nema rezultata."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokušaj preuzeti informaciju stila koristeći odabrani sakupljač. Drugi sakupljači će se koristiti ako zadani sakupljač nema rezultata."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Pokušaj preuzeti raspoloženje koristeći odabrani sakupljač. Drugi sakupljači će se koristiti ako zadani sakupljač nema rezultata."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Ako Musicbrainz ne pruža poveznicu do allmusic i/ili discogs, možemo pretražiti te stranice temeljeno na nazivu izvođača. Budući da nazivi izvođača nisu jedinstveni, to potencijalno može rezultirati mješavinom metapodataka od više izvođača istog naziva."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.hu_hu/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.hu_hu/strings.po
index f8b639cd07..28debd1a84 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.hu_hu/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.hu_hu/strings.po
@@ -2,31 +2,35 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Balázs Meskó <meskobalazs@mailbox.org>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Balázs Meskó <meskobalazs@mailbox.org>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Hungarian (Hungary) (https://www.transifex.com/teamxbmc/teams/40581/hu_HU/)\n"
+"Language: hu_HU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: hu_HU\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Beállítások"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Előnyben részesített nyelv az előadó életrajzához"
msgctxt "#30002"
msgid "Prefer biography from"
@@ -53,47 +57,29 @@ msgid "Allow less accurate search results"
msgstr "Kevésbé pontos keresési találatok engedélyezése"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Ha elérhető, az előadó életrajza a kiválasztott nyelven lesz letöltve. "
-"Egyébként angolra fog váltani."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Ha elérhető, az előadó életrajza a kiválasztott nyelven lesz letöltve. Egyébként angolra fog váltani."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Próbálja meg megszerezni az előadó életrajzát a kiválasztott leolvasóval. Más leolvasókat használunk, ha az előnyben részesített nem ad eredményt."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Próbálja meg megszerezni az előadó diszkográfiáját a kiválasztott leolvasóval. Más leolvasókat használunk, ha az előnyben részesített nem ad eredményt."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Próbáljon műfaji információkat szerezni a kiválasztott leolvasó segítségével. Más leolvasókat használunk, ha az előnyben részesített nem ad eredményt."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Próbáljon stílusinformációkat szerezni a kiválasztott leolvasóval. Más leolvasókat használunk, ha az előnyben részesített nem ad eredményt."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Próbáljon hangulatinformációkat szerezni a kiválasztott leolvasóval. Más leolvasókat használunk, ha az előnyben részesített nem ad eredményt."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Ha a Musicbrainz nem nyújt hivatkozást az allmusicra vagy a discogsra, akkor az előadónév és az oszlopnév alapján kereshetünk ezeken a webhelyeken. Mivel az előadónevek nem egyediek, ez potenciálisan az albumok metaadatainak keverékét eredményezheti."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.hy_am/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.hy_am/strings.po
index 313cd97bf5..86e1b0d42b 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.hy_am/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.hy_am/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Armenian (Armenia) (https://www.transifex.com/teamxbmc/teams/40581/hy_AM/)\n"
+"Language: hy_AM\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: hy_AM\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.id_id/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.id_id/strings.po
index d7e293f226..067fc8912f 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.id_id/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.id_id/strings.po
@@ -5,88 +5,82 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Language-Team: Indonesian (Indonesia) (https://www.transifex.com/teamxbmc/teams/40581/id_ID/)\n"
+"PO-Revision-Date: 2021-11-18 22:13+0000\n"
+"Last-Translator: Nao3Line Prez <n.yazawa6932@gmail.com>\n"
+"Language-Team: Indonesian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/id_id/>\n"
+"Language: id_id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: id_ID\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 4.9\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Scraper musik generik untuk artis"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Mencari informasi artis dan karya seni di beberapa situs web."
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "Preferensi"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Bahasa pilihan untuk biografi artis"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Lebih suka biografi dari"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Lebih suka diskografi dari"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Lebih suka genre dari"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Lebih suka gaya dari"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Lebih suka suasana dari"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Ijinkan hasil pencarian keakurasi rendah"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Jika tersedia, biografi artis akan diunduh dalam bahasa yang dipilih. Ini akan dibalikkan ke bahasa Inggris."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Cobalah untuk mendapatkan biografi artis menggunakan scraper yang dipilih. Scraper lain akan digunakan jika scraper pilihan tidak memberikan hasil."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Cobalah untuk mendapatkan diskografi artis menggunakan scraper yang dipilih. Scraper lain akan digunakan jika scraper pilihan tidak memberikan hasil."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Cobalah untuk mendapatkan info genre menggunakan scraper yang dipilih. Scraper lain akan digunakan jika scraper pilihan tidak memberikan hasil."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Cobalah untuk mendapatkan info gaya menggunakan scraper yang dipilih. Scraper lain akan digunakan jika scraper pilihan tidak memberikan hasil."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Cobalah untuk mendapatkan info suasana menggunakan scraper yang dipilih. Scraper lain akan digunakan jika scraper pilihan tidak memberikan hasil."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Jika Musicbrainz tidak menyediakan tautan ke semua musik dan/atau diskog, kami dapat mencari situs tersebut berdasarkan nama artis. Karena nama artis tidak unik, hal ini berpotensi menghasilkan campuran metadata dari beberapa artis dengan nama yang sama."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.is_is/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.is_is/strings.po
index 99ff855189..8b3ed26b93 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.is_is/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.is_is/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Icelandic (Iceland) (https://www.transifex.com/teamxbmc/teams/40581/is_IS/)\n"
+"PO-Revision-Date: 2022-02-17 15:13+0000\n"
+"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
+"Language-Team: Icelandic <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/is_is/>\n"
+"Language: is_is\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: is_IS\n"
-"Plural-Forms: nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);\n"
+"Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n"
+"X-Generator: Weblate 4.10.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Almennur tónlistarskrapari fyrir flytjendur"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Leitar að upplýsingum um flytjendur og myndefni þeim tengt á mörgum vefsvæðum."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Kjörstillingar"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Æskilegt tungumál æviágrips listamanns"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Velja frekar æviágrip frá"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Velja frekar plötulista frá"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Velja frekar flokka frá"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Velja frekar stíla frá"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Velja frekar skapbrigði frá"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Leyfa minna nákvæmar leitarniðurstöður"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Ef slíkt er tiltækt, verður æviágrip flytjenda sótt á völdu tungumáli. Til vara verður stuðst við ensku."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Reyna að fá æviágrip listamanns með völdum skrapara. Aðrir skraparar verða notaðir ef sá valdi skilar engum niðurstöðum."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Reyna að fá plötulista listamanns með völdum skrapara. Aðrir skraparar verða notaðir ef sá valdi skilar engum niðurstöðum."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Reyna að fá upplýsingar um flokka með völdum skrapara. Aðrir skraparar verða notaðir ef sá valdi skilar engum niðurstöðum."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Reyna að fá upplýsingar um stíl með völdum skrapara. Aðrir skraparar verða notaðir ef sá valdi skilar engum niðurstöðum."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Reyna að fá upplýsingar um skapbrigði með völdum skrapara. Aðrir skraparar verða notaðir ef sá valdi skilar engum niðurstöðum."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Ef Musicbrainz gefur ekki tengil á allmusic og/eða discogs, getum við leitað á þessum vefsvæðum eftir listamannsnafni. Sé nafn flytjanda ekki einstakt, getur þetta leitt til blöndunar á lýsigögnum frá mörgum flytjendum með sama nafni."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.it_it/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.it_it/strings.po
index b155928813..0787e58149 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.it_it/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.it_it/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Marco Viti <marcoviti@alice.it>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Marco Viti <marcoviti@alice.it>, 2020\n"
-"Language-Team: Italian (Italy) (https://www.transifex.com/teamxbmc/teams/40581/it_IT/)\n"
+"PO-Revision-Date: 2023-03-22 21:16+0000\n"
+"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
+"Language-Team: Italian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/it_it/>\n"
+"Language: it_it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: it_IT\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.15.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Scraper musicale generico per artisti"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Cerca informazioni sull'artista e artwork su più siti web."
msgctxt "#30000"
msgid "Preferences"
@@ -34,81 +39,48 @@ msgstr "Preferisci biografia da"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr "Preferisco la discografia da"
+msgstr "Preferisco discografia da"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr "Preferisci i generi da"
+msgstr "Preferisci generi da"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr "Preferisci gli stili da"
+msgstr "Preferisci stili da"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Preferisci stati d'animo da"
msgctxt "#30101"
msgid "Allow less accurate search results"
msgstr "Consenti risultati di ricerca meno accurati"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Se disponibile, la biografia dell'artista verrà scaricata nella lingua "
-"selezionata. Poi tornerà all'inglese."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Se disponibile, la biografia dell'artista verrà scaricata nella lingua selezionata. Altrimenti verrà scaricata in inglese."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Prova a ottenere la biografia dell'artista utilizzando lo scraper "
-"selezionato. Se lo scraper preferito non restituisce risultati, prova con "
-"altri scraper."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Prova ad ottenere la biografia dell'artista utilizzando lo scraper selezionato. Verranno utilizzati altri scraper se lo scraper preferito non dovesse restituire risultati."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Cerca di ottenere la discografia dell'artista utilizzando lo scraper "
-"selezionato. Se lo scraper preferito non restituisce risultati, prova con "
-"altri scraper."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Prova ad ottenere la discografia dell'artista utilizzando lo scraper selezionato. Verranno utilizzati altri scraper se lo scraper preferito non dovesse restituire risultati."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Prova a ottenere informazioni sul genere utilizzando lo scraper selezionato."
-" Se lo scraper preferito non restituisce risultati, utilizza altri scraper."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Prova ad ottenere informazioni sul genere utilizzando lo scraper selezionato. Verranno utilizzati altri scraper se lo scraper preferito non dovesse restituire risultati."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Prova a ottenere informazioni sullo stile utilizzando lo scraper "
-"selezionato. Se lo scraper preferito non restituisce risultati, prova con "
-"altri scraper."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Prova ad ottenere informazioni sullo stile utilizzando lo scraper selezionato. Verranno utilizzati altri scraper se lo scraper preferito non dovesse restituire risultati."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Prova ad ottenere informazioni sull'umore utilizzando lo scraper selezionato. Verranno utilizzati altri scraper se lo scraper preferito non dovesse restituire risultati."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Se Musicbrainz non fornisce un collegamento a allmusic e/o discogs, possiamo"
-" cercare quei siti in base al nome dell'artista e all'album. In circostanze "
-"in cui un artista ha pubblicato più album con lo stesso nome, ciò potrebbe "
-"potenzialmente risultare in un misto di metadati di quegli album."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Se Musicbrainz non fornisce un collegamento a allmusic e/o discogs, possiamo cercare quei siti in base al nome dell'artista e al nome dell'album. Nel caso in cui un artista avesse pubblicato più album con lo stesso nome, si potrebbe potenzialmente creare una combinazione di metadati di quegli album."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ja_jp/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ja_jp/strings.po
index c94cf5f375..2bd492dad2 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ja_jp/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ja_jp/strings.po
@@ -5,88 +5,82 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Language-Team: Japanese (Japan) (https://www.transifex.com/teamxbmc/teams/40581/ja_JP/)\n"
+"PO-Revision-Date: 2023-07-23 15:11+0000\n"
+"Last-Translator: yohru <yohru7@gmail.com>\n"
+"Language-Team: Japanese <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/ja_jp/>\n"
+"Language: ja_jp\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ja_JP\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 4.18.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "環境設定"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "アーティストの経歴レビューで優先する言語"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "~からの人物紹介を優先する"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "~からのディスコグラフィを優先する"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "~からのジャンルを優先する"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "~からのスタイルを優先する"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "~からのムードを優先する"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "低い精度の検索結果を許可する"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.kn_in/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.kn_in/strings.po
index d6c8524206..da1b322a2e 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.kn_in/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.kn_in/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Kannada (India) (https://www.transifex.com/teamxbmc/teams/40581/kn_IN/)\n"
+"Language: kn_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: kn_IN\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ko_kr/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ko_kr/strings.po
index aee9e7f4f7..5b138763a4 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ko_kr/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ko_kr/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Minho Park <parkmino@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Minho Park <parkmino@gmail.com>, 2020\n"
-"Language-Team: Korean (Korea) (https://www.transifex.com/teamxbmc/teams/40581/ko_KR/)\n"
+"PO-Revision-Date: 2021-08-21 01:29+0000\n"
+"Last-Translator: Minho Park <parkmino@gmail.com>\n"
+"Language-Team: Korean <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/ko_kr/>\n"
+"Language: ko_kr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ko_KR\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 4.7.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "아티스트를 위한 일반 음악 스크레이퍼"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "여러 웹사이트에서 아티스트 정보와 작품을 검색합니다."
msgctxt "#30000"
msgid "Preferences"
@@ -53,47 +58,29 @@ msgid "Allow less accurate search results"
msgstr "약간 느슨한 검색 결과 허용"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr "가능하면 아티스트 생애를 선호 언어로 내려 받습니다. 기본 영어."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "선택된 스크레이퍼로 아티스트 생애를 내려 받습니다. 선호 스크레이퍼의 결과가 없으면 다른 것이 사용됩니다."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "선택된 스크레이퍼로 아티스트 디스코그래피를 내려 받습니다. 선호 스크레이퍼의 결과가 없으면 다른 것이 사용됩니다."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "선택된 스크레이퍼로 장르 정보를 내려받습니다. 선호하는 스크레이퍼의 결과가 없으면 다른 것이 쓰입니다."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "선택된 스크레이퍼로 스타일 정보를 내려받습니다. 선호하는 스크레이퍼의 결과가 없으면 다른 것이 쓰입니다."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "선택된 스크레이퍼로 무드 정보를 내려받습니다. 선호하는 스크레이퍼의 결과가 없으면 다른 것이 쓰입니다."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"뮤직브레인즈이 allmusic이나 discogs의 링크를 제공하지 않으면, albumname으로 검색할 수 있습니다. 아티스트 이름이 "
-"유일하지 않으므로, 이 음반들의 메타데이터로부터 뒤섞인 결과가 나올 수 있습니다."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "뮤직브레인즈이 allmusic이나 discogs의 링크를 제공하지 않으면, albumname으로 검색할 수 있습니다. 아티스트 이름이 유일하지 않으므로, 이 음반들의 메타데이터로부터 뒤섞인 결과가 나올 수 있습니다."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.lt_lt/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.lt_lt/strings.po
index 38b04e9c5c..f2d0b064b0 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.lt_lt/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.lt_lt/strings.po
@@ -2,24 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Zapata11 <raimondas.duzinskas@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Zapata11 <raimondas.duzinskas@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Lithuanian (Lithuania) (https://www.transifex.com/teamxbmc/teams/40581/lt_LT/)\n"
+"Language: lt_LT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: lt_LT\n"
"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Nustatymai"
@@ -53,62 +57,29 @@ msgid "Allow less accurate search results"
msgstr "Leisti mažiau tikslius paieškos rezultatus"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Jei įmanoma, atlikėjo biografija bus atsisiųsta pasirinkta kalba. Tai bus "
-"anglų kalba. Nepavykus bus atsiųsta angliškai."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Jei įmanoma, atlikėjo biografija bus atsisiųsta pasirinkta kalba. Tai bus anglų kalba. Nepavykus bus atsiųsta angliškai."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Bus bandoma gauti atlikėjo biografiją naudojant pasirinktą graibyklę. Kitos "
-"graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Bus bandoma gauti atlikėjo biografiją naudojant pasirinktą graibyklę. Kitos graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Bus bandoma gauti atlikėjo diskografiją naudojant pasirinktą graibyklę. "
-"Kitos graibyklės bus naudojamos, jei pageidaujama graibyklė neduos "
-"rezultatų."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Bus bandoma gauti atlikėjo diskografiją naudojant pasirinktą graibyklę. Kitos graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Bus bandoma gauti žanrų informaciją naudojant pasirinktą graibyklę. Kitos "
-"graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Bus bandoma gauti žanrų informaciją naudojant pasirinktą graibyklę. Kitos graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Bus bandoma gauti stilių informaciją naudojant pasirinktą graibyklę. Kitos "
-"graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Bus bandoma gauti stilių informaciją naudojant pasirinktą graibyklę. Kitos graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
-"Bus bandoma gauti nuotaikų informaciją naudojant pasirinktą graibyklę. Kitos"
-" graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Bus bandoma gauti nuotaikų informaciją naudojant pasirinktą graibyklę. Kitos graibyklės bus naudojamos, jei pageidaujama graibyklė neduos rezultatų."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Jei Musicbrainz nepateikia nuorodos į „allmusic“ ir/arba \"discogs\", šiose "
-"svetainėse galime atlikti paiešką pagal atlikėjo vardą. Kadangi atlikėjų "
-"vardai nėra unikalūs, rezultate galite gauti kelių atlikėjų tuo pačiu vardu "
-"metaduomenų mišinį."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Jei Musicbrainz nepateikia nuorodos į „allmusic“ ir/arba \"discogs\", šiose svetainėse galime atlikti paiešką pagal atlikėjo vardą. Kadangi atlikėjų vardai nėra unikalūs, rezultate galite gauti kelių atlikėjų tuo pačiu vardu metaduomenų mišinį."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.lv_lv/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.lv_lv/strings.po
index 8bede0c7b5..62720ffa36 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.lv_lv/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.lv_lv/strings.po
@@ -5,88 +5,82 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Language-Team: Latvian (Latvia) (https://www.transifex.com/teamxbmc/teams/40581/lv_LV/)\n"
+"PO-Revision-Date: 2023-02-20 20:15+0000\n"
+"Last-Translator: Coool (github.com/Coool) <coool@mail.lv>\n"
+"Language-Team: Latvian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/lv_lv/>\n"
+"Language: lv_lv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: lv_LV\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
+"X-Generator: Weblate 4.15.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Vispārējs mūzikas skrāpis, kas iegūst informāciju par mūzikas izpildītājiem"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Meklē informāciju par izpildītāju un iegūst to attēlu no dažādām vietnēm."
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "Iestatījumi"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Izpildītāja biogrāfijas vēlamā valoda"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Dot priekšroku iegūt biogrāfijas datus no"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Dot priekšroku iegūt diskogrāfijas datus no"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Dot priekšroku iegūt žanru datus no"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Dot priekšroku iegūt stilu datus no"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Dot priekšroku iegūt noskaņu datus no"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Pieļaut mazāk precīza datu iegūšanu"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Ja ir pieejami, izpildītāja biogrāfijas dati tiks iegūti izvēlētajā valodā. Ja nav, angļu valodā."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mēģināt iegūt izpildītāja biogrāfiju, izmantojot izvēlēto skrāpi. Ja izvēlētais skrāpis nevarēs iegūt datus, tiks izmantoti citi skrāpji."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mēģināt iegūt izpildītāja diskogrāfiju, izmantojot izvēlēto skrāpi. Ja izvēlētais skrāpis nevarēs iegūt datus, tiks izmantoti citi skrāpji."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mēģināt iegūt žanru informāciju, izmantojot izvēlēto skrāpi. Ja izvēlētais skrāpis nevarēs iegūt datus, tiks izmantoti citi skrāpji."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mēģināt iegūt stila informāciju, izmantojot izvēlēto skrāpi. Ja izvēlētais skrāpis nevarēs iegūt datus, tiks izmantoti citi skrāpji."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mēģināt iegūt noskaņojuma informāciju, izmantojot izvēlēto skrāpi. Ja izvēlētais skrāpis nevarēs iegūt datus, tiks izmantoti citi skrāpji."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Ja MusicBrainz nesatur saiti uz allmusic un/vai discogs vietni, mēs varam iegūt datus tajās pēc izpildītāja vārda. Tā kā izpildītāju vārdi nav unikāli, iespējams, var tikt iegūti nepareizi dati. Jo pastāv iespēja, ka eksistē vairāki mākslinieki ar vienu un to pašu vārdu."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.mi/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.mi/strings.po
index cfd40ec894..128afc76b6 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.mi/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.mi/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Maori (https://www.transifex.com/teamxbmc/teams/40581/mi/)\n"
+"Language: mi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: mi\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.mk_mk/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.mk_mk/strings.po
index 5a69b49fdd..6831d4daae 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.mk_mk/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.mk_mk/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Macedonian (Macedonia) (https://www.transifex.com/teamxbmc/teams/40581/mk_MK/)\n"
+"Language: mk_MK\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: mk_MK\n"
"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ml_in/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ml_in/strings.po
index e9165d2931..1628e4908c 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ml_in/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ml_in/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Malayalam (India) (https://www.transifex.com/teamxbmc/teams/40581/ml_IN/)\n"
+"Language: ml_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ml_IN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.mn_mn/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.mn_mn/strings.po
index 7dcfbfbdee..86e7f752b2 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.mn_mn/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.mn_mn/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Mongolian (Mongolia) (https://www.transifex.com/teamxbmc/teams/40581/mn_MN/)\n"
+"Language: mn_MN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: mn_MN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ms_my/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ms_my/strings.po
index f83434fa61..9c0e027c6b 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ms_my/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ms_my/strings.po
@@ -2,24 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# abuyop <abuyop@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: abuyop <abuyop@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Malay (Malaysia) (https://www.transifex.com/teamxbmc/teams/40581/ms_MY/)\n"
+"Language: ms_MY\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ms_MY\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Keutamaan"
@@ -53,47 +57,29 @@ msgid "Allow less accurate search results"
msgstr "Benarkan keputusan gelintar kurang tepat"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Jika tersedia, biografi artis akan dimuat turun dalam bahasa terpilih. Ia "
-"akan dijatuh-balik ke bahasa Inggeris."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Jika tersedia, biografi artis akan dimuat turun dalam bahasa terpilih. Ia akan dijatuh-balik ke bahasa Inggeris."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.mt_mt/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.mt_mt/strings.po
index 3b490ff0dd..0ac1f451ed 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.mt_mt/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.mt_mt/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Maltese (Malta) (https://www.transifex.com/teamxbmc/teams/40581/mt_MT/)\n"
+"Language: mt_MT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: mt_MT\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.my_mm/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.my_mm/strings.po
index b85a248490..3b99a9234b 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.my_mm/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.my_mm/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Burmese (Myanmar) (https://www.transifex.com/teamxbmc/teams/40581/my_MM/)\n"
+"Language: my_MM\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: my_MM\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.nb_no/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.nb_no/strings.po
index 8c9c194479..e8e6d976b6 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.nb_no/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.nb_no/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Norwegian Bokmål (Norway) (https://www.transifex.com/teamxbmc/teams/40581/nb_NO/)\n"
+"Language: nb_NO\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: nb_NO\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Innstillinger"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.nl_nl/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.nl_nl/strings.po
index 6bdfd1d0e2..ddde3d86b9 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.nl_nl/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.nl_nl/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Dutch (Netherlands) (https://www.transifex.com/teamxbmc/teams/40581/nl_NL/)\n"
+"PO-Revision-Date: 2022-09-06 10:59+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"Language-Team: Dutch <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/nl_nl/>\n"
+"Language: nl_nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: nl_NL\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.14\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Generieke muziek schraper voor artiesten"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Zoekt naar informatie over artiesten en kunstwerken op meerdere websites."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Voorkeuren"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Voorkeurstaal voor artiestenbiografie"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Liever biografie van"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Liever discografie van"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Voorkeur voor genres van"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Voorkeur voor stijlen van"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Liever stemmingen van"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Minder nauwkeurige zoekresultaten toestaan"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Indien beschikbaar, wordt de artiestenbiografie gedownload in de geselecteerde taal. Het zal terugvallen op het Engels."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer de biografie van de artiest te krijgen met de geselecteerde schraper. Andere schrapers worden gebruikt als de gewenste schraper geen resultaten oplevert."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer de biografie van de artiest te krijgen met de geselecteerde schraper. Andere schrapers worden gebruikt als de gewenste schraper geen resultaten oplevert."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer genre-informatie te krijgen met behulp van de geselecteerde schraper. Andere schrapers worden gebruikt als de gewenste schraper geen resultaten oplevert."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer stijlinformatie te krijgen met de geselecteerde schraper. Andere schrapers worden gebruikt als de gewenste schraper geen resultaten oplevert."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Probeer stemmingsinformatie te krijgen met de geselecteerde schraper. Andere schrapers worden gebruikt als de gewenste schraper geen resultaten oplevert."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Als Musicbrainz geen link geeft naar alle muziek en/of discogs, kunnen we die sites doorzoeken op artiestnaam. Aangezien artiestennamen niet uniek zijn, kan dit mogelijk resulteren in een mengeling van metadata van meerdere artiesten met dezelfde naam."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.pl_pl/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.pl_pl/strings.po
index c2615a32e0..d671ba3b78 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.pl_pl/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.pl_pl/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Valdnet, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Valdnet, 2020\n"
-"Language-Team: Polish (Poland) (https://www.transifex.com/teamxbmc/teams/40581/pl_PL/)\n"
+"PO-Revision-Date: 2021-07-31 13:29+0000\n"
+"Last-Translator: Marek Adamski <fevbew@wp.pl>\n"
+"Language-Team: Polish <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/pl_pl/>\n"
+"Language: pl_pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: pl_PL\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
+"X-Generator: Weblate 4.7.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Ogólny ekstraktor muzyczny wykonawców"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Wyszukuje informacje o wykonawcy oraz grafikę na wielu witrynach internetowych."
msgctxt "#30000"
msgid "Preferences"
@@ -26,7 +31,7 @@ msgstr "Preferencje"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Preferowany język biografii wykonawcy"
msgctxt "#30002"
msgid "Prefer biography from"
@@ -53,47 +58,29 @@ msgid "Allow less accurate search results"
msgstr "Zezwalaj na mniej dokładne wyniki wyszukiwania"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Jeśli jest dostępna, biografia artysty zostanie pobrana w wybranym języku. "
-"Można zawsze powrócić do języka angielskiego."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Jeśli jest dostępna, biografia wykonawcy zostanie pobrana w wybranym języku. Można zawsze powrócić do języka angielskiego."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Spróbuj uzyskać biografię wykonawcy za pomocą wybranego ekstraktora. Inne ekstraktory zostaną użyte, jeśli preferowany ekstraktor nie poda rezultatów."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Spróbuj uzyskać dyskografię wykonawcy za pomocą wybranego ekstraktora. Inne ekstraktory zostaną użyte, jeśli preferowany ekstraktor nie poda rezultatów."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Spróbuj uzyskać informacje o gatunku za pomocą wybranego ekstraktora. Inne ekstraktory zostaną użyte, jeśli preferowany ekstraktor nie poda rezultatów."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Spróbuj uzyskać informacje o stylu za pomocą wybranego ekstraktora. Inne ekstraktory zostaną użyte, jeśli preferowany ekstraktor nie poda rezultatów."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Spróbuj uzyskać informacje o nastroju za pomocą wybranego ekstraktora. Inne ekstraktory zostaną użyte, jeśli preferowany ekstraktor nie poda rezultatów."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Jeśli Musicbrainz nie udostępnia łącza do allmusic i/lub discogs, możemy przeszukać te strony na podstawie nazwy wykonawcy. Ponieważ nazwy wykonawców nie są unikalne, może to potencjalnie skutkować mieszanką metadanych pochodzących od wielu wykonawców o tej samej nazwie."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.pt_br/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.pt_br/strings.po
index 719ce43160..8d544b9983 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.pt_br/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.pt_br/strings.po
@@ -2,25 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Lucas Mindêllo de Andrade <lucas@mindello.com.br>, 2020
-# Lizandra Silva <lizandra.c.s@gmail.com>, 2020
-# Igor Rückert, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: Igor Rückert, 2020\n"
-"Language-Team: Portuguese (Brazil) (https://www.transifex.com/teamxbmc/teams/40581/pt_BR/)\n"
+"PO-Revision-Date: 2021-12-02 00:13+0000\n"
+"Last-Translator: Fabio <fabioihle+kodi@alunos.utfpr.edu.br>\n"
+"Language-Team: Portuguese (Brazil) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/pt_br/>\n"
+"Language: pt_br\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: pt_BR\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 4.9.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Scraper genérico de música para artistas"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Pesquisa por artes e informações de artistas em múltiplos sites."
msgctxt "#30000"
msgid "Preferences"
@@ -52,64 +55,32 @@ msgstr "Preferir humores de"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr "Permitir resultados de pesquisa menos precisos"
+msgstr "Permitir resultados de busca menos precisos"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Se disponível, a biografia do artista será baixada no idioma selecionado. "
-"Caso contrário, será baixada em inglês."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Se disponível, a biografia do artista será baixada no idioma selecionado. Caso contrário, será baixada em inglês."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
-"Tentar obter a biografia do artista usando o scraper selecionado. Os demais "
-"scrapers serão utilizados se o prioritário não trouxer resultados."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter a biografia do artista usando o scraper selecionado. Os demais scrapers serão utilizados se o prioritário não trouxer resultados."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
-"Tentar obter a discografia do artista usando o scraper selecionado. Os "
-"demais scrapers serão utilizados se o prioritário não trouxer resultados."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter a discografia do artista usando o scraper selecionado. Os demais scrapers serão utilizados se o prioritário não trouxer resultados."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Tentar obter as informações de gênero usando o scraper selecionado. Os "
-"demais scrapers serão utilizados se o prioritário não trouxer resultados."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter as informações de gênero usando o scraper selecionado. Os demais scrapers serão utilizados se o prioritário não trouxer resultados."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
-"Tentar obter informações de estilo usando o scraper selecionado. Os demais "
-"scrapers serão utilizados se o prioritário não trouxer resultados."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter informações de estilo usando o scraper selecionado. Os demais scrapers serão utilizados se o prioritário não trouxer resultados."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
-"Tentar obter as informações de humor usando o scraper selecionado. Os demais"
-" scrapers serão utilizados se o prioritário não trouxer resultados."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Tentar obter as informações de humor usando o scraper selecionado. Os demais scrapers serão utilizados se o prioritário não trouxer resultados."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Se o MusicBrainz não fornecer um link para allmusic e/ou discogs, podemos "
-"pesquisar nesses sites pelo nome do artista. Como os nomes dos artistas não "
-"são exclusivos, isto pode resultar em uma mistura de metadados de vários "
-"artistas com o mesmo nome."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Se o MusicBrainz não fornecer um link para allmusic e/ou discogs, podemos pesquisar nesses sites pelo nome do artista. Como os nomes dos artistas não são exclusivos, isto pode resultar em uma mistura de metadados de vários artistas com o mesmo nome."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.pt_pt/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.pt_pt/strings.po
index f254d539ae..794e48a459 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.pt_pt/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.pt_pt/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Portuguese (Portugal) (https://www.transifex.com/teamxbmc/teams/40581/pt_PT/)\n"
+"Language: pt_PT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: pt_PT\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Preferências"
@@ -37,60 +42,44 @@ msgstr ""
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Géneros preferidos de"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Estilos preferidos de"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Preferência de humor"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Permitir resultados menos precisos"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ro_ro/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ro_ro/strings.po
index 74b063759f..39666c1f0d 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ro_ro/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ro_ro/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Romanian (Romania) (https://www.transifex.com/teamxbmc/teams/40581/ro_RO/)\n"
+"Language: ro_RO\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ro_RO\n"
"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Preferințe"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ru_ru/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ru_ru/strings.po
index a30c7a681f..ec78745c93 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ru_ru/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ru_ru/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Russian (Russia) (https://www.transifex.com/teamxbmc/teams/40581/ru_RU/)\n"
+"PO-Revision-Date: 2021-07-07 15:29+0000\n"
+"Last-Translator: vdkbsd <valexgus@gmail.com>\n"
+"Language-Team: Russian <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/ru_ru/>\n"
+"Language: ru_ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ru_RU\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+"X-Generator: Weblate 4.7.1\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Сканер для поиска информации об исполнителях музыки"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Поиск информации об исполнителе и иллюстраций на различных веб-сайтах."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Предпочтения"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Предпочтительный язык для биографии исполнителя"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Предпочитать биографию из"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Предпочитать дискографию из"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Предпочитать жанры из"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Предпочитать стили из"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Предпочитать настроения из"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Разрешить получать менее точные результаты поиска"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Если доступно, биография исполнителя будет загружена на выбранном языке. Иначе – на английском языке."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Попытка получить биографию исполнителя с помощью выбранного сканера. Другие сканеры будут использоваться, если выбранный не даст результатов."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Попытка получить дискографию исполнителя с помощью выбранного сканера. Другие сканеры будут использоваться, если выбранный не даст результатов."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Попытка получить информацию о жанрах с помощью выбранного сканера. Другие сканеры будут использоваться, если выбранный не даст результатов."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Попытка получить информацию о стиле с помощью выбранного сканера. Другие сканеры будут использоваться, если выбранный не даст результатов."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Попытка получить информацию о настроении с помощью выбранного сканера. Другие сканеры будут использоваться, если выбранный не даст результатов."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Если Musicbrainz не предоставляет ссылку на allmusic и/или discogs, мы можем выполнить поиск на этих сайтах по имени исполнителя. Поскольку имена исполнителей не являются уникальными, это может привести к смешению метаданных нескольких исполнителей с одинаковыми именами."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.si_lk/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.si_lk/strings.po
index 19e8beeb48..60d6e6683b 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.si_lk/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.si_lk/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Sinhala (Sri Lanka) (https://www.transifex.com/teamxbmc/teams/40581/si_LK/)\n"
+"Language: si_LK\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: si_LK\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.sk_sk/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.sk_sk/strings.po
index 240efba87e..5d204fe2c7 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.sk_sk/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.sk_sk/strings.po
@@ -2,95 +2,84 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Slovak (Slovakia) (https://www.transifex.com/teamxbmc/teams/40581/sk_SK/)\n"
+"Language: sk_SK\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: sk_SK\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Preferencie"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Preferovaný jazyk pre biografiu interpreta"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Preferovať biografiu z"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Preferovať diskografiu z"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Preferovať žánre z"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Preferovať štýly z"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Preferovať nálady z"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Povoliť menej presné výsledky vyhľadávania"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Pokiaľ sa dá, bude biografia interpreta stiahnutá vo vybranom jazyku. Pokiaľ nie, stiahne sa v Angličtine."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Skúsi získať biografiu interpreta z vybraného scrapera. Ostatné scrapery budú použité až vtedy, pokiaľ preferovaný scraper nič nenájde."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Skúsi získať diskografiu interpreta z vybraného scrapera. Ostatné scrapery budú použité až vtedy, pokiaľ preferovaný scraper nič nenájde."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Skúsi získať informácie o žánri z vybraných scraperov. Ostatné scrapery budú použité až vtedy, pokiaľ preferovaný scraper nič nenájde."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Skúsi získať informácie o štýle z vybraných scraperov. Ostatné scrapery budú použité až vtedy, pokiaľ preferovaný scraper nič nenájde."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Skúsi získať informácie o nálade z vybraných scraperov. Ostatné scrapery budú použité až vtedy, pokiaľ preferovaný scraper nič nenájde."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Pokiaľ Muzicbrainz neposkytne odkaz na allmusic a/alebo discogs, môžeme vyhľadať tieto stránky na základe artistname. Keďže mená interpretov nie sú unikátne, toto môže potenciálne vyústiť v zmiešaní metadát rôznych interpretov pod tým istým menom."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.sl_si/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.sl_si/strings.po
index df055de848..82233b9665 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.sl_si/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.sl_si/strings.po
@@ -5,19 +5,28 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Slovenian (Slovenia) (https://www.transifex.com/teamxbmc/teams/40581/sl_SI/)\n"
+"Language: sl_SI\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: sl_SI\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "Prednostne Nastavitve"
msgctxt "#30001"
msgid "Preferred language for artist biography"
@@ -48,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.sq_al/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.sq_al/strings.po
index 33ff099842..71436f5331 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.sq_al/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.sq_al/strings.po
@@ -5,88 +5,81 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Albanian (Albania) (https://www.transifex.com/teamxbmc/teams/40581/sq_AL/)\n"
+"Language: sq_AL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: sq_AL\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
-msgstr ""
+msgstr "Preferenca"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Gjuha e preferuar për biografinë e artistit"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Biografi Preferuar nga"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Preferoni diskografinë nga"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Preferoni zhanret nga"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Preferoni stilet nga"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Preferoni gjendjet shpirtërore nga"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Lejoni rezultate më pak të sakta të kërkimit"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Nëse është e disponueshme, biografia e artistit do të shkarkohet në gjuhën e zgjedhur. Do të bjerë përsëri në anglisht."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mundohuni të merrni biografinë e artistit duke përdorur kruajtësin e zgjedhur. Kruajtës të tjerë do të përdoren nëse kruajtësi i preferuar nuk jep rezultate."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mundohuni të merrni diskografinë e artistit duke përdorur kruajtësin e zgjedhur. Kruajtës të tjerë do të përdoren nëse kruajtësi i preferuar nuk jep rezultate."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mundohuni të merrni informacione të zhanrit duke përdorur kruajtësin e zgjedhur. Kruajtës të tjerë do të përdoren nëse kruajtësi i preferuar nuk jep rezultate."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mundohuni të merrni informacione për stilin duke përdorur kruajtësin e zgjedhur. Kruajtës të tjerë do të përdoren nëse kruajtësi i preferuar nuk jep rezultate."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Mundohuni të merrni informacione për disponimin duke përdorur kruajtësin e zgjedhur. Kruajtës të tjerë do të përdoren nëse kruajtësi i preferuar nuk jep rezultate."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Nëse Musicbrainz nuk ofron një lidhje me almuzikën dhe/ose diskotekat, ne mund të kërkojmë ato faqe bazuar në emrin e artistit. Meqenëse emrat e artistëve nuk janë unikë, kjo mund të rezultojë në një përzierje të meta të dhënave nga shumë artistë me të njëjtin emër."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.sr_rs/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.sr_rs/strings.po
index 626689a011..d1b76d0ea0 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.sr_rs/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.sr_rs/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Serbian (Serbia) (https://www.transifex.com/teamxbmc/teams/40581/sr_RS/)\n"
+"Language: sr_RS\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: sr_RS\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Подешавања"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.sr_rs@latin/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.sr_rs@latin/strings.po
index 67c0e1e4ac..b639b907a2 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.sr_rs@latin/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.sr_rs@latin/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Serbian (Latin) (Serbia) (https://www.transifex.com/teamxbmc/teams/40581/sr_RS@latin/)\n"
+"Language: sr_RS@latin\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: sr_RS@latin\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "Podešavanja"
@@ -52,45 +57,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.sv_se/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.sv_se/strings.po
index 99719cc4bc..8cb829dce8 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.sv_se/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.sv_se/strings.po
@@ -2,24 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# Tobias Boväng <tobias.bovang@supergott.com>, 2020
-# krampus <krampuss@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: krampus <krampuss@gmail.com>, 2020\n"
-"Language-Team: Swedish (Sweden) (https://www.transifex.com/teamxbmc/teams/40581/sv_SE/)\n"
+"PO-Revision-Date: 2021-10-21 19:30+0000\n"
+"Last-Translator: Prahlis <prahl.tobias@gmail.com>\n"
+"Language-Team: Swedish <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/sv_se/>\n"
+"Language: sv_se\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: sv_SE\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.8\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Allmän musikskrapa för artister"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Söker efter artistinformation och artwork från flera källor."
msgctxt "#30000"
msgid "Preferences"
@@ -27,7 +31,7 @@ msgstr "Inställningar"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Föredraget språk för artistbiografi"
msgctxt "#30002"
msgid "Prefer biography from"
@@ -54,51 +58,29 @@ msgid "Allow less accurate search results"
msgstr "Tillåt mindre träffsäkra sökresultat"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
-"Artistens biografi kommer att laddas hem på det valda språket om den finns "
-"tillgänglig. Om inte kommer den att laddas hem på engelska."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Om det finns tillgängligt kommer artistens biografi att hämtas på det valda språket. Annars kommer engelska att användas."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Försök att få artistbiografi med den valda skraparen. Andra skrapare används om den föredragna skraparen inte ger några resultat."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Försök få artistdiskografin med den valda skraparen. Andra skrapare används om den föredragna skraparen inte ger några resultat."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Försök att få genreinformation med den valda skraparen. Andra skrapare används om den föredragna skraparen inte ger några resultat."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Försök att få formatinformation med den valda skraparen. Andra skrapare används om den föredragna skraparen inte ger några resultat."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Försök få information om stämning med den valda skraparen. Andra skrapare används om den föredragna skraparen inte ger några resultat."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"Om Musicbrainz inte tillhandahåller en länk till Allmusic och/eller Discogs "
-"så kan vi söka på de siterna baserat på artistnamn & albumtitel. I de fall "
-"där en artist släppt flera album med samma titel kan det potentiellt "
-"resultera i en blandning av metadata från dessa album."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Om Musicbrainz inte tillhandahåller en länk till Allmusic och/eller Discogs så kan vi söka på de siterna baserat på artistnamn & albumtitel. I de fall där en artist släppt flera album med samma titel kan det potentiellt resultera i en blandning av metadata från dessa album."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.szl/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.szl/strings.po
index 20ef353d38..263035c358 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.szl/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.szl/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Silesian (https://www.transifex.com/teamxbmc/teams/40581/szl/)\n"
+"Language: szl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: szl\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.ta_in/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.ta_in/strings.po
index 651927a7bd..694d7d50b4 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.ta_in/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.ta_in/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Tamil (India) (https://www.transifex.com/teamxbmc/teams/40581/ta_IN/)\n"
+"Language: ta_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ta_IN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.te_in/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.te_in/strings.po
index e520a5b28e..8bfbbc656a 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.te_in/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.te_in/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Telugu (India) (https://www.transifex.com/teamxbmc/teams/40581/te_IN/)\n"
+"Language: te_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: te_IN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.tg_tj/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.tg_tj/strings.po
index 88cb8d8e6a..d09f9f4e84 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.tg_tj/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.tg_tj/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Tajik (Tajikistan) (https://www.transifex.com/teamxbmc/teams/40581/tg_TJ/)\n"
+"Language: tg_TJ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: tg_TJ\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.th_th/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.th_th/strings.po
index 4b9ffe95d6..33ee5510ef 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.th_th/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.th_th/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Thai (Thailand) (https://www.transifex.com/teamxbmc/teams/40581/th_TH/)\n"
+"Language: th_TH\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: th_TH\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.tr_tr/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.tr_tr/strings.po
index 9297947a85..8719247824 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.tr_tr/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.tr_tr/strings.po
@@ -2,22 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
-"Language-Team: Turkish (Turkey) (https://www.transifex.com/teamxbmc/teams/40581/tr_TR/)\n"
+"PO-Revision-Date: 2022-04-19 16:13+0000\n"
+"Last-Translator: queeup <queeup@zoho.com>\n"
+"Language-Team: Turkish <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/tr_tr/>\n"
+"Language: tr_tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: tr_TR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Weblate 4.11.2\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "Sanatçılar için genel müzik bilgi sağlayıcısı"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "Birden çok web sitesinde sanatçı bilgilerini ve albüm kapaklarını arar."
msgctxt "#30000"
msgid "Preferences"
@@ -25,72 +31,56 @@ msgstr "Tercihler"
msgctxt "#30001"
msgid "Preferred language for artist biography"
-msgstr ""
+msgstr "Sanatçı biyografisi için varsayılan dil"
msgctxt "#30002"
msgid "Prefer biography from"
-msgstr ""
+msgstr "Biyografinin alınacağı yer:"
msgctxt "#30003"
msgid "Prefer discography from"
-msgstr ""
+msgstr "Diskografinin alınacağı yer:"
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "Tarzların alınacağı yer:"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "Stillerin alınacağı yer:"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "Ruh hallerinin alınacağı yer:"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "Doğruluk düzeyi düşük arama sonuçlarına izin ver"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
-msgstr ""
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
+msgstr "Sanatçı biyografisi varsa seçili dilde, yoksa İngilizce indirilecektir."
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Seçilen sağlayıcıyı kullanarak sanatçı biyografisini almaya çalışın. Tercih edilen sağlayıcı sonuç vermezse diğer sağlayıcılar kullanılacaktır."
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Seçilen sağlayıcıyı kullanarak sanatçı diskografisini almaya çalışın. Tercih edilen sağlayıcı sonuç vermezse diğer sağlayıcılar kullanılacaktır."
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Seçilen sağlayıcıyı kullanarak tarz bilgisini almaya çalışın. Tercih edilen sağlayıcı sonuç vermezse diğer sağlayıcılar kullanılacaktır."
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Seçilen sağlayıcıyı kullanarak stil bilgisini almaya çalışın. Tercih edilen sağlayıcı sonuç vermezse diğer sağlayıcılar kullanılacaktır."
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
-msgstr ""
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
+msgstr "Seçilen sağlayıcıyı kullanarak ruh hali bilgisini almaya çalışın. Tercih edilen sağlayıcı sonuç vermezse diğer sağlayıcılar kullanılacaktır."
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "Musicbrainz allmusic ve/veya discogs sitesine bir bağlantı sağlamazsa, bu siteleri sanatçı adına göre arayabiliriz. Sanatçı adları benzersiz olmadığından, bu durum potansiyel olarak aynı ada sahip birden çok sanatçının meta verilerinin karışmasıyla sonuçlanabilir."
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.uk_ua/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.uk_ua/strings.po
index e92ba8d820..a371ecad34 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.uk_ua/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.uk_ua/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Ukrainian (Ukraine) (https://www.transifex.com/teamxbmc/teams/40581/uk_UA/)\n"
+"Language: uk_UA\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: uk_UA\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.uz_uz/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.uz_uz/strings.po
index 56f2f4494e..5f2aa77eef 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.uz_uz/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.uz_uz/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Uzbek (Uzbekistan) (https://www.transifex.com/teamxbmc/teams/40581/uz_UZ/)\n"
+"Language: uz_UZ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: uz_UZ\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.vi_vn/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.vi_vn/strings.po
index 798cace915..086263e10f 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.vi_vn/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.vi_vn/strings.po
@@ -5,16 +5,24 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Vietnamese (Viet Nam) (https://www.transifex.com/teamxbmc/teams/40581/vi_VN/)\n"
+"Language: vi_VN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: vi_VN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr ""
@@ -48,45 +56,29 @@ msgid "Allow less accurate search results"
msgstr ""
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.zh_cn/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.zh_cn/strings.po
index cc1e5caf5b..393b7c2f9e 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.zh_cn/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.zh_cn/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-# taxigps <taxigps@sina.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: taxigps <taxigps@sina.com>, 2020\n"
-"Language-Team: Chinese (China) (https://www.transifex.com/teamxbmc/teams/40581/zh_CN/)\n"
+"PO-Revision-Date: 2021-09-12 16:30+0000\n"
+"Last-Translator: taxigps <taxigps@sina.com>\n"
+"Language-Team: Chinese (China) <https://kodi.weblate.cloud/projects/kodi-add-ons-information-providers/metadata-generic-artists/zh_cn/>\n"
+"Language: zh_cn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 4.8\n"
+
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr "歌手通用音乐刮削器"
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr "从多个网站搜索歌手信息和艺术图片。"
msgctxt "#30000"
msgid "Preferences"
@@ -53,47 +58,29 @@ msgid "Allow less accurate search results"
msgstr "允许不精确的搜索结果"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr "下载所选语言的歌手传记,无此语种评论时英语为后备。"
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "使用所选刮削器获取歌手传记,当首选刮削器无结果时,将使用其它刮削器。"
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "使用所选刮削器获取歌手作品集,当首选刮削器无结果时,将使用其它刮削器。"
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "使用所选刮削器获取类型信息,当首选刮削器无结果时,将使用其它自刮削器。"
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "使用所选刮削器获取风格信息,当首选刮削器无结果时,将使用其它自刮削器。"
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr "使用所选刮削器获取情绪信息,当首选刮削器无结果时,将使用其它刮削器。"
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
-msgstr ""
-"当 Musicbrainz 没有提供指向 allmusic 和/或 discogs "
-"的链接时,我们用歌手名搜索这些网站。由于歌手名不是唯一的,这可能会导致来自多个同名歌手的元数据混杂。"
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
+msgstr "当 Musicbrainz 没有提供指向 allmusic 和/或 discogs 的链接时,我们用歌手名搜索这些网站。由于歌手名不是唯一的,这可能会导致来自多个同名歌手的元数据混杂。"
diff --git a/addons/metadata.generic.artists/resources/language/resource.language.zh_tw/strings.po b/addons/metadata.generic.artists/resources/language/resource.language.zh_tw/strings.po
index c3ffe10e5f..fd17bbcca8 100644
--- a/addons/metadata.generic.artists/resources/language/resource.language.zh_tw/strings.po
+++ b/addons/metadata.generic.artists/resources/language/resource.language.zh_tw/strings.po
@@ -2,23 +2,28 @@
# Addon Name: Generic Artist Scraper
# Addon id: metadata.generic.artists
# Addon Provider: Team Kodi
-# Translators:
-# TeamKODI <transifex.translator@gmail.com>, 2020
-#
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2020-09-08 20:22+0000\n"
-"Last-Translator: TeamKODI <transifex.translator@gmail.com>, 2020\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
"Language-Team: Chinese (Taiwan) (https://www.transifex.com/teamxbmc/teams/40581/zh_TW/)\n"
+"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+msgctxt "Addon Summary"
+msgid "Generic music scraper for artists"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Searches for artist information and artwork across multiple websites."
+msgstr ""
+
msgctxt "#30000"
msgid "Preferences"
msgstr "喜好設定"
@@ -37,60 +42,44 @@ msgstr ""
msgctxt "#30004"
msgid "Prefer genres from"
-msgstr ""
+msgstr "喜好的類型從"
msgctxt "#30005"
msgid "Prefer styles from"
-msgstr ""
+msgstr "喜好的風格從"
msgctxt "#30006"
msgid "Prefer moods from"
-msgstr ""
+msgstr "喜好的氛圍從"
msgctxt "#30101"
msgid "Allow less accurate search results"
-msgstr ""
+msgstr "允許較不精確的搜尋結果"
msgctxt "#30201"
-msgid ""
-"If available, the artist biography will be downloaded in the selected "
-"language. It will fallback to English."
+msgid "If available, the artist biography will be downloaded in the selected language. It will fallback to English."
msgstr ""
msgctxt "#30202"
-msgid ""
-"Try to get the artist biography using the selected scraper. Other scrapers "
-"will be used if the preferred scraper returns no results."
+msgid "Try to get the artist biography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30203"
-msgid ""
-"Try to get the artist discography using the selected scraper. Other scrapers"
-" will be used if the preferred scraper returns no results."
+msgid "Try to get the artist discography using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30204"
-msgid ""
-"Try to get genre info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get genre info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30205"
-msgid ""
-"Try to get style info using the selected scraper. Other scrapers will be "
-"used if the preferred scraper returns no results."
+msgid "Try to get style info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30206"
-msgid ""
-"Try to get mood info using the selected scraper. Other scrapers will be used"
-" if the preferred scraper returns no results."
+msgid "Try to get mood info using the selected scraper. Other scrapers will be used if the preferred scraper returns no results."
msgstr ""
msgctxt "#30301"
-msgid ""
-"If Musicbrainz does not provide a link to allmusic and/or discogs, we can "
-"search those sites based on artistname. Since artist names are not unique, "
-"this could potentially result in a mixture of metadata from multiple artists"
-" by the same name."
+msgid "If Musicbrainz does not provide a link to allmusic and/or discogs, we can search those sites based on artistname. Since artist names are not unique, this could potentially result in a mixture of metadata from multiple artists by the same name."
msgstr ""
diff --git a/addons/repository.xbmc.org/addon.xml b/addons/repository.xbmc.org/addon.xml
index d68ff776bc..c258f182d3 100644
--- a/addons/repository.xbmc.org/addon.xml
+++ b/addons/repository.xbmc.org/addon.xml
@@ -158,7 +158,7 @@
<disclaimer lang="es_AR">No todos los add-ons en este repositorio han sido creados por el equipo de Kodi y este no es responsable de su contenido</disclaimer>
<disclaimer lang="es_ES">El Equipo Kodi no ha creado todos los Add-ons de este repositorio y no es responsable de su contenido</disclaimer>
<disclaimer lang="es_MX">El equipo de Kodi no hizo todos los complementos en este repositorio y no es responsable de su contenido</disclaimer>
- <disclaimer lang="et_EE">Kodi meeskond ei teinud kõiki lisasid selles hoidlas ja ei vastuta ka nende sisu eest.</disclaimer>
+ <disclaimer lang="et_EE">Kodi meeskond ei teinud kõiki lisasid selles hoidlas ja ei vastuta ka nende sisu eest</disclaimer>
<disclaimer lang="eu_ES">Kodi taldeak ez ditu biltegi honetako gehigarri guztiak sortu eta ez da beren edukien erantzule.</disclaimer>
<disclaimer lang="fa_IR">همه افزونه های این منبع محصول کار تیم کودی نیستند و این تیم مسئولیتی در قبال محتوای آنها ندارد.</disclaimer>
<disclaimer lang="fi_FI">Team Kodi ei ole kehittänyt kaikkia varaston lisäosia, eikä ole vastuussa niiden sisällöstä.</disclaimer>
diff --git a/addons/repository.xbmc.org/resources/language/resource.language.et_ee/strings.po b/addons/repository.xbmc.org/resources/language/resource.language.et_ee/strings.po
index 2a93f968ac..70a391a822 100644
--- a/addons/repository.xbmc.org/resources/language/resource.language.et_ee/strings.po
+++ b/addons/repository.xbmc.org/resources/language/resource.language.et_ee/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-03-27 15:08+0000\n"
-"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"PO-Revision-Date: 2023-10-20 03:05+0000\n"
+"Last-Translator: rimasx <riks_12@hot.ee>\n"
"Language-Team: Estonian <https://kodi.weblate.cloud/projects/kodi-core/repository-xbmc-org/et_ee/>\n"
"Language: et_ee\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.0.2\n"
msgctxt "Addon Summary"
msgid "Install Add-ons from Kodi.tv"
@@ -27,4 +27,4 @@ msgstr "Lae alla ja installi lisasid ametlikust Kodi.tv hoidlast.[CR] Ametliku
msgctxt "Addon Disclaimer"
msgid "Team Kodi did not make all the add-ons on this repository and are not responsible for their content"
-msgstr "Kodi meeskond ei teinud kõiki lisasid selles hoidlas ja ei vastuta ka nende sisu eest."
+msgstr "Kodi meeskond ei teinud kõiki lisasid selles hoidlas ja ei vastuta ka nende sisu eest"
diff --git a/addons/repository.xbmc.org/resources/language/resource.language.hu_hu/strings.po b/addons/repository.xbmc.org/resources/language/resource.language.hu_hu/strings.po
index 8eb6882c9c..163c14a66f 100644
--- a/addons/repository.xbmc.org/resources/language/resource.language.hu_hu/strings.po
+++ b/addons/repository.xbmc.org/resources/language/resource.language.hu_hu/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-03-27 15:08+0000\n"
-"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"PO-Revision-Date: 2023-11-28 18:10+0000\n"
+"Last-Translator: Frodo19 <bilbohu@gmail.com>\n"
"Language-Team: Hungarian <https://kodi.weblate.cloud/projects/kodi-core/repository-xbmc-org/hu_hu/>\n"
"Language: hu_hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Install Add-ons from Kodi.tv"
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index cb5a0b9cfd..296c4039e1 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -953,6 +953,7 @@ msgstr ""
#: addons/skin.estuary/xml/SkinSettings.xml
#: addons/skin.estuary/xml/Variables.xml
#: xbmc/dialogs/GUIDialogPlayEject.cpp
+#: xbmc/favourites/ContextMenus.cpp
#: xbmc/games/windows/GUIWindowGames.cpp
#: xbmc/music/ContextMenus.h
#: xbmc/video/ContextMenus.cpp
@@ -5624,27 +5625,67 @@ msgstr ""
#empty strings from id 10802 to 10819
+#. Title of controller configuration window
msgctxt "#10820"
-msgid "Game video filter"
+msgid "Controller Configuration"
msgstr ""
+#. Title of the "My Games" window
msgctxt "#10821"
msgid "Games"
msgstr ""
+#. Title of the in-game OSD menu
msgctxt "#10822"
-msgid "Game OSD"
+msgid "Menu"
msgstr ""
+#. Title of the in-game video filter selection dialog
msgctxt "#10823"
-msgid "Game controllers"
+msgid "Video Filter"
msgstr ""
+#. Title of the in-game video stretch mode selection dialog
msgctxt "#10824"
-msgid "Game video settings"
+msgid "Stretch Mode"
+msgstr ""
+
+#. Title of the in-game volume dialog
+msgctxt "#10825"
+msgid "Volume"
+msgstr ""
+
+#. Title of the in-game dialog for advanced emulator settings
+msgctxt "#10826"
+msgid "Advanced Settings"
+msgstr ""
+
+#. Title of the in-game video rotation selection dialog
+msgctxt "#10827"
+msgid "Video Rotation"
msgstr ""
-#empty strings from id 10825 to 11999
+#. Title of the window for setting up controller ports used by the game
+msgctxt "#10828"
+msgid "Port Setup"
+msgstr ""
+
+#. Title of the in-game dialog to select a savestate for the current game
+msgctxt "#10829"
+msgid "Select Savestate"
+msgstr ""
+
+#. Title of the out-of-game dialog to select a savestate for the current game
+msgctxt "#10830"
+msgid "Select Savestate"
+msgstr ""
+
+#. Name of window for viewing and configuring players while playing a game
+msgctxt "#10831"
+msgid "Players"
+msgstr ""
+
+#empty strings from id 10832 to 11999
#: xbmc/guilib/WindowIDs.h
msgctxt "#12000"
@@ -5722,9 +5763,11 @@ msgstr ""
#: addons/skin.estuary/xml/SkinSettings.xml
#: addons/skin.estuary/xml/Variables.xml
#: xbmc/Autorun.cpp
+#: xbmc/favourites/ContextMenus.cpp
#: xbmc/pvr/PVRGUIActionsPlayback.cpp
#: xbmc/video/ContextMenus.cpp
#: xbmc/video/guilib/VideoSelectActionProcessor.cpp
+#: xbmc/video/guilib/VideoPlayActionProcessor.cpp
msgctxt "#12021"
msgid "Play from beginning"
msgstr ""
@@ -7304,7 +7347,7 @@ msgstr ""
#: system/settings/settings.xml
msgctxt "#13436"
-msgid "Use display HDR capabilities"
+msgid "Adjust display HDR mode"
msgstr ""
#: system/settings/settings.xml
@@ -8769,6 +8812,9 @@ msgstr ""
#empty strings from id 15209 to 15212
+#. Label for context menu action to choose a player and play
+#: xbmc/music/ContextMenus.h
+#: xbmc/video/ContextMenus.h
#: xbmc/windows/GUIWindowFileManager.cpp
msgctxt "#15213"
msgid "Play using..."
@@ -9636,6 +9682,7 @@ msgstr ""
#. generic 'information' label used in different places, like labels for message box headers
#: xbmc/event/windows/GUIWindowEventLog.cpp
+#: xbmc/favourites/ContextMenus.cpp
#: xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp
#: xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp
#: xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp
@@ -15309,7 +15356,25 @@ msgctxt "#22033"
msgid "Charset"
msgstr ""
-#empty strings from id 22034 to 22078
+#empty strings from id 22034 to 22075
+
+#. Label for setting to choose the default action for "play"
+#: system/settings/settings.xml
+msgctxt "#22076"
+msgid "Default play action"
+msgstr ""
+
+#. Label for value for default play action setting
+#: system/settings/settings.xml
+msgctxt "#22077"
+msgid "Ask if resumable"
+msgstr ""
+
+#. Label for value for default play action setting
+#: system/settings/settings.xml
+msgctxt "#22078"
+msgid "Resume"
+msgstr ""
#: system/settings/settings.xml
msgctxt "#22079"
@@ -15329,6 +15394,7 @@ msgid "Show information"
msgstr ""
#. Label to denote that there is something 'more'
+#. xbmc/favourites/ContextMenus.h
#: xbmc/listproviders/DirectoryProvider.cpp
#: xbmc/video/guilib/VideoSelectActionProcessor.cpp
#: system/settings/settings.xml
@@ -16072,17 +16138,7 @@ msgctxt "#24127"
msgid "Automatically download first subtitle from the search result list"
msgstr ""
-#. Info dialog after migrating userdata from xbmc to kodi
-#: xbmc/Application.cpp
-msgctxt "#24128"
-msgid "Configuration has been moved"
-msgstr ""
-
-#. Info dialog after migrating userdata from xbmc to kodi
-#: xbmc/Application.cpp
-msgctxt "#24129"
-msgid "The configuration of XBMC has been moved to the new location for Kodi. Please refer to http://kodi.wiki/view/Migration - this message will not be shown again!"
-msgstr ""
+#empty strings from id 24128 to 24129
#: system/settings/settings.xml
msgctxt "#24130"
@@ -17061,7 +17117,13 @@ msgctxt "#29994"
msgid "Roles"
msgstr ""
-#empty strings from id 29995 to 33000
+#. Label for multi-disc movies, including movie title (param 0) and disc number (param 1).
+#: xbc/video/VideoInfoScanner.cpp
+msgctxt "#29995"
+msgid "{0:s} (Disc {1:s})"
+msgstr ""
+
+#empty strings from id 29996 to 33000
#strings 30000 thru 30999 reserved for plug-ins and plug-in settings
#strings 31000 thru 31999 reserved for skins
#strings 32000 thru 32999 reserved for scripts
@@ -19638,7 +19700,11 @@ msgctxt "#36203"
msgid "Enables the \"Personal Video Recorder\" (PVR) features. This requires that at least one PVR add-on is installed."
msgstr ""
-#empty string with id 36204
+#. Description of setting with label #22076 "Default play action"
+#: system/settings/settings.xml
+msgctxt "#36204"
+msgid "Toggle between [Ask if resumable] (default) and [Resume].[CR][Ask if resumable] will ask whether to play from beginning or to resume (if a resume point is present).[CR][Resume] will automatically resume videos from the last position that you were viewing them.[CR]If no resume point is set, playback will automatically start from the beginning."
+msgstr ""
#: system/settings/settings.xml
msgctxt "#36205"
@@ -20152,10 +20218,10 @@ msgctxt "#36298"
msgid "If enabled, a recording for the programme to remind will be scheduled when auto-closing the reminder popup, if supported by the PVR add-on and backend."
msgstr ""
-#. Description of setting with label #13436 "Use display HDR capabilities"
+#. Description of setting with label #13436 "Adjust display HDR mode"
#: system/settings/settings.xml
msgctxt "#36299"
-msgid "Switch display into HDR mode if media with HDR information is played.[CR]If disabled, HDR information are applied using Kodi's internal HDR path."
+msgid "Allow the HDR mode of the display to be changed to best match the media.[CR]When disabled, Kodi applies tonemapping as needed to adapt the media to the current HDR mode of the display."
msgstr ""
#. Description of setting with label #20226 "Movie set information folder"
@@ -21914,10 +21980,12 @@ msgctxt "#37014"
msgid "Last used profile"
msgstr ""
+#. Label used in different places to denote the function to open a folder to list its content
#: addons/skin.estuary/xml/SkinSettings.xml
#: addons/skin.estuary/xml/Variables.xml
+#: xbmc/favourites/ContextMenus.h
#: xbmc/music/ContextMenus.h
-#: xbmc/windows/GUIMediaWindow.cpp
+#: xbmc/video/ContextMenus.h
msgctxt "#37015"
msgid "Browse into"
msgstr ""
@@ -22111,7 +22179,155 @@ msgctxt "#37052"
msgid "NFS protocol version to use when establishing NFS connections"
msgstr ""
-#empty strings from id 37053 to 38010
+#. Settings / Services "Chunk Size" group label
+#: system/settings/settings.xml
+msgctxt "#37053"
+msgid "Chunk Size"
+msgstr ""
+
+#. Setting #37054 "NFS Chunk Size"
+#: xbmc/filesystem/NFSFile.cpp
+msgctxt "#37054"
+msgid "NFS Chunk Size"
+msgstr ""
+
+#. Description of setting #37054 "NFS Chunk Size"
+#: system/settings/settings.xml
+msgctxt "#37055"
+msgid "Data chunk size used on NFS connections"
+msgstr ""
+
+#. Setting #37056 "SMB Chunk Size"
+#: system/settings/settings.xml
+msgctxt "#37056"
+msgid "SMB Chunk Size"
+msgstr ""
+
+#. Description of setting #37056 "SMB Chunk Size"
+#: system/settings/settings.xml
+msgctxt "#37057"
+msgid "Data chunk size used on SMB connections"
+msgstr ""
+
+#empty strings from id 37058 to 37100
+
+#. Settings / Services "Caching" category label
+#: system/settings/settings.xml
+msgctxt "#37101"
+msgid "Caching"
+msgstr ""
+
+#. Description of category #37101 "Caching"
+#: system/settings/settings.xml
+msgctxt "#37102"
+msgid "This category contains settings that configure caching of local and network files, and internet streams."
+msgstr ""
+
+#. Setting #37103 "Buffer Mode"
+#: system/settings/settings.xml
+msgctxt "#37103"
+msgid "Buffer Mode"
+msgstr ""
+
+#. Description of setting #37103 "Buffer Mode"
+#: system/settings/settings.xml
+msgctxt "#37104"
+msgid "Configure the media content to buffer."
+msgstr ""
+
+#. Setting #37105 "Memory Size"
+#: system/settings/settings.xml
+msgctxt "#37105"
+msgid "Memory Size"
+msgstr ""
+
+#. Description of setting #37105 "Memory Size"
+#: system/settings/settings.xml
+msgctxt "#37106"
+msgid "The size of the memory buffer in Mbytes. Setting zero (0) forces buffering to disk, which is not recommend on flash storage devices (eMMC, SD cards, SSD)."
+msgstr ""
+
+#. Setting #37107 "Read Factor"
+#: system/settings/settings.xml
+msgctxt "#37107"
+msgid "Read Factor"
+msgstr ""
+
+#. Description of setting #37107 "Read Factor"
+#: system/settings/settings.xml
+msgctxt "#37108"
+msgid "The read factor determines cache fill rate in terms of avg. bitrate of stream x Read Factor. Increasing this multiple, cache fills faster but more bandwidth is consumed. Large multiples may cause CPU spikes on some devices, saturate the connection and worsen performance."
+msgstr ""
+
+#. Description of setting "Chunk Size"
+#: system/settings/settings.xml
+msgctxt "#37109"
+msgid "The data chunk size to use when a filesystem or protocol does not force the value, e.g. NFS will override this with its own value."
+msgstr ""
+
+#. Value of setting - NONE
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37110"
+msgid "No buffer"
+msgstr ""
+
+#. Value of setting - TRUE INTERNET
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37111"
+msgid "Only buffer true internet streams: HTTP, HTTPS, etc."
+msgstr ""
+
+#. Value of setting - INTERNET
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37112"
+msgid "Buffer all internet filesystems, including: FTP, WebDAV, etc."
+msgstr ""
+
+#. Value of setting - NETWORK
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37113"
+msgid "Buffer all network filesystems, including: SMB, NFS, etc."
+msgstr ""
+
+#. Value of setting - ALL
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37114"
+msgid "Buffer all filesystems, including local files"
+msgstr ""
+
+#. Value of setting
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37115"
+msgid "Caches entire file on disk storage"
+msgstr ""
+
+#empty strings from id 37116 to 37119
+
+#. Value of setting - Byte
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37120"
+msgid "{0:d} Byte"
+msgstr ""
+
+#. Value of setting - KByte
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37121"
+msgid "{0:d} KB"
+msgstr ""
+
+#. Value of setting - MByte
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37122"
+msgid "{0:d} MB"
+msgstr ""
+
+#. Value of setting - GByte
+#: xbmc/settings/SevicesSettings.cpp
+msgctxt "#37123"
+msgid "{0:d} GB"
+msgstr ""
+
+#empty strings from id 37124 to 38010
#. Setting #38011 "Show All Items entry"
#: system/settings/settings.xml
@@ -23488,7 +23704,7 @@ msgstr ""
#. Help text of setting "Allow use DXVA Video Super Resolution" with label #13417
#: system/settings/settings.xml
msgctxt "#39194"
-msgid "Enables advanced DXVA upscaler using NVIDIA \"RTX Video Super Resolution\" or \"Intel Video Super Resolution\".[CR]Used when video source is 1080p or less (progressive only) and source resolution is lower than display resolution.[CR]It's only available on specific hardware: NVIDIA RTX 40x, RTX 30x and Intel Arc A770, A750."
+msgid "Enables advanced DXVA upscaler using NVIDIA \"RTX Video Super Resolution\" or \"Intel Video Super Resolution\".[CR]Used when the video source is 1920x1080 or less and the source resolution is lower than the display resolution.[CR]Only available on specific hardware: NVIDIA RTX 20xx, 30xx, 40xx and above, Intel Arc A770, A750."
msgstr ""
#. Description of setting with label #13418 "Use high precision processing"
@@ -23508,3 +23724,434 @@ msgstr ""
msgctxt "#39197"
msgid "If enabled, Dolby Vision profile 7 will be converted to profile 8.1, which is more commonly supported by devices. Enable if your device supports Dolby Vision, but has issues with some videos."
msgstr ""
+
+# 40000 to 40800 are reserved for Video Versions feature
+
+#. Video versions
+#: xbmc/filesystem/VideoDatabaseDirectory.cpp
+#: xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp
+#: addons/skin.estuary/xml/DialogVideoInfo.xml
+msgctxt "#40000"
+msgid "Versions"
+msgstr ""
+
+#. Manage video version menu
+#: xbmc/video/dialogs/GUIDialogVideoInfo.cpp
+msgctxt "#40001"
+msgid "Manage {0:s} version"
+msgstr ""
+
+#. Manage video version dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40002"
+msgid "Convert into an additional version of {0:s}"
+msgstr ""
+
+#. Video version selection dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40003"
+msgid "Select {0:s} version"
+msgstr ""
+
+#. New video version dialog button
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+#: xbmc/video/windows/GUIWindowVideoNav.cpp
+msgctxt "#40004"
+msgid "New version..."
+msgstr ""
+
+#. Warning dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40005"
+msgid "Operation not supported"
+msgstr ""
+
+#. Warning dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40006"
+msgid "The selected {0:s} contains multiple versions. Cannot convert into an additional version of another one. Remove it from the library, rescan, and convert each version separately."
+msgstr ""
+
+#. Warning dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40007"
+msgid "The \"{0:s}\" version already exists!"
+msgstr ""
+
+#. Different video version found dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40008"
+msgid "Different {0:s} version found"
+msgstr ""
+
+#. Different video version found dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40009"
+msgid "Found a different version of {0:s} \"{1:s}\" ({2:s}). Would you like to convert it into an additional version of the original?"
+msgstr ""
+
+#. "video version" string in singular format
+#: xbmc/media/MediaType.cpp
+msgctxt "#40010"
+msgid "video version"
+msgstr ""
+
+#. "video version" string in plural format
+#: xbmc/media/MediaType.cpp
+msgctxt "#40011"
+msgid "video versions"
+msgstr ""
+
+#. "video version" string in singular capital format
+#: xbmc/media/MediaType.cpp
+msgctxt "#40012"
+msgid "Video version"
+msgstr ""
+
+#. "video version" string in plural capital format
+#: xbmc/media/MediaType.cpp
+msgctxt "#40013"
+msgid "Video versions"
+msgstr ""
+
+#. Add new video version dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40014"
+msgid "Add new {0:s} version"
+msgstr ""
+
+#. Add new video extras dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40015"
+msgid "Add new {0:s} extras"
+msgstr ""
+
+#. Add new video version dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40016"
+msgid "The selected video is already \"{0:s}\" version of current {1:s}."
+msgstr ""
+
+#. Add new video version dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40017"
+msgid "The selected video is \"{0:s}\" version of {1:s} \"{2:s}\". Would you like to remove this version and add it to current {3:s}?"
+msgstr ""
+
+#. Remove video version dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40018"
+msgid "Remove video version"
+msgstr ""
+
+#. Add new video version dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40019"
+msgid "Unable to remove default version \"{0:s}\" from {1:s} \"{2:s}\". Change the default version and try again."
+msgstr ""
+
+#. Remove video version dialog text
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40020"
+msgid "Are you sure to remove the video version \"{0:s}\"?"
+msgstr ""
+
+#. Convert video version menu
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40021"
+msgid "Convert {0:s} version"
+msgstr ""
+
+#. Manage video version dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40022"
+msgid "Manage {0:s}"
+msgstr ""
+
+#. Choose video version dialog title
+#: xbmc/video/dialogs/GUIDialogVideoVersion.cpp
+msgctxt "#40023"
+msgid "Choose {0:s}"
+msgstr ""
+
+#. Select default video version setting
+#: system/settings/settings.xml
+msgctxt "#40200"
+msgid "Select default video version"
+msgstr ""
+
+#. Help for select default video version setting
+#: system/settings/settings.xml
+msgctxt "#40201"
+msgid "Defines how to handle selection of videos with multiple versions.[CR][Enabled] Automatically select the default video version without prompting.[CR][Disabled] Display a dialogue to select a version of the video."
+msgstr ""
+
+#. Ignore different video versions settting
+#: system/settings/settings.xml
+msgctxt "#40202"
+msgid "Ignore different video versions on scan"
+msgstr ""
+
+#. Help for Ignore different video versions settting
+#: system/settings/settings.xml
+msgctxt "#40203"
+msgid "Select scanner action for different video versions.[CR][Enabled] Add different video versions to library seperately without confirmation.[CR][Disabled] Prompt for converting different video versions into additional versions of the original."
+msgstr ""
+
+#. Ignore video extras settting
+#: system/settings/settings.xml
+msgctxt "#40204"
+msgid "Ignore video extras on scan"
+msgstr ""
+
+#. Help for ignore video extras settting
+#: system/settings/settings.xml
+msgctxt "#40205"
+msgid "Select scanner action for video extras in \"extras\" folder.[CR][Enabled] Scan video extras like regular videos.[CR][Disabled] Add video extras to library for associated video."
+msgstr ""
+
+#. Show video versions as folder settting
+#: system/settings/settings.xml
+msgctxt "#40206"
+msgid "Show video with multiple versions as folder"
+msgstr ""
+
+#. Help for show video versions as folder settting
+#: system/settings/settings.xml
+msgctxt "#40207"
+msgid "When enabled, a video with multiple versions will be shown as a folder in the video library. This folder can then be opened to display the individual video versions. When disabled, the configured select action will be applied."
+msgstr ""
+
+#. Choose video version context menu item label
+#: xbmc/video/ContextMenus.h
+msgctxt "#40208"
+msgid "Choose version"
+msgstr ""
+
+#empty strings from id 40209 to 40399
+
+# Video versions
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40400"
+msgid "Standard Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40401"
+msgid "Extended Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40402"
+msgid "Unrated Version"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40403"
+msgid "Uncut Version"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40404"
+msgid "Remastered Version"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40405"
+msgid "4K"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40406"
+msgid "Theatrical Cut"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40407"
+msgid "Director's Cut"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40408"
+msgid "Special Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40409"
+msgid "Limited Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40410"
+msgid "Complete Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40411"
+msgid "The Final Cut"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40412"
+msgid "Super Duper Cut"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40413"
+msgid "Collector's Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40414"
+msgid "Ultimate Collector's Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40415"
+msgid "Criterion Collection Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40416"
+msgid "Fan Edit"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40417"
+msgid "Black and White Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40418"
+msgid "BluRay"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40419"
+msgid "WEB-DL"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40420"
+msgid "3D"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40421"
+msgid "8K"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40422"
+msgid "IMAX"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40423"
+msgid "UHD"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40424"
+msgid "FHD"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40425"
+msgid "HD"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40426"
+msgid "SD"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40427"
+msgid "DVD"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40428"
+msgid "VHS"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40429"
+msgid "VCD"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40430"
+msgid "REMUX"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40431"
+msgid "10th Anniversary Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40432"
+msgid "20th Anniversary Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40433"
+msgid "25th Anniversary Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40434"
+msgid "30th Anniversary Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40435"
+msgid "40th Anniversary Edition"
+msgstr ""
+
+#. Name of a video version, like "Director's Cut"
+#: xbmc/video/VideoDatabase.cpp
+msgctxt "#40436"
+msgid "50th Anniversary Edition"
+msgstr ""
+
+# 40000 to 40800 are reserved for Video Versions feature
diff --git a/addons/screensaver.xbmc.builtin.dim/addon.xml b/addons/screensaver.xbmc.builtin.dim/addon.xml
index 290290a6a8..030198fd42 100644
--- a/addons/screensaver.xbmc.builtin.dim/addon.xml
+++ b/addons/screensaver.xbmc.builtin.dim/addon.xml
@@ -18,7 +18,7 @@
<summary lang="cs_CZ">Spořič obrazovky, který ztmaví vaši obrazovku</summary>
<summary lang="cy_GB">Arbedwr sgrin sy&apos;n pylu eich sgrin</summary>
<summary lang="da_DK">Pauseskærm som dæmper din skærm</summary>
- <summary lang="de_DE">Bildschirmschoner zum Abdunkeln des Bildschirmes</summary>
+ <summary lang="de_DE">Bildschirmschoner zum Abdunkeln des Bildschirms</summary>
<summary lang="el_GR">Προφύλαξη οθόνης που σκιάζει την οθόνη σας</summary>
<summary lang="en_AU">Screensaver that dims your screen</summary>
<summary lang="en_GB">Screensaver that dims your screen</summary>
diff --git a/addons/screensaver.xbmc.builtin.dim/resources/language/resource.language.de_de/strings.po b/addons/screensaver.xbmc.builtin.dim/resources/language/resource.language.de_de/strings.po
index b87b946660..965dd0d6a4 100644
--- a/addons/screensaver.xbmc.builtin.dim/resources/language/resource.language.de_de/strings.po
+++ b/addons/screensaver.xbmc.builtin.dim/resources/language/resource.language.de_de/strings.po
@@ -5,21 +5,21 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2021-07-11 07:17+0000\n"
-"Last-Translator: Kai Sommerfeld <kai.sommerfeld@gmx.com>\n"
+"PO-Revision-Date: 2023-11-07 09:09+0000\n"
+"Last-Translator: Stefan <goleo@posteo.de>\n"
"Language-Team: German <https://kodi.weblate.cloud/projects/kodi-core/screensaver-xbmc-builtin-dim/de_de/>\n"
"Language: de_de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.7.1\n"
+"X-Generator: Weblate 5.1\n"
msgctxt "Addon Summary"
msgid "Screensaver that dims your screen"
-msgstr "Bildschirmschoner zum Abdunkeln des Bildschirmes"
+msgstr "Bildschirmschoner zum Abdunkeln des Bildschirms"
msgctxt "Addon Description"
msgid "The Dim screensaver is a simple screensaver that will dim (fade out) your screen to a setable value between 20 and 100% ."
diff --git a/addons/skin.estouchy/addon.xml b/addons/skin.estouchy/addon.xml
index d70635632c..9b85890ee0 100644
--- a/addons/skin.estouchy/addon.xml
+++ b/addons/skin.estouchy/addon.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<addon id="skin.estouchy" version="3.0.8" name="Estouchy" provider-name="Team Kodi">
+<addon id="skin.estouchy" version="4.0.0" name="Estouchy" provider-name="Team Kodi">
<requires>
- <import addon="xbmc.gui" version="5.16.0"/>
+ <import addon="xbmc.gui" version="5.17.0"/>
</requires>
<extension point="xbmc.gui.skin" debugging="false">
<res width="1280" height="960" aspect="4:3" folder="xml"/>
diff --git a/addons/skin.estouchy/language/resource.language.en_us/strings.po b/addons/skin.estouchy/language/resource.language.en_us/strings.po
index 2e71a54240..1f6a3c59e0 100644
--- a/addons/skin.estouchy/language/resource.language.en_us/strings.po
+++ b/addons/skin.estouchy/language/resource.language.en_us/strings.po
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-07-01 08:07+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: English (United States) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estouchy/en_us/>\n"
"Language: en_us\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.13\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Skin for touchscreen devices"
@@ -81,7 +81,7 @@ msgstr ""
#. Label to show player info page
msgctxt "#31018"
msgid "Player"
-msgstr ""
+msgstr "Player"
#. Label to show video decoder name
msgctxt "#31019"
@@ -346,7 +346,7 @@ msgstr "Show deleted"
#. Label to show the video codec name
msgctxt "#31600"
msgid "Video codec"
-msgstr ""
+msgstr "Video codec"
#. Label to show the video resolution
msgctxt "#31601"
diff --git a/addons/skin.estouchy/language/resource.language.hu_hu/strings.po b/addons/skin.estouchy/language/resource.language.hu_hu/strings.po
index 758f9020d6..160be92857 100644
--- a/addons/skin.estouchy/language/resource.language.hu_hu/strings.po
+++ b/addons/skin.estouchy/language/resource.language.hu_hu/strings.po
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-03 15:42+0000\n"
+"PO-Revision-Date: 2023-09-10 02:41+0000\n"
"Last-Translator: Frodo19 <bilbohu@gmail.com>\n"
"Language-Team: Hungarian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estouchy/hu_hu/>\n"
"Language: hu_hu\n"
@@ -164,7 +164,7 @@ msgstr "Játszási lista mentése"
msgctxt "#31057"
msgid "Close playlist"
-msgstr "Lejátszási lista törlése"
+msgstr "Lejátszási lista bezárása"
msgctxt "#31058"
msgid "System music files"
diff --git a/addons/skin.estouchy/language/resource.language.sk_sk/strings.po b/addons/skin.estouchy/language/resource.language.sk_sk/strings.po
index 579bc0d722..b941bd61a8 100644
--- a/addons/skin.estouchy/language/resource.language.sk_sk/strings.po
+++ b/addons/skin.estouchy/language/resource.language.sk_sk/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-07-01 08:26+0000\n"
-"Last-Translator: Christian Gade <gade@kodi.tv>\n"
+"PO-Revision-Date: 2023-09-14 18:32+0000\n"
+"Last-Translator: Jose Riha <jose1711@gmail.com>\n"
"Language-Team: Slovak <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estouchy/sk_sk/>\n"
"Language: sk_sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
-"X-Generator: Weblate 4.13\n"
+"X-Generator: Weblate 5.0.1\n"
msgctxt "Addon Summary"
msgid "Skin for touchscreen devices"
@@ -361,7 +361,7 @@ msgstr "Video aspekt"
#. Label to show the video bitrate
msgctxt "#31603"
msgid "Video bitrate"
-msgstr ""
+msgstr "Prenosová rýchlosť videa"
#. Label to show the audio codec name
msgctxt "#31604"
@@ -376,7 +376,7 @@ msgstr "Audio kanály"
#. Label to show the audio bitrate
msgctxt "#31606"
msgid "Audio bitrate"
-msgstr ""
+msgstr "Prenosová rýchlosť zvuku"
#. Label to show the screen resolution
msgctxt "#31607"
@@ -386,7 +386,7 @@ msgstr "Rozlíšenie obrazovky"
#. Label to show the system rendering speed
msgctxt "#31608"
msgid "System rendering speed"
-msgstr ""
+msgstr "Rýchlosť vykresľovania systému"
#. Label to show the system CPU usage
msgctxt "#31609"
diff --git a/addons/skin.estouchy/language/resource.language.sv_se/strings.po b/addons/skin.estouchy/language/resource.language.sv_se/strings.po
index a3c28bf2cf..6caa7f29be 100644
--- a/addons/skin.estouchy/language/resource.language.sv_se/strings.po
+++ b/addons/skin.estouchy/language/resource.language.sv_se/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-26 16:11+0000\n"
-"Last-Translator: Andreas Bolin <bolin.andreas@gmail.com>\n"
+"PO-Revision-Date: 2023-10-22 16:54+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Swedish <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estouchy/sv_se/>\n"
"Language: sv_se\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.0.2\n"
msgctxt "Addon Summary"
msgid "Skin for touchscreen devices"
@@ -76,7 +76,7 @@ msgstr "Album"
#. Label to show PVR info page
msgctxt "#31017"
msgid "PVR"
-msgstr ""
+msgstr "PVR"
#. Label to show player info page
msgctxt "#31018"
@@ -116,7 +116,7 @@ msgstr "Uppdaterad:"
msgctxt "#31040"
msgid "Select + X"
-msgstr ""
+msgstr "Select + X"
msgctxt "#31041"
msgid "Select + B"
@@ -124,7 +124,7 @@ msgstr ""
msgctxt "#31042"
msgid "Select + Start"
-msgstr ""
+msgstr "Select + Start"
msgctxt "#31043"
msgid "PAUSED"
diff --git a/addons/skin.estouchy/language/resource.language.vi_vn/strings.po b/addons/skin.estouchy/language/resource.language.vi_vn/strings.po
index 9065bff85a..90bfdd7ede 100644
--- a/addons/skin.estouchy/language/resource.language.vi_vn/strings.po
+++ b/addons/skin.estouchy/language/resource.language.vi_vn/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-14 00:21+0000\n"
-"Last-Translator: Nguyễn Trung Hậu <trunghau1712@gmail.com>\n"
+"PO-Revision-Date: 2023-10-22 16:54+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Vietnamese <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estouchy/vi_vn/>\n"
"Language: vi_vn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.0.2\n"
msgctxt "Addon Summary"
msgid "Skin for touchscreen devices"
@@ -76,7 +76,7 @@ msgstr "Album"
#. Label to show PVR info page
msgctxt "#31017"
msgid "PVR"
-msgstr ""
+msgstr "PVR"
#. Label to show player info page
msgctxt "#31018"
@@ -338,7 +338,7 @@ msgstr "Xoá nhóm"
msgctxt "#31564"
msgid "Show hidden"
-msgstr ""
+msgstr "Hiển thị ẩn"
msgctxt "#31565"
msgid "Show deleted"
diff --git a/addons/skin.estouchy/media/DefaultFavourites.png b/addons/skin.estouchy/media/DefaultFavourites.png
new file mode 100644
index 0000000000..62c3dd5d74
--- /dev/null
+++ b/addons/skin.estouchy/media/DefaultFavourites.png
Binary files differ
diff --git a/addons/skin.estouchy/media/flagging/video/theora.png b/addons/skin.estouchy/media/flagging/video/theora.png
new file mode 100644
index 0000000000..8f1af4bb94
--- /dev/null
+++ b/addons/skin.estouchy/media/flagging/video/theora.png
Binary files differ
diff --git a/addons/skin.estouchy/media/icon_breadcrumb_favourites.png b/addons/skin.estouchy/media/icon_breadcrumb_favourites.png
new file mode 100644
index 0000000000..eb183e8f9b
--- /dev/null
+++ b/addons/skin.estouchy/media/icon_breadcrumb_favourites.png
Binary files differ
diff --git a/addons/skin.estouchy/xml/DialogFavourites.xml b/addons/skin.estouchy/xml/DialogFavourites.xml
deleted file mode 100644
index 8da89f7fbe..0000000000
--- a/addons/skin.estouchy/xml/DialogFavourites.xml
+++ /dev/null
@@ -1,148 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<window>
- <defaultcontrol always="true">450</defaultcontrol>
- <include>16x9_xPos_Relocation</include>
- <include>Window_OpenClose_Animation_Zoom</include>
- <coordinates>
- <posx>80</posx>
- <posy>90</posy>
- </coordinates>
- <controls>
- <include>BehindDialogFadeOut</include>
- <control type="image">
- <posx>0</posx>
- <posy>0</posy>
- <width>1120</width>
- <height>60</height>
- <texture border="5">dialog_header.png</texture>
- </control>
- <control type="image">
- <posx>0</posx>
- <posy>60</posy>
- <width>1120</width>
- <height>720</height>
- <texture>dialog_back.png</texture>
- </control>
- <control type="label">
- <description>header label</description>
- <posx>20</posx>
- <posy>0</posy>
- <include>WindowTitleCommons</include>
- <width>1000</width>
- <label>$LOCALIZE[1036]</label>
- </control>
- <control type="group">
- <posx>1050</posx>
- <posy>0</posy>
- <include>DialogCloseButtonCommons</include>
- </control>
- <control type="image">
- <posx>0</posx>
- <posy>60</posy>
- <width>1070</width>
- <height>720</height>
- <texture colordiffuse="40000000">panel.png</texture>
- </control>
- <control type="panel" id="450">
- <posx>1</posx>
- <posy>60</posy>
- <width>1068</width>
- <height>720</height>
- <onup>450</onup>
- <onleft>450</onleft>
- <onright>450</onright>
- <ondown>450</ondown>
- <pagecontrol>60</pagecontrol>
- <scrolltime>200</scrolltime>
- <itemlayout width="267" height="360">
- <control type="image">
- <posx>10</posx>
- <posy>10</posy>
- <width>247</width>
- <height>340</height>
- <texture colordiffuse="50000000">black.png</texture>
- </control>
- <control type="image">
- <posx>10</posx>
- <posy>10</posy>
- <width>247</width>
- <height>277</height>
- <aspectratio align="center">keep</aspectratio>
- <texture>$INFO[ListItem.Icon]</texture>
- </control>
- <control type="image">
- <posx>10</posx>
- <posy>287</posy>
- <width>247</width>
- <height>63</height>
- <texture colordiffuse="50000000">black.png</texture>
- </control>
- <control type="textbox">
- <posx>10</posx>
- <posy>287</posy>
- <width>247</width>
- <height>63</height>
- <font>font20</font>
- <align>center</align>
- <aligny>center</aligny>
- <selectedcolor>selected</selectedcolor>
- <label>$INFO[ListItem.Label]</label>
- </control>
- </itemlayout>
- <focusedlayout width="267" height="360">
- <control type="image">
- <posx>10</posx>
- <posy>10</posy>
- <width>247</width>
- <height>340</height>
- <texture>list_focus.png</texture>
- </control>
- <control type="image">
- <posx>10</posx>
- <posy>10</posy>
- <width>247</width>
- <height>277</height>
- <aspectratio align="center">keep</aspectratio>
- <texture>$INFO[ListItem.Icon]</texture>
- </control>
- <control type="image">
- <posx>10</posx>
- <posy>287</posy>
- <width>247</width>
- <height>63</height>
- <texture colordiffuse="50000000">black.png</texture>
- </control>
- <control type="textbox">
- <posx>10</posx>
- <posy>287</posy>
- <width>247</width>
- <height>63</height>
- <font>font20</font>
- <align>center</align>
- <aligny>center</aligny>
- <selectedcolor>selected</selectedcolor>
- <label>$INFO[ListItem.Label]</label>
- </control>
- </focusedlayout>
- </control>
- <control type="scrollbar" id="60">
- <posx>1082</posx>
- <posy>90</posy>
- <width>26</width>
- <height>660</height>
- <texturesliderbackground colordiffuse="30FFFFFF">white.png</texturesliderbackground>
- <texturesliderbar colordiffuse="grey">white.png</texturesliderbar>
- <texturesliderbarfocus colordiffuse="blue">white.png</texturesliderbarfocus>
- <textureslidernib>blank.png</textureslidernib>
- <textureslidernibfocus>blank.png</textureslidernibfocus>
- <onleft>3</onleft>
- <onright>3</onright>
- <ondown>60</ondown>
- <onup>60</onup>
- <showonepage>false</showonepage>
- <orientation>vertical</orientation>
- <animation effect="fade" time="150">Visible</animation>
- <animation effect="fade" time="150">Hidden</animation>
- </control>
- </controls>
-</window>
diff --git a/addons/skin.estouchy/xml/FileManager.xml b/addons/skin.estouchy/xml/FileManager.xml
index 815e747b4e..b33f5c9b65 100644
--- a/addons/skin.estouchy/xml/FileManager.xml
+++ b/addons/skin.estouchy/xml/FileManager.xml
@@ -281,7 +281,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_button_favourites.png</icon>
</item>
</content>
diff --git a/addons/skin.estouchy/xml/Includes.xml b/addons/skin.estouchy/xml/Includes.xml
index bb168e958a..edd607f544 100644
--- a/addons/skin.estouchy/xml/Includes.xml
+++ b/addons/skin.estouchy/xml/Includes.xml
@@ -64,6 +64,7 @@
<value condition="Window.IsVisible(Pictures)">icon_breadcrumb_pictures.png</value>
<value condition="Window.IsVisible(Programs) | Window.IsVisible(AddonBrowser)">icon_breadcrumb_addons.png</value>
<value condition="Window.IsVisible(TVChannels) | Window.IsVisible(RadioChannels) | Window.IsVisible(TVGuide) | Window.IsVisible(RadioGuide) | Window.IsVisible(TVRecordings) | Window.IsVisible(RadioRecordings) | Window.IsVisible(TVSearch) | Window.IsVisible(RadioSearch) | Window.IsVisible(TVTimers) | Window.IsVisible(RadioTimers) | Window.IsVisible(TVTimerRules) | Window.IsVisible(RadioTimerRules)">icon_breadcrumb_tv.png</value>
+ <value condition="Window.IsVisible(FavouritesBrowser)">icon_breadcrumb_favourites.png</value>
<value>icon_breadcrumb_settings.png</value>
</variable>
<variable name="SeekLabel">
@@ -948,7 +949,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_menu_favourites.png</icon>
<thumb></thumb>
<visible>!Skin.HasSetting(HideHomeButtonFavourites)</visible>
diff --git a/addons/skin.estouchy/xml/MyFavourites.xml b/addons/skin.estouchy/xml/MyFavourites.xml
new file mode 100644
index 0000000000..80fa3b8546
--- /dev/null
+++ b/addons/skin.estouchy/xml/MyFavourites.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<window>
+ <defaultcontrol always="true">50</defaultcontrol>
+ <views>50,500</views>
+ <onunload>ClearProperty(PopupMenuVisible,Home)</onunload>
+ <controls>
+ <include>CommonBackground</include>
+ <include>SideMenu</include>
+ <include>Header</include>
+ <include>CommonNowPlaying</include>
+ <include>Footer</include>
+ <control type="label">
+ <description>Window label</description>
+ <include>MediaWindowTitleCommons</include>
+ <label>$LOCALIZE[1036]</label>
+ </control>
+ <control type="group">
+ <include>Window_OpenClose_Animation_Zoom</include>
+ <include content="Thumbnail" condition="String.IsEqual(Skin.AspectRatio,16:9)"> <!-- view id = 50 -->
+ <param name="panel-width" value="1530"/>
+ <param name="layout-width" value="306"/>
+ </include>
+ <include content="Thumbnail" condition="String.IsEqual(Skin.AspectRatio,4:3)"> <!-- view id = 50 -->
+ <param name="panel-width" value="1090"/>
+ <param name="layout-width" value="363"/>
+ </include>
+ <include content="List" condition="String.IsEqual(Skin.AspectRatio,16:9)"> <!-- view id = 500 -->
+ <param name="panel-width" value="1530"/>
+ <param name="label-width" value="1230"/>
+ <param name="label2-posx" value="1360r"/>
+ </include>
+ <include content="List" condition="String.IsEqual(Skin.AspectRatio,4:3)"> <!-- view id = 500 -->
+ <param name="panel-width" value="1090"/>
+ <param name="label-width" value="790"/>
+ <param name="label2-posx" value="920r"/>
+ </include>
+ </control>
+ <include>ScrollBarCommons</include>
+ <include>BottomMenu</include>
+ <include content="PopupMenu">
+ <param name="panel-posy">400</param>
+ <param name="panel-height">160</param>
+ </include>
+ <include>ScrollOffsetLabel</include>
+ </controls>
+</window>
diff --git a/addons/skin.estouchy/xml/MyWeather.xml b/addons/skin.estouchy/xml/MyWeather.xml
index f097c82361..17b2e607cd 100644
--- a/addons/skin.estouchy/xml/MyWeather.xml
+++ b/addons/skin.estouchy/xml/MyWeather.xml
@@ -519,7 +519,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_button_favourites.png</icon>
</item>
</content>
diff --git a/addons/skin.estouchy/xml/Settings.xml b/addons/skin.estouchy/xml/Settings.xml
index c67ca45bf5..870e1aa9d1 100644
--- a/addons/skin.estouchy/xml/Settings.xml
+++ b/addons/skin.estouchy/xml/Settings.xml
@@ -45,7 +45,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_button_favourites.png</icon>
</item>
<item>
diff --git a/addons/skin.estouchy/xml/SettingsCategory.xml b/addons/skin.estouchy/xml/SettingsCategory.xml
index beb3f47279..12cea16900 100644
--- a/addons/skin.estouchy/xml/SettingsCategory.xml
+++ b/addons/skin.estouchy/xml/SettingsCategory.xml
@@ -153,7 +153,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_button_favourites.png</icon>
</item>
<item>
diff --git a/addons/skin.estouchy/xml/SettingsProfile.xml b/addons/skin.estouchy/xml/SettingsProfile.xml
index 70a461b210..c85b453797 100644
--- a/addons/skin.estouchy/xml/SettingsProfile.xml
+++ b/addons/skin.estouchy/xml/SettingsProfile.xml
@@ -85,7 +85,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_button_favourites.png</icon>
</item>
</content>
diff --git a/addons/skin.estouchy/xml/SkinSettings.xml b/addons/skin.estouchy/xml/SkinSettings.xml
index 446a892a71..060304cb48 100644
--- a/addons/skin.estouchy/xml/SkinSettings.xml
+++ b/addons/skin.estouchy/xml/SkinSettings.xml
@@ -99,7 +99,7 @@
</item>
<item>
<label>1036</label>
- <onclick>ActivateWindow(Favourites)</onclick>
+ <onclick>ActivateWindow(FavouritesBrowser)</onclick>
<icon>icon_button_favourites.png</icon>
</item>
</content>
diff --git a/addons/skin.estouchy/xml/ViewsList.xml b/addons/skin.estouchy/xml/ViewsList.xml
index fbeedd24da..0aa95a4b33 100644
--- a/addons/skin.estouchy/xml/ViewsList.xml
+++ b/addons/skin.estouchy/xml/ViewsList.xml
@@ -51,7 +51,7 @@
<height>62</height>
<texture background="true">$INFO[Listitem.Icon]</texture>
<aspectratio>keep</aspectratio>
- <visible>[Container.Content() + !Window.IsVisible(Pictures)] | Container.Content(Files) | Container.Content(Games) | Container.Content(Genres) | Container.Content(Years) | Container.Content(Directors) | Container.Content(Studios) | Container.Content(Countries) | Container.Content(Tags) | [Container.Content(Addons) + ListItem.IsFolder] | [Window.IsVisible(Pictures) + String.IsEmpty(Container.FolderPath)]</visible>
+ <visible>[Container.Content() + !Window.IsVisible(Pictures)] | Container.Content(Files) | Container.Content(Games) | Container.Content(Genres) | Container.Content(Years) | Container.Content(Directors) | Container.Content(Studios) | Container.Content(Countries) | Container.Content(Tags) | Container.Content(Favourites) | [Container.Content(Addons) + ListItem.IsFolder] | [Window.IsVisible(Pictures) + String.IsEmpty(Container.FolderPath)]</visible>
</control>
<control type="label">
<posx>75</posx>
@@ -193,7 +193,7 @@
<height>62</height>
<texture background="true">$INFO[Listitem.Icon]</texture>
<aspectratio>keep</aspectratio>
- <visible>[Container.Content() + !Window.IsVisible(Pictures)] | Container.Content(Files) | Container.Content(Games) | Container.Content(Genres) | Container.Content(Years) | Container.Content(Directors) | Container.Content(Studios) | Container.Content(Countries) | Container.Content(Tags) | [Container.Content(Addons) + ListItem.IsFolder] | [Window.IsVisible(Pictures) + String.IsEmpty(Container.FolderPath)]</visible>
+ <visible>[Container.Content() + !Window.IsVisible(Pictures)] | Container.Content(Files) | Container.Content(Games) | Container.Content(Genres) | Container.Content(Years) | Container.Content(Directors) | Container.Content(Studios) | Container.Content(Countries) | Container.Content(Tags) | Container.Content(Favourites) | [Container.Content(Addons) + ListItem.IsFolder] | [Window.IsVisible(Pictures) + String.IsEmpty(Container.FolderPath)]</visible>
</control>
<control type="label">
<posx>75</posx>
diff --git a/addons/skin.estouchy/xml/ViewsThumbnail.xml b/addons/skin.estouchy/xml/ViewsThumbnail.xml
index 99a40208d4..708ec6c799 100644
--- a/addons/skin.estouchy/xml/ViewsThumbnail.xml
+++ b/addons/skin.estouchy/xml/ViewsThumbnail.xml
@@ -17,7 +17,7 @@
<animation effect="slide" start="0,0" end="-1,0" time="0" condition="String.IsEqual(Skin.AspectRatio,4:3)">Conditional</animation>
<animation effect="slide" start="0,0" end="-1,0" time="0" condition="String.IsEqual(Skin.AspectRatio,16:9) + Container.Content(Episodes)">Conditional</animation>
<animation effect="slide" start="0,0" end="28,0" time="0" condition="String.IsEqual(Skin.AspectRatio,4:3) + Container.Content(Episodes)">Conditional</animation>
- <itemlayout condition="!Container.Content(Movies) + !Container.Content(Seasons) + !Container.Content(Episodes) + !Container.Content(TVShows) + !Container.Content(MusicVideos) + !Container.Content(Videos)" height="250" width="218">
+ <itemlayout condition="!Container.Content(Movies) + !Container.Content(Seasons) + !Container.Content(Episodes) + !Container.Content(TVShows) + !Container.Content(MusicVideos) + !Container.Content(Videos) + !Container.Content(Favourites)" height="250" width="218">
<control type="image">
<posx>5</posx>
<posy>0</posy>
@@ -89,7 +89,7 @@
<texture>$INFO[ListItem.Overlay]</texture>
</control>
</itemlayout>
- <focusedlayout condition="!Container.Content(Movies) + !Container.Content(Seasons) + !Container.Content(Episodes) + !Container.Content(TVShows) + !Container.Content(MusicVideos) + !Container.Content(Videos)" height="250" width="218">
+ <focusedlayout condition="!Container.Content(Movies) + !Container.Content(Seasons) + !Container.Content(Episodes) + !Container.Content(TVShows) + !Container.Content(MusicVideos) + !Container.Content(Videos) + !Container.Content(Favourites)" height="250" width="218">
<control type="image">
<posx>5</posx>
<posy>0</posy>
@@ -422,6 +422,76 @@
<visible>!ListItem.IsResumable</visible>
</control>
</focusedlayout>
+ <itemlayout condition="Container.Content(Favourites)" width="267" height="360">
+ <control type="image">
+ <posx>10</posx>
+ <posy>10</posy>
+ <width>247</width>
+ <height>340</height>
+ <texture colordiffuse="50000000">black.png</texture>
+ </control>
+ <control type="image">
+ <posx>10</posx>
+ <posy>10</posy>
+ <width>247</width>
+ <height>277</height>
+ <aspectratio align="center">keep</aspectratio>
+ <texture>$INFO[ListItem.Icon]</texture>
+ </control>
+ <control type="image">
+ <posx>10</posx>
+ <posy>287</posy>
+ <width>247</width>
+ <height>63</height>
+ <texture colordiffuse="50000000">black.png</texture>
+ </control>
+ <control type="textbox">
+ <posx>10</posx>
+ <posy>287</posy>
+ <width>247</width>
+ <height>63</height>
+ <font>font20</font>
+ <align>center</align>
+ <aligny>center</aligny>
+ <selectedcolor>selected</selectedcolor>
+ <label>$INFO[ListItem.Label]</label>
+ </control>
+ </itemlayout>
+ <focusedlayout condition="Container.Content(Favourites)" width="267" height="360">
+ <control type="image">
+ <posx>10</posx>
+ <posy>10</posy>
+ <width>247</width>
+ <height>340</height>
+ <texture>list_focus.png</texture>
+ </control>
+ <control type="image">
+ <posx>10</posx>
+ <posy>10</posy>
+ <width>247</width>
+ <height>277</height>
+ <aspectratio align="center">keep</aspectratio>
+ <texture>$INFO[ListItem.Icon]</texture>
+ </control>
+ <control type="image">
+ <posx>10</posx>
+ <posy>287</posy>
+ <width>247</width>
+ <height>63</height>
+ <texture colordiffuse="50000000">black.png</texture>
+ </control>
+ <control type="textbox">
+ <posx>10</posx>
+ <posy>287</posy>
+ <width>247</width>
+ <height>63</height>
+ <font>font20</font>
+ <align>center</align>
+ <aligny>center</aligny>
+ <selectedcolor>selected</selectedcolor>
+ <label>$INFO[ListItem.Label]</label>
+ </control>
+ </focusedlayout>
</control>
</include>
</includes>
diff --git a/addons/skin.estuary/addon.xml b/addons/skin.estuary/addon.xml
index 07f4d0cf7c..4341d3324a 100644
--- a/addons/skin.estuary/addon.xml
+++ b/addons/skin.estuary/addon.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<addon id="skin.estuary" version="3.0.10" name="Estuary" provider-name="phil65, Ichabod Fletchman">
+<addon id="skin.estuary" version="4.0.0" name="Estuary" provider-name="phil65, Ichabod Fletchman">
<requires>
- <import addon="xbmc.gui" version="5.16.0"/>
+ <import addon="xbmc.gui" version="5.17.0"/>
</requires>
<extension point="xbmc.gui.skin" debugging="false">
<res width="1920" height="1440" aspect="4:3" default="false" folder="xml" />
diff --git a/addons/skin.estuary/colors/brown.xml b/addons/skin.estuary/colors/brown.xml
index 845d5685b0..e75f74dafc 100644
--- a/addons/skin.estuary/colors/brown.xml
+++ b/addons/skin.estuary/colors/brown.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33738627</color>
<color name="dialog_tint">FF282524</color>
<color name="button_focus">FF83513F</color>
+ <color name="button_alt_focus">8083513F</color>
<color name="selected">FFFF4400</color>
-</colors> \ No newline at end of file
+</colors>
diff --git a/addons/skin.estuary/colors/charcoal.xml b/addons/skin.estuary/colors/charcoal.xml
index a77ccc7a22..1a2a778ac5 100644
--- a/addons/skin.estuary/colors/charcoal.xml
+++ b/addons/skin.estuary/colors/charcoal.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">4D808080</color>
<color name="dialog_tint">FF262626</color>
<color name="button_focus">FFA2A2A2</color>
+ <color name="button_alt_focus">80A2A2A2</color>
<color name="selected">FF11E7B1</color>
</colors>
diff --git a/addons/skin.estuary/colors/chartreuse.xml b/addons/skin.estuary/colors/chartreuse.xml
index b05615cc12..77bf016ae8 100644
--- a/addons/skin.estuary/colors/chartreuse.xml
+++ b/addons/skin.estuary/colors/chartreuse.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">3398FF05</color>
<color name="dialog_tint">FF202920</color>
<color name="button_focus">FF4FAF00</color>
+ <color name="button_alt_focus">804FAF00</color>
<color name="selected">FF24C6C9</color>
</colors>
diff --git a/addons/skin.estuary/colors/concrete.xml b/addons/skin.estuary/colors/concrete.xml
index 81bf41a3c5..366c7d8ffd 100644
--- a/addons/skin.estuary/colors/concrete.xml
+++ b/addons/skin.estuary/colors/concrete.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33DA845C</color>
<color name="dialog_tint">FF242728</color>
<color name="button_focus">FF607D8B</color>
+ <color name="button_alt_focus">80607D8B</color>
<color name="selected">FFFF8C00</color>
</colors>
diff --git a/addons/skin.estuary/colors/defaults.xml b/addons/skin.estuary/colors/defaults.xml
index bf5d7b166d..2ca70020d5 100644
--- a/addons/skin.estuary/colors/defaults.xml
+++ b/addons/skin.estuary/colors/defaults.xml
@@ -12,6 +12,7 @@
<color name="blue">FF7ACAFE</color>
<color name="red">FFCE4421</color>
<color name="button_focus">FF12A0C7</color>
+ <color name="button_alt_focus">8012A0C7</color>
<color name="text_shadow">22000000</color>
<color name="border_alpha">60FFFFFF</color>
<color name="disabled">40FFFFFF</color>
diff --git a/addons/skin.estuary/colors/gold.xml b/addons/skin.estuary/colors/gold.xml
index 84d50ee7ee..7a2399e8ed 100644
--- a/addons/skin.estuary/colors/gold.xml
+++ b/addons/skin.estuary/colors/gold.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33FFFF00</color>
<color name="dialog_tint">FF2B2B22</color>
<color name="button_focus">FFCFA700</color>
+ <color name="button_alt_focus">80CFA700</color>
<color name="selected">FFFFF000</color>
</colors>
diff --git a/addons/skin.estuary/colors/green.xml b/addons/skin.estuary/colors/green.xml
index 073f128e11..9aa86d5454 100644
--- a/addons/skin.estuary/colors/green.xml
+++ b/addons/skin.estuary/colors/green.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">3300A3CC</color>
<color name="dialog_tint">FF1F2722</color>
<color name="button_focus">FF24CA7A</color>
+ <color name="button_alt_focus">8024CA7A</color>
<color name="selected">FF14D519</color>
</colors>
diff --git a/addons/skin.estuary/colors/maroon.xml b/addons/skin.estuary/colors/maroon.xml
index e4dbcc5e87..46ad34949f 100644
--- a/addons/skin.estuary/colors/maroon.xml
+++ b/addons/skin.estuary/colors/maroon.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33DB2C83</color>
<color name="dialog_tint">FF262020</color>
<color name="button_focus">FFC40300</color>
+ <color name="button_alt_focus">80C40300</color>
<color name="selected">FF24C6C9</color>
</colors>
diff --git a/addons/skin.estuary/colors/midnight.xml b/addons/skin.estuary/colors/midnight.xml
index 7aba048bd9..e35d0bf299 100644
--- a/addons/skin.estuary/colors/midnight.xml
+++ b/addons/skin.estuary/colors/midnight.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">334F4F9E</color>
<color name="dialog_tint">FF181B1E</color>
<color name="button_focus">FF2866A4</color>
+ <color name="button_alt_focus">802866A4</color>
<color name="selected">FF5BE5EE</color>
</colors>
diff --git a/addons/skin.estuary/colors/orange.xml b/addons/skin.estuary/colors/orange.xml
index 501a1facce..f5eadf8d0a 100644
--- a/addons/skin.estuary/colors/orange.xml
+++ b/addons/skin.estuary/colors/orange.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33BDAE12</color>
<color name="dialog_tint">FF2B2621</color>
<color name="button_focus">FFFF9800</color>
+ <color name="button_alt_focus">80FF9800</color>
<color name="selected">FFFFF100</color>
</colors>
diff --git a/addons/skin.estuary/colors/pink.xml b/addons/skin.estuary/colors/pink.xml
index 2dfd136b32..aa26c3ff30 100644
--- a/addons/skin.estuary/colors/pink.xml
+++ b/addons/skin.estuary/colors/pink.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">3363BCE9</color>
<color name="dialog_tint">FF2B2225</color>
<color name="button_focus">FFE91E63</color>
+ <color name="button_alt_focus">80E91E63</color>
<color name="selected">FF94D800</color>
</colors>
diff --git a/addons/skin.estuary/colors/rose.xml b/addons/skin.estuary/colors/rose.xml
index 62b8672f89..f4f2f5d0e9 100644
--- a/addons/skin.estuary/colors/rose.xml
+++ b/addons/skin.estuary/colors/rose.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33FFB3D7</color>
<color name="dialog_tint">FF251F24</color>
<color name="button_focus">FFFF8EC4</color>
+ <color name="button_alt_focus">80FF8EC4</color>
<color name="selected">FFFF0261</color>
</colors>
diff --git a/addons/skin.estuary/colors/teal.xml b/addons/skin.estuary/colors/teal.xml
index a7d408c287..6bca4c1406 100644
--- a/addons/skin.estuary/colors/teal.xml
+++ b/addons/skin.estuary/colors/teal.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33F07942</color>
<color name="dialog_tint">FF222A2A</color>
<color name="button_focus">FF009688</color>
+ <color name="button_alt_focus">80009688</color>
<color name="selected">FFC67F03</color>
</colors>
diff --git a/addons/skin.estuary/colors/violet.xml b/addons/skin.estuary/colors/violet.xml
index e9ec02a79e..b6b1dd3f74 100644
--- a/addons/skin.estuary/colors/violet.xml
+++ b/addons/skin.estuary/colors/violet.xml
@@ -4,5 +4,6 @@
<color name="secondary_background">33FFB3D7</color>
<color name="dialog_tint">FF27222A</color>
<color name="button_focus">FFC050FF</color>
+ <color name="button_alt_focus">80C050FF</color>
<color name="selected">FFFF0054</color>
</colors>
diff --git a/addons/skin.estuary/language/resource.language.af_za/strings.po b/addons/skin.estuary/language/resource.language.af_za/strings.po
index 3c5d45020c..e17f7f1462 100644
--- a/addons/skin.estuary/language/resource.language.af_za/strings.po
+++ b/addons/skin.estuary/language/resource.language.af_za/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Afrikaans (South Africa) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/af_za/>\n"
"Language: af_za\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Outomatiese Inteken met begin"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Kies + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Kies + Begin"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Hoof kieslys items"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Stelsel"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Ekstras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Kies + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Kies + Begin"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR info"
diff --git a/addons/skin.estuary/language/resource.language.am_et/strings.po b/addons/skin.estuary/language/resource.language.am_et/strings.po
index 4587a8874b..8bad3b388c 100644
--- a/addons/skin.estuary/language/resource.language.am_et/strings.po
+++ b/addons/skin.estuary/language/resource.language.am_et/strings.po
@@ -250,14 +250,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -746,6 +738,26 @@ msgctxt "#31611"
msgid "System"
msgstr "ስርአት"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "የ PVR መረጃ"
diff --git a/addons/skin.estuary/language/resource.language.ar_sa/strings.po b/addons/skin.estuary/language/resource.language.ar_sa/strings.po
index d514b64075..37eddb7d75 100644
--- a/addons/skin.estuary/language/resource.language.ar_sa/strings.po
+++ b/addons/skin.estuary/language/resource.language.ar_sa/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: 2023-01-29 21:15+0000\n"
"Last-Translator: psp2111-ADSLGATE <psp100000@hotmail.com>\n"
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "تسجيل الدخول التلقائي عند بدء التشغيل"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "عناصر القائمة الرئيسية"
@@ -752,6 +744,34 @@ msgctxt "#31611"
msgid "System"
msgstr "النظام"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "معلومات PVR"
diff --git a/addons/skin.estuary/language/resource.language.ast_es/strings.po b/addons/skin.estuary/language/resource.language.ast_es/strings.po
index a4bdcd785a..d5f3c50898 100644
--- a/addons/skin.estuary/language/resource.language.ast_es/strings.po
+++ b/addons/skin.estuary/language/resource.language.ast_es/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr "Media"
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.az_az/strings.po b/addons/skin.estuary/language/resource.language.az_az/strings.po
index e2a1b1d4e1..aea6b8847d 100644
--- a/addons/skin.estuary/language/resource.language.az_az/strings.po
+++ b/addons/skin.estuary/language/resource.language.az_az/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr "Media"
msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.be_by/strings.po b/addons/skin.estuary/language/resource.language.be_by/strings.po
index cfceed63e0..f527aa986e 100644
--- a/addons/skin.estuary/language/resource.language.be_by/strings.po
+++ b/addons/skin.estuary/language/resource.language.be_by/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Аўтаматычна ўваходзіць у сістэму падчас запуску"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Элементы галоўнага меню"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Сістэма"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Інфармацыя PVR"
diff --git a/addons/skin.estuary/language/resource.language.bg_bg/strings.po b/addons/skin.estuary/language/resource.language.bg_bg/strings.po
index bdefc206ef..e4c78ee013 100644
--- a/addons/skin.estuary/language/resource.language.bg_bg/strings.po
+++ b/addons/skin.estuary/language/resource.language.bg_bg/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Bulgarian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/bg_bg/>\n"
"Language: bg_bg\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Автоматично влизане след стартиране"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Елементи в менюто"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Системни"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Екстри"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Информация за ПВР"
diff --git a/addons/skin.estuary/language/resource.language.bs_ba/strings.po b/addons/skin.estuary/language/resource.language.bs_ba/strings.po
index 111cff73a2..a2717a40d1 100644
--- a/addons/skin.estuary/language/resource.language.bs_ba/strings.po
+++ b/addons/skin.estuary/language/resource.language.bs_ba/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.ca_es/strings.po b/addons/skin.estuary/language/resource.language.ca_es/strings.po
index 912cceddd7..1e90762898 100644
--- a/addons/skin.estuary/language/resource.language.ca_es/strings.po
+++ b/addons/skin.estuary/language/resource.language.ca_es/strings.po
@@ -5,17 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-08-15 22:36+0000\n"
-"Last-Translator: Xean <xeanhort007@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Catalan (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/ca_es/>\n"
"Language: ca_es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.13\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Autenticació automàtica a l'inici"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elements menú principal"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extres"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Informació PVR"
diff --git a/addons/skin.estuary/language/resource.language.cs_cz/strings.po b/addons/skin.estuary/language/resource.language.cs_cz/strings.po
index 6d7b8aa13b..b85ee441bf 100644
--- a/addons/skin.estuary/language/resource.language.cs_cz/strings.po
+++ b/addons/skin.estuary/language/resource.language.cs_cz/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-03-21 11:09+0000\n"
-"Last-Translator: Kryštof Černý <cleverline1mc@gmail.com>\n"
+"PO-Revision-Date: 2023-09-06 17:24+0000\n"
+"Last-Translator: HansCR <h.vanek@gmail.com>\n"
"Language-Team: Czech <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/cs_cz/>\n"
"Language: cs_cz\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
-"X-Generator: Weblate 4.11.2\n"
+"X-Generator: Weblate 4.18.2\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -231,11 +231,11 @@ msgstr "Založeno na Arial"
msgctxt "#31054"
msgid "Press [B]Left[/B] to step back, or [B]Right[/B] to step forward"
-msgstr ""
+msgstr "Stiskněte [B]Vlevo[/B] pro krok zpět nebo [B]Vpravo[/B] pro krok vpřed"
msgctxt "#31055"
msgid "Press [B]Right[/B] to frame advance"
-msgstr ""
+msgstr "Stiskněte [B]Vpravo[/B] pro posunutí o snímek vpřed"
msgctxt "#31056"
msgid "Go to playlist"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatické přihlášení po spuštění"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Položky hlavní nabídky"
@@ -496,7 +488,7 @@ msgstr "Zbývá"
msgctxt "#31135"
msgid "Binary"
-msgstr ""
+msgstr "Binární"
msgctxt "#31136"
msgid "Click here to see latest changes..."
@@ -647,37 +639,37 @@ msgstr "Nastavení související s grafikou alb."
#. Label for OSD settings category
msgctxt "#31170"
msgid "On screen display"
-msgstr ""
+msgstr "Nabídka na obrazovce"
#. Helper text for the label of OSD settings category
msgctxt "#31171"
msgid "On screen display (OSD) related settings"
-msgstr ""
+msgstr "Nastavení k nabídce na obrazovce (OSD)"
#. Setting Automatically close video OSD
msgctxt "#31172"
msgid "Automatically close video OSD"
-msgstr ""
+msgstr "Automaticky zavřít OSD u videa"
#. Setting auto close time for video osd
msgctxt "#31173"
msgid "Video OSD autoclose time (seconds)"
-msgstr ""
+msgstr "Čas automatického uzavření OSD u videa (sekundy)"
#. Setting to control what happens when clicking a music album on the home screen
msgctxt "#31174"
msgid "Default select action for albums on the home screen"
-msgstr ""
+msgstr "Výchozí akce pro alba na hlavní obrazovce"
#. Setting to control what happens when clicking a TV show on the home screen
msgctxt "#31175"
msgid "Default select action for TV shows on the home screen"
-msgstr ""
+msgstr "Výchozí akce pro seriály na hlavní obrazovce"
#. Setting to control what happens when clicking a movie set on the home screen
msgctxt "#31176"
msgid "Default select action for movie sets on the home screen"
-msgstr ""
+msgstr "Výchozí akce pro filmy na hlavní obrazovce"
# empty strings from id 31170 to 31599
#. Label to show the video codec name
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Systém"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Informace o PVR"
diff --git a/addons/skin.estuary/language/resource.language.cy_gb/strings.po b/addons/skin.estuary/language/resource.language.cy_gb/strings.po
index 0a90e700fa..d00644595d 100644
--- a/addons/skin.estuary/language/resource.language.cy_gb/strings.po
+++ b/addons/skin.estuary/language/resource.language.cy_gb/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -752,6 +744,26 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Manylion y PVR"
diff --git a/addons/skin.estuary/language/resource.language.da_dk/strings.po b/addons/skin.estuary/language/resource.language.da_dk/strings.po
index 5ab6db23be..dcd0e0b464 100644
--- a/addons/skin.estuary/language/resource.language.da_dk/strings.po
+++ b/addons/skin.estuary/language/resource.language.da_dk/strings.po
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-11-12 17:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Danish <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/da_dk/>\n"
"Language: da_dk\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.14.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatisk login ved opstart"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Hovedmenuens emner"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "Tilføj version"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "Tilføj ekstramateriale"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "Indstil som standard"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Ekstramateriale"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR-information"
diff --git a/addons/skin.estuary/language/resource.language.de_de/strings.po b/addons/skin.estuary/language/resource.language.de_de/strings.po
index 296698cb2c..e928a6a2bf 100644
--- a/addons/skin.estuary/language/resource.language.de_de/strings.po
+++ b/addons/skin.estuary/language/resource.language.de_de/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-02-08 03:30+0000\n"
+"PO-Revision-Date: 2023-11-24 16:07+0000\n"
"Last-Translator: Kai Sommerfeld <ksooo@users.noreply.kodi.weblate.cloud>\n"
"Language-Team: German <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/de_de/>\n"
"Language: de_de\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatische Anmeldung beim Programmstart"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Hauptmenüeinträge"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "Version hinzufügen"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "Extras hinzufügen"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "Als Standard festlegen"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR-Informationen"
diff --git a/addons/skin.estuary/language/resource.language.el_gr/strings.po b/addons/skin.estuary/language/resource.language.el_gr/strings.po
index f3bd8bd383..10cf6ca87c 100644
--- a/addons/skin.estuary/language/resource.language.el_gr/strings.po
+++ b/addons/skin.estuary/language/resource.language.el_gr/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Greek <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/el_gr/>\n"
"Language: el_gr\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Αυτόματη είσοδος κατά την έναρξη"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Αντικείμενα βασικού μενού"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Σύστημα"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Πληροφορίες PVR"
diff --git a/addons/skin.estuary/language/resource.language.en_au/strings.po b/addons/skin.estuary/language/resource.language.en_au/strings.po
index 58e6d8a1b0..d6e090e305 100644
--- a/addons/skin.estuary/language/resource.language.en_au/strings.po
+++ b/addons/skin.estuary/language/resource.language.en_au/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-03-01 13:10+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: English (Australia) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/en_au/>\n"
"Language: en_au\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -750,3 +742,23 @@ msgstr "Media"
msgctxt "#31611"
msgid "System"
msgstr "System"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
diff --git a/addons/skin.estuary/language/resource.language.en_gb/strings.po b/addons/skin.estuary/language/resource.language.en_gb/strings.po
index d930a28a83..636c7ed555 100644
--- a/addons/skin.estuary/language/resource.language.en_gb/strings.po
+++ b/addons/skin.estuary/language/resource.language.en_gb/strings.po
@@ -308,15 +308,7 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-#: /xml/GameOSD.xml
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-#: /xml/GameOSD.xml
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
+#empty strings from id 31059 to 31060
#: /xml/SkinSettings.xml
msgctxt "#31061"
@@ -929,3 +921,27 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#: /xml/DialogVideoVersion.xml
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#: /xml/DialogVideoVersion.xml
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#: /xml/DialogVideoVersion.xml
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#: /xml/DialogVideoVersion.xml
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.en_nz/strings.po b/addons/skin.estuary/language/resource.language.en_nz/strings.po
index 2e858433d5..3a73ae9a62 100644
--- a/addons/skin.estuary/language/resource.language.en_nz/strings.po
+++ b/addons/skin.estuary/language/resource.language.en_nz/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-03-01 13:10+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: English (New Zealand) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/en_nz/>\n"
"Language: en_nz\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatic Login on startup"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Main menu items"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR info"
diff --git a/addons/skin.estuary/language/resource.language.en_us/strings.po b/addons/skin.estuary/language/resource.language.en_us/strings.po
index be412c0959..d064d8c968 100644
--- a/addons/skin.estuary/language/resource.language.en_us/strings.po
+++ b/addons/skin.estuary/language/resource.language.en_us/strings.po
@@ -5,17 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-10-26 04:27+0000\n"
-"Last-Translator: Dud <dud1992bling@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: English (United States) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/en_us/>\n"
"Language: en_us\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.14.1\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatic Login on startup"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Main menu items"
@@ -510,7 +502,7 @@ msgstr ""
#. Label to show player info page
msgctxt "#31138"
msgid "Player"
-msgstr ""
+msgstr "Player"
#. Label to show video decoder name
msgctxt "#31139"
@@ -647,7 +639,7 @@ msgstr "Artwork related settings."
#. Label for OSD settings category
msgctxt "#31170"
msgid "On screen display"
-msgstr ""
+msgstr "On screen display"
#. Helper text for the label of OSD settings category
msgctxt "#31171"
@@ -657,7 +649,7 @@ msgstr ""
#. Setting Automatically close video OSD
msgctxt "#31172"
msgid "Automatically close video OSD"
-msgstr ""
+msgstr "Automatically close video OSD"
#. Setting auto close time for video osd
msgctxt "#31173"
@@ -683,7 +675,7 @@ msgstr ""
#. Label to show the video codec name
msgctxt "#31600"
msgid "Video codec"
-msgstr ""
+msgstr "Video codec"
#. Label to show the video resolution
msgctxt "#31601"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR info"
diff --git a/addons/skin.estuary/language/resource.language.eo/strings.po b/addons/skin.estuary/language/resource.language.eo/strings.po
index e71ed7408f..db83ad8f43 100644
--- a/addons/skin.estuary/language/resource.language.eo/strings.po
+++ b/addons/skin.estuary/language/resource.language.eo/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "System"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.es_ar/strings.po b/addons/skin.estuary/language/resource.language.es_ar/strings.po
index 7a57ccd8ae..bb508fffb7 100644
--- a/addons/skin.estuary/language/resource.language.es_ar/strings.po
+++ b/addons/skin.estuary/language/resource.language.es_ar/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Spanish (Argentina) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/es_ar/>\n"
"Language: es_ar\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Iniciar sesión automaticamente al iniciar"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elementos de menú principal"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Información PVR"
diff --git a/addons/skin.estuary/language/resource.language.es_es/strings.po b/addons/skin.estuary/language/resource.language.es_es/strings.po
index 939ddcc1ae..2e070c4963 100644
--- a/addons/skin.estuary/language/resource.language.es_es/strings.po
+++ b/addons/skin.estuary/language/resource.language.es_es/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-04-22 02:48+0000\n"
+"PO-Revision-Date: 2023-11-23 17:23+0000\n"
"Last-Translator: José Antonio Alvarado <jalvarado0.eses@gmail.com>\n"
"Language-Team: Spanish (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/es_es/>\n"
"Language: es_es\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Iniciar sesión automáticamente"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elementos del menú principal"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "Añadir versión"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "Añadir extras"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "Configurar por defecto"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Información de PVR"
diff --git a/addons/skin.estuary/language/resource.language.es_mx/strings.po b/addons/skin.estuary/language/resource.language.es_mx/strings.po
index 8cd5d6daa4..a04aeab744 100644
--- a/addons/skin.estuary/language/resource.language.es_mx/strings.po
+++ b/addons/skin.estuary/language/resource.language.es_mx/strings.po
@@ -5,17 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-01-08 16:52+0000\n"
-"Last-Translator: Edson Armando <edsonarmando78@outlook.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Spanish (Mexico) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/es_mx/>\n"
"Language: es_mx\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.10.1\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Inicio de sesión automático al iniciar"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elementos del menú principal"
@@ -647,37 +639,37 @@ msgstr "Configuración relacionada al arte."
#. Label for OSD settings category
msgctxt "#31170"
msgid "On screen display"
-msgstr ""
+msgstr "Información en pantalla"
#. Helper text for the label of OSD settings category
msgctxt "#31171"
msgid "On screen display (OSD) related settings"
-msgstr ""
+msgstr "Configuración relacionada a la información en pantalla"
#. Setting Automatically close video OSD
msgctxt "#31172"
msgid "Automatically close video OSD"
-msgstr ""
+msgstr "Ocultar info automáticamente"
#. Setting auto close time for video osd
msgctxt "#31173"
msgid "Video OSD autoclose time (seconds)"
-msgstr ""
+msgstr "Tiempo para ocultar info (en segundos)"
#. Setting to control what happens when clicking a music album on the home screen
msgctxt "#31174"
msgid "Default select action for albums on the home screen"
-msgstr ""
+msgstr "Acción predeterminada al seleccionar álbumes en la pantalla principal"
#. Setting to control what happens when clicking a TV show on the home screen
msgctxt "#31175"
msgid "Default select action for TV shows on the home screen"
-msgstr ""
+msgstr "Acción predeterminada al seleccionar series en la pantalla principal"
#. Setting to control what happens when clicking a movie set on the home screen
msgctxt "#31176"
msgid "Default select action for movie sets on the home screen"
-msgstr ""
+msgstr "Acción predeterminada al seleccionar conjuntos de películas en la pantalla principal"
# empty strings from id 31170 to 31599
#. Label to show the video codec name
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Información del PVR"
diff --git a/addons/skin.estuary/language/resource.language.et_ee/strings.po b/addons/skin.estuary/language/resource.language.et_ee/strings.po
index 5c994a3d9a..b3ec1e545f 100644
--- a/addons/skin.estuary/language/resource.language.et_ee/strings.po
+++ b/addons/skin.estuary/language/resource.language.et_ee/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-02-21 16:56+0000\n"
-"Last-Translator: rimasx <riks_12@hot.ee>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Estonian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/et_ee/>\n"
"Language: et_ee\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Käivitusel logib ise sisse"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Peamenüü üksused"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Süsteem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Lisad"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR-i info"
diff --git a/addons/skin.estuary/language/resource.language.eu_es/strings.po b/addons/skin.estuary/language/resource.language.eu_es/strings.po
index 894a9a25c4..207de3f014 100644
--- a/addons/skin.estuary/language/resource.language.eu_es/strings.po
+++ b/addons/skin.estuary/language/resource.language.eu_es/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Hasi saioa automatikoki abioan"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Menuko elementu nagusiak"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR informazioa"
diff --git a/addons/skin.estuary/language/resource.language.fa_af/strings.po b/addons/skin.estuary/language/resource.language.fa_af/strings.po
index 53bb7a1a68..5e02454a97 100644
--- a/addons/skin.estuary/language/resource.language.fa_af/strings.po
+++ b/addons/skin.estuary/language/resource.language.fa_af/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.fa_ir/strings.po b/addons/skin.estuary/language/resource.language.fa_ir/strings.po
index 20dd59cbd2..387e468039 100644
--- a/addons/skin.estuary/language/resource.language.fa_ir/strings.po
+++ b/addons/skin.estuary/language/resource.language.fa_ir/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "ورود خودکار هنگام آغاز"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "گزینش + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "گزینش + آغاز"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "موردهای فهرست اصلی"
@@ -751,6 +743,34 @@ msgctxt "#31611"
msgid "System"
msgstr "سیستم"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "گزینش + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "گزینش + آغاز"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "اطَلاعات PVR"
diff --git a/addons/skin.estuary/language/resource.language.fi_fi/strings.po b/addons/skin.estuary/language/resource.language.fi_fi/strings.po
index dcd4238f0d..1da1778093 100644
--- a/addons/skin.estuary/language/resource.language.fi_fi/strings.po
+++ b/addons/skin.estuary/language/resource.language.fi_fi/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-24 11:49+0000\n"
+"PO-Revision-Date: 2023-11-28 18:10+0000\n"
"Last-Translator: Oskari Lavinto <olavinto@protonmail.com>\n"
"Language-Team: Finnish <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/fi_fi/>\n"
"Language: fi_fi\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -199,7 +199,7 @@ msgstr "Kameran valmistaja"
msgctxt "#31042"
msgid "Playlist options"
-msgstr "Toistolistan valinnat"
+msgstr "Toistolistavalinnat"
msgctxt "#31043"
msgid "Set the type and add rules to create a smart playlist. These playlists are dynamic and include all media items from your database which apply to your chosen rules."
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Kirjaudu automaattisesti käynnistettäessä"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Valinta + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Valitse + Aloita"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Päävalikon kohteet"
@@ -271,11 +263,11 @@ msgstr "Osiot"
msgctxt "#31065"
msgid "Video playlist"
-msgstr "Videoiden toistolista"
+msgstr "Videotoistolista"
msgctxt "#31066"
msgid "Music playlist"
-msgstr "Musiikin toistolista"
+msgstr "Musiikkitoistolista"
msgctxt "#31067"
msgid "Event log"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Järjestelmä"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Lisäsisältö"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Valinta + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Valitse + Aloita"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR-tiedot"
diff --git a/addons/skin.estuary/language/resource.language.fil/strings.po b/addons/skin.estuary/language/resource.language.fil/strings.po
index 971903cbca..603fc1adfd 100644
--- a/addons/skin.estuary/language/resource.language.fil/strings.po
+++ b/addons/skin.estuary/language/resource.language.fil/strings.po
@@ -251,14 +251,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -750,3 +742,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.fo_fo/strings.po b/addons/skin.estuary/language/resource.language.fo_fo/strings.po
index 320df2ecf6..959e7aeeee 100644
--- a/addons/skin.estuary/language/resource.language.fo_fo/strings.po
+++ b/addons/skin.estuary/language/resource.language.fo_fo/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "Skipan"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.fr_ca/strings.po b/addons/skin.estuary/language/resource.language.fr_ca/strings.po
index 07abd83ef5..23333f0d73 100644
--- a/addons/skin.estuary/language/resource.language.fr_ca/strings.po
+++ b/addons/skin.estuary/language/resource.language.fr_ca/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: French (Canada) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/fr_ca/>\n"
"Language: fr_ca\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Connexion automatique au démarrage"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Éléments du menu principal"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Système"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Suppléments"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Informations des numériscopes"
diff --git a/addons/skin.estuary/language/resource.language.fr_fr/strings.po b/addons/skin.estuary/language/resource.language.fr_fr/strings.po
index 801137de4c..eff9f87ce4 100644
--- a/addons/skin.estuary/language/resource.language.fr_fr/strings.po
+++ b/addons/skin.estuary/language/resource.language.fr_fr/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-12-22 00:55+0000\n"
-"Last-Translator: skypichat <skypichat@hotmail.fr>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: French (France) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/fr_fr/>\n"
"Language: fr_fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.15\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Identification auto. au démarrage"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Sélectionner + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Sélectionner + Démarrage"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Menu principal"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Système"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Sélectionner + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Sélectionner + Démarrage"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Infos enregistreur vidéo"
diff --git a/addons/skin.estuary/language/resource.language.gl_es/strings.po b/addons/skin.estuary/language/resource.language.gl_es/strings.po
index 48de94bc76..cc17384e37 100644
--- a/addons/skin.estuary/language/resource.language.gl_es/strings.po
+++ b/addons/skin.estuary/language/resource.language.gl_es/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-03-27 01:18+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Galician (Spain) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/gl_es/>\n"
"Language: gl_es\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Amosar o inicio de sesión ao arrancar"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Inicio"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elementos do menú principal"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Inicio"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Info. do PVR"
diff --git a/addons/skin.estuary/language/resource.language.he_il/strings.po b/addons/skin.estuary/language/resource.language.he_il/strings.po
index 544ae9707c..1615eaa58d 100644
--- a/addons/skin.estuary/language/resource.language.he_il/strings.po
+++ b/addons/skin.estuary/language/resource.language.he_il/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-02-22 16:57+0000\n"
-"Last-Translator: Dan Davidson <nnnvvv450@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Hebrew (Israel) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/he_il/>\n"
"Language: he_il\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && n % 10 == 0) ? 2 : 3));\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "התחברות אוטומטית באיתחול"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "פריטי תפריט ראשי"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "מערכת"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "מיוחדים"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "מידע PVR"
diff --git a/addons/skin.estuary/language/resource.language.hi_in/strings.po b/addons/skin.estuary/language/resource.language.hi_in/strings.po
index f728fbab27..5d3b9d2656 100644
--- a/addons/skin.estuary/language/resource.language.hi_in/strings.po
+++ b/addons/skin.estuary/language/resource.language.hi_in/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "प्रणाली"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.hr_hr/strings.po b/addons/skin.estuary/language/resource.language.hr_hr/strings.po
index e55248b315..3e9a37913d 100644
--- a/addons/skin.estuary/language/resource.language.hr_hr/strings.po
+++ b/addons/skin.estuary/language/resource.language.hr_hr/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-12-30 20:19+0000\n"
-"Last-Translator: gogogogi <trebelnik2@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Croatian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/hr_hr/>\n"
"Language: hr_hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 4.15\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatska prijava pri pokretanju"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Stavke glavnog izbornika"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sustav"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Dodatno"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR informacije"
diff --git a/addons/skin.estuary/language/resource.language.hu_hu/strings.po b/addons/skin.estuary/language/resource.language.hu_hu/strings.po
index c785223022..8f3a973b75 100644
--- a/addons/skin.estuary/language/resource.language.hu_hu/strings.po
+++ b/addons/skin.estuary/language/resource.language.hu_hu/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-01-29 21:15+0000\n"
+"PO-Revision-Date: 2023-11-28 18:10+0000\n"
"Last-Translator: Frodo19 <bilbohu@gmail.com>\n"
"Language-Team: Hungarian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/hu_hu/>\n"
"Language: hu_hu\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatikus bejelentkezés indításkor"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Kiválasztás + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Kiválasztás + indítás"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Főmenü elemek"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Rendszer"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "Verzió hozzáadása"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "Extrák hozzáadása"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "Alapállapotba állít"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extrák"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Kiválasztás + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Kiválasztás + indítás"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR információ"
diff --git a/addons/skin.estuary/language/resource.language.hy_am/strings.po b/addons/skin.estuary/language/resource.language.hy_am/strings.po
index 0f1ae55358..e4a235899c 100644
--- a/addons/skin.estuary/language/resource.language.hy_am/strings.po
+++ b/addons/skin.estuary/language/resource.language.hy_am/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.id_id/strings.po b/addons/skin.estuary/language/resource.language.id_id/strings.po
index c00b103051..8a09cdd1c0 100644
--- a/addons/skin.estuary/language/resource.language.id_id/strings.po
+++ b/addons/skin.estuary/language/resource.language.id_id/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-11-27 00:11+0000\n"
-"Last-Translator: Nao3Line Prez <n.yazawa6932@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Indonesian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/id_id/>\n"
"Language: id_id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.14.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Login Otomatis saat mulai"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Item menu utama"
@@ -751,6 +743,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Ekstra"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Info PVR"
diff --git a/addons/skin.estuary/language/resource.language.is_is/strings.po b/addons/skin.estuary/language/resource.language.is_is/strings.po
index 5ad397235e..100c50845d 100644
--- a/addons/skin.estuary/language/resource.language.is_is/strings.po
+++ b/addons/skin.estuary/language/resource.language.is_is/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-02-09 00:07+0000\n"
-"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Icelandic <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/is_is/>\n"
"Language: is_is\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Sjálfvirk innskráning í ræsingu"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Val + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Val + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Atriði í aðalvalmynd"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Kerfi"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Aukahlutir"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Val + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Val + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Upplýsingar um PVR-upptöku"
diff --git a/addons/skin.estuary/language/resource.language.it_it/strings.po b/addons/skin.estuary/language/resource.language.it_it/strings.po
index 8f75052fca..a025541584 100644
--- a/addons/skin.estuary/language/resource.language.it_it/strings.po
+++ b/addons/skin.estuary/language/resource.language.it_it/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-05-17 14:15+0000\n"
+"PO-Revision-Date: 2023-11-26 21:18+0000\n"
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
"Language-Team: Italian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/it_it/>\n"
"Language: it_it\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.17\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Accesso automatico all'avvio"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Seleziona + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Seleziona + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Voci menu principale"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "Aggiungi versione"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "Aggiungi extra"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "Imposta predefinito"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extra"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Seleziona + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Seleziona + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Info PVR"
diff --git a/addons/skin.estuary/language/resource.language.ja_jp/strings.po b/addons/skin.estuary/language/resource.language.ja_jp/strings.po
index 8149e593e5..69c3f792b9 100644
--- a/addons/skin.estuary/language/resource.language.ja_jp/strings.po
+++ b/addons/skin.estuary/language/resource.language.ja_jp/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-11 19:35+0000\n"
-"Last-Translator: yohru <yohru7@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Japanese <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/ja_jp/>\n"
"Language: ja_jp\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "スタートアップ時に自動ログイン"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "セレクト + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "セレクト + スタート"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "メインメニューアイテム"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "システム"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "エクストラ"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "セレクト + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "セレクト + スタート"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR詳細"
diff --git a/addons/skin.estuary/language/resource.language.kn_in/strings.po b/addons/skin.estuary/language/resource.language.kn_in/strings.po
index 9047208f89..34f828cdd3 100644
--- a/addons/skin.estuary/language/resource.language.kn_in/strings.po
+++ b/addons/skin.estuary/language/resource.language.kn_in/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.ko_kr/strings.po b/addons/skin.estuary/language/resource.language.ko_kr/strings.po
index df7aaead83..8ef4f0e504 100644
--- a/addons/skin.estuary/language/resource.language.ko_kr/strings.po
+++ b/addons/skin.estuary/language/resource.language.ko_kr/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-11-15 09:47+0000\n"
+"PO-Revision-Date: 2023-11-23 17:23+0000\n"
"Last-Translator: Minho Park <parkmino@gmail.com>\n"
"Language-Team: Korean <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/ko_kr/>\n"
"Language: ko_kr\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.14.2\n"
+"X-Generator: Weblate 5.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "시작할 때 자동 로그인"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "메인 메뉴 항목"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "시스템"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "버전 추가"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "기타 추가"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "기본값으로 설정"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "기타"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR 정보"
diff --git a/addons/skin.estuary/language/resource.language.lt_lt/strings.po b/addons/skin.estuary/language/resource.language.lt_lt/strings.po
index 68039b637d..71515e93c4 100644
--- a/addons/skin.estuary/language/resource.language.lt_lt/strings.po
+++ b/addons/skin.estuary/language/resource.language.lt_lt/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatinis prisijungimas paleidžiant"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Meniu elementai"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR informacija"
diff --git a/addons/skin.estuary/language/resource.language.lv_lv/strings.po b/addons/skin.estuary/language/resource.language.lv_lv/strings.po
index 96c1772a47..9079c568d3 100644
--- a/addons/skin.estuary/language/resource.language.lv_lv/strings.po
+++ b/addons/skin.estuary/language/resource.language.lv_lv/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatiski pieteikties uzsākot"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Galvenās izvēlnes ieraksti"
@@ -744,6 +736,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistēma"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR info"
diff --git a/addons/skin.estuary/language/resource.language.mi/strings.po b/addons/skin.estuary/language/resource.language.mi/strings.po
index 4d1a8cfaee..45fa7f2fe7 100644
--- a/addons/skin.estuary/language/resource.language.mi/strings.po
+++ b/addons/skin.estuary/language/resource.language.mi/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "Pūnaha"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.mk_mk/strings.po b/addons/skin.estuary/language/resource.language.mk_mk/strings.po
index 82c35d1533..b66f58b587 100644
--- a/addons/skin.estuary/language/resource.language.mk_mk/strings.po
+++ b/addons/skin.estuary/language/resource.language.mk_mk/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Елементи на гловното мени"
@@ -744,6 +736,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Систем"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
#~ msgctxt "#31138"
#~ msgid "Player process info"
#~ msgstr "Информации за процесот на плеерот"
diff --git a/addons/skin.estuary/language/resource.language.ml_in/strings.po b/addons/skin.estuary/language/resource.language.ml_in/strings.po
index d9e89b2681..71f7ef0849 100644
--- a/addons/skin.estuary/language/resource.language.ml_in/strings.po
+++ b/addons/skin.estuary/language/resource.language.ml_in/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.mn_mn/strings.po b/addons/skin.estuary/language/resource.language.mn_mn/strings.po
index effe407608..f63630b9b5 100644
--- a/addons/skin.estuary/language/resource.language.mn_mn/strings.po
+++ b/addons/skin.estuary/language/resource.language.mn_mn/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.ms_my/strings.po b/addons/skin.estuary/language/resource.language.ms_my/strings.po
index f57dcfbc1b..801473c0cf 100644
--- a/addons/skin.estuary/language/resource.language.ms_my/strings.po
+++ b/addons/skin.estuary/language/resource.language.ms_my/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-03-27 01:18+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Malay <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/ms_my/>\n"
"Language: ms_my\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.11.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Daftar masuk automatik ketika permulaan"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Item menu utama"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Ekstra"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Maklumat PVR"
diff --git a/addons/skin.estuary/language/resource.language.mt_mt/strings.po b/addons/skin.estuary/language/resource.language.mt_mt/strings.po
index c5c7b129cf..7714b75c10 100644
--- a/addons/skin.estuary/language/resource.language.mt_mt/strings.po
+++ b/addons/skin.estuary/language/resource.language.mt_mt/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -750,3 +742,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.my_mm/strings.po b/addons/skin.estuary/language/resource.language.my_mm/strings.po
index 065de18428..a1919193ea 100644
--- a/addons/skin.estuary/language/resource.language.my_mm/strings.po
+++ b/addons/skin.estuary/language/resource.language.my_mm/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Burmese <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/my_mm/>\n"
"Language: my_mm\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -385,7 +377,7 @@ msgstr ""
#. viewtype name
msgctxt "#31102"
msgid "Wall"
-msgstr ""
+msgstr "Icons"
msgctxt "#31103"
msgid "Enter text here..."
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "စနစ်"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.nb_no/strings.po b/addons/skin.estuary/language/resource.language.nb_no/strings.po
index 00c6bc411d..d9e003fd7b 100644
--- a/addons/skin.estuary/language/resource.language.nb_no/strings.po
+++ b/addons/skin.estuary/language/resource.language.nb_no/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Norwegian Bokmål <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/nb_no/>\n"
"Language: nb_no\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatisk pålogging ved oppstart"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Hovedmenyelementer"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Annet"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR-informasjon"
diff --git a/addons/skin.estuary/language/resource.language.nl_nl/strings.po b/addons/skin.estuary/language/resource.language.nl_nl/strings.po
index 27fc0f031e..74c0069013 100644
--- a/addons/skin.estuary/language/resource.language.nl_nl/strings.po
+++ b/addons/skin.estuary/language/resource.language.nl_nl/strings.po
@@ -5,17 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-09-07 12:39+0000\n"
-"Last-Translator: Marc Botermans <marcbotermans@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Dutch <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/nl_nl/>\n"
"Language: nl_nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.14\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatisch inloggen tijdens opstarten"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Hoofdmenu-items"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Systeem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extra's"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR info"
diff --git a/addons/skin.estuary/language/resource.language.pl_pl/strings.po b/addons/skin.estuary/language/resource.language.pl_pl/strings.po
index 1e041c2858..ae48f955ab 100644
--- a/addons/skin.estuary/language/resource.language.pl_pl/strings.po
+++ b/addons/skin.estuary/language/resource.language.pl_pl/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-11-12 17:13+0000\n"
+"PO-Revision-Date: 2023-11-23 17:23+0000\n"
"Last-Translator: Marek Adamski <fevbew@wp.pl>\n"
"Language-Team: Polish <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/pl_pl/>\n"
"Language: pl_pl\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
-"X-Generator: Weblate 4.14.2\n"
+"X-Generator: Weblate 5.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Loguj automatycznie po uruchomieniu"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Menu startowe"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr "Dodaj wersję"
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr "Dodaj dodatki"
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr "Ustaw domyślną"
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Dodatki"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "O telewizji"
diff --git a/addons/skin.estuary/language/resource.language.pt_br/strings.po b/addons/skin.estuary/language/resource.language.pt_br/strings.po
index f034bb5f1c..8a7ea22398 100644
--- a/addons/skin.estuary/language/resource.language.pt_br/strings.po
+++ b/addons/skin.estuary/language/resource.language.pt_br/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-06-25 09:43+0000\n"
-"Last-Translator: Wanilton Campos <campos.wanilton@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Portuguese (Brazil) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/pt_br/>\n"
"Language: pt_br\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.18\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Login automático ao inicializar"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Selecionar + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Selecione + Iniciar"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Ítens Menu Principal"
@@ -647,7 +639,7 @@ msgstr "Ajustes relacionados as artes."
#. Label for OSD settings category
msgctxt "#31170"
msgid "On screen display"
-msgstr ""
+msgstr "Na tela OSD"
#. Helper text for the label of OSD settings category
msgctxt "#31171"
@@ -667,17 +659,17 @@ msgstr "Tempo de fechamento automático do vídeo OSD (segundos)"
#. Setting to control what happens when clicking a music album on the home screen
msgctxt "#31174"
msgid "Default select action for albums on the home screen"
-msgstr ""
+msgstr "Ação de seleção padrão para álbuns na tela inicial"
#. Setting to control what happens when clicking a TV show on the home screen
msgctxt "#31175"
msgid "Default select action for TV shows on the home screen"
-msgstr ""
+msgstr "Ação de seleção padrão para seriados na tela inicial"
#. Setting to control what happens when clicking a movie set on the home screen
msgctxt "#31176"
msgid "Default select action for movie sets on the home screen"
-msgstr ""
+msgstr "Ação de seleção padrão para coletâneas na tela inicial"
# empty strings from id 31170 to 31599
#. Label to show the video codec name
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Selecionar + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Selecione + Iniciar"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Informação do PVR"
diff --git a/addons/skin.estuary/language/resource.language.pt_pt/strings.po b/addons/skin.estuary/language/resource.language.pt_pt/strings.po
index 20be9e61ce..3f313b0938 100644
--- a/addons/skin.estuary/language/resource.language.pt_pt/strings.po
+++ b/addons/skin.estuary/language/resource.language.pt_pt/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Portuguese (Portugal) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/pt_pt/>\n"
"Language: pt_pt\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Iniciar sessão ao arrancar"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Selecionar + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Selecionar + Iniciar"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Itens do menu principal"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistema"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extras"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Selecionar + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Selecionar + Iniciar"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Info PVR"
diff --git a/addons/skin.estuary/language/resource.language.ro_ro/strings.po b/addons/skin.estuary/language/resource.language.ro_ro/strings.po
index b26d41c85e..13fcc3374a 100644
--- a/addons/skin.estuary/language/resource.language.ro_ro/strings.po
+++ b/addons/skin.estuary/language/resource.language.ro_ro/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Romanian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/ro_ro/>\n"
"Language: ro_ro\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Autentificare automată la pornire"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elemente meniu principal"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extra"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Informații PVR"
diff --git a/addons/skin.estuary/language/resource.language.ru_ru/strings.po b/addons/skin.estuary/language/resource.language.ru_ru/strings.po
index fe86abe0f7..cf48ad1e7f 100644
--- a/addons/skin.estuary/language/resource.language.ru_ru/strings.po
+++ b/addons/skin.estuary/language/resource.language.ru_ru/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: 2022-12-26 02:05+0000\n"
"Last-Translator: Andrei Stepanov <adem4ik@gmail.com>\n"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Автоматическая авторизация при запуске"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Элементы главного меню"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Система"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Сведения PVR"
diff --git a/addons/skin.estuary/language/resource.language.si_lk/strings.po b/addons/skin.estuary/language/resource.language.si_lk/strings.po
index a020daefe1..dbe7fd3c0d 100644
--- a/addons/skin.estuary/language/resource.language.si_lk/strings.po
+++ b/addons/skin.estuary/language/resource.language.si_lk/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "පද්ධතිය"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.sk_sk/strings.po b/addons/skin.estuary/language/resource.language.sk_sk/strings.po
index f7e774f563..14bc85b319 100644
--- a/addons/skin.estuary/language/resource.language.sk_sk/strings.po
+++ b/addons/skin.estuary/language/resource.language.sk_sk/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-04 23:17+0000\n"
+"PO-Revision-Date: 2023-09-14 18:32+0000\n"
"Last-Translator: Jose Riha <jose1711@gmail.com>\n"
"Language-Team: Slovak <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/sk_sk/>\n"
"Language: sk_sk\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.0.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatické prihlásenie pri spustení"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Vybrať + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Vybrať + Štart"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Položky hlavnej ponuky"
@@ -420,7 +412,7 @@ msgstr "Hľadať na YouTube"
msgctxt "#31115"
msgid "Toggle subtitle"
-msgstr ""
+msgstr "Prepnúť titulky"
msgctxt "#31116"
msgid "Remove this main menu item"
@@ -647,12 +639,12 @@ msgstr "Nastavenia súvisiace s Artwork (umeleckou grafikou)."
#. Label for OSD settings category
msgctxt "#31170"
msgid "On screen display"
-msgstr ""
+msgstr "Ponuka na obrazovke"
#. Helper text for the label of OSD settings category
msgctxt "#31171"
msgid "On screen display (OSD) related settings"
-msgstr ""
+msgstr "Nastavenia ponuky na obrazovke (OSD)"
#. Setting Automatically close video OSD
msgctxt "#31172"
@@ -667,17 +659,17 @@ msgstr "Čas automatického zatvárania OSD videa (sekundy)"
#. Setting to control what happens when clicking a music album on the home screen
msgctxt "#31174"
msgid "Default select action for albums on the home screen"
-msgstr ""
+msgstr "Predvolená akcia pre albumy na hlavnej obrazovke"
#. Setting to control what happens when clicking a TV show on the home screen
msgctxt "#31175"
msgid "Default select action for TV shows on the home screen"
-msgstr ""
+msgstr "Predvolená akcia pre seriály na hlavnej obrazovke"
#. Setting to control what happens when clicking a movie set on the home screen
msgctxt "#31176"
msgid "Default select action for movie sets on the home screen"
-msgstr ""
+msgstr "Predvolená akcia pre filmy na hlavnej obrazovke"
# empty strings from id 31170 to 31599
#. Label to show the video codec name
@@ -698,7 +690,7 @@ msgstr "Video aspekt"
#. Label to show the video bitrate
msgctxt "#31603"
msgid "Video bitrate"
-msgstr ""
+msgstr "Prenosová rýchlosť videa"
#. Label to show the audio codec name
msgctxt "#31604"
@@ -713,7 +705,7 @@ msgstr "Audio kanály"
#. Label to show the audio bitrate
msgctxt "#31606"
msgid "Audio bitrate"
-msgstr ""
+msgstr "Prenosová rýchlosť zvuku"
#. Label to show the screen resolution
msgctxt "#31607"
@@ -723,7 +715,7 @@ msgstr "Rozlíšenie obrazovky"
#. Label to show the system rendering speed
msgctxt "#31608"
msgid "System rendering speed"
-msgstr ""
+msgstr "Rýchlosť vykresľovania systému"
#. Label to show the system CPU usage
msgctxt "#31609"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Systém"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Vybrať + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Vybrať + Štart"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Informácie o PVR"
diff --git a/addons/skin.estuary/language/resource.language.sl_si/strings.po b/addons/skin.estuary/language/resource.language.sl_si/strings.po
index 87f6d62818..f16cafc7bf 100644
--- a/addons/skin.estuary/language/resource.language.sl_si/strings.po
+++ b/addons/skin.estuary/language/resource.language.sl_si/strings.po
@@ -5,17 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-08-26 12:27+0000\n"
-"Last-Translator: Matej <mateju@src.gnome.org>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Slovenian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/sl_si/>\n"
"Language: sl_si\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
-"X-Generator: Weblate 4.13\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -748,3 +740,23 @@ msgstr "Predstavne vsebine"
msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Dodatki"
diff --git a/addons/skin.estuary/language/resource.language.sq_al/strings.po b/addons/skin.estuary/language/resource.language.sq_al/strings.po
index 785d1213ce..b43d5326b5 100644
--- a/addons/skin.estuary/language/resource.language.sq_al/strings.po
+++ b/addons/skin.estuary/language/resource.language.sq_al/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr "Media"
msgctxt "#31611"
msgid "System"
msgstr "Sistemi"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.sr_rs/strings.po b/addons/skin.estuary/language/resource.language.sr_rs/strings.po
index 9f5b775c19..1af83d169a 100644
--- a/addons/skin.estuary/language/resource.language.sr_rs/strings.po
+++ b/addons/skin.estuary/language/resource.language.sr_rs/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Аутоматска Пријава при укључивању"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Ставке главног менија"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Систем"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR информације"
diff --git a/addons/skin.estuary/language/resource.language.sr_rs@latin/strings.po b/addons/skin.estuary/language/resource.language.sr_rs@latin/strings.po
index f3886c91d1..cf62767ae2 100644
--- a/addons/skin.estuary/language/resource.language.sr_rs@latin/strings.po
+++ b/addons/skin.estuary/language/resource.language.sr_rs@latin/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Serbian (latin) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/sr_Latn/>\n"
"Language: sr_rs@latin\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatska Prijava pri uključivanju"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Stavke glavnog menija"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Dodaci"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR informacije"
diff --git a/addons/skin.estuary/language/resource.language.sv_se/strings.po b/addons/skin.estuary/language/resource.language.sv_se/strings.po
index c8fdfe0ef1..f531099cdb 100644
--- a/addons/skin.estuary/language/resource.language.sv_se/strings.po
+++ b/addons/skin.estuary/language/resource.language.sv_se/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-26 16:11+0000\n"
-"Last-Translator: Andreas Bolin <bolin.andreas@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Swedish <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/sv_se/>\n"
"Language: sv_se\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Automatisk inloggning vid uppstart"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Huvudmenyobjekt"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "System"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Extra"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR-info"
diff --git a/addons/skin.estuary/language/resource.language.szl/strings.po b/addons/skin.estuary/language/resource.language.szl/strings.po
index ca7aa1d520..7462d9faf8 100644
--- a/addons/skin.estuary/language/resource.language.szl/strings.po
+++ b/addons/skin.estuary/language/resource.language.szl/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-03 15:42+0000\n"
-"Last-Translator: gkkulik <gregorykkulik@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Silesian <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/szl/>\n"
"Language: szl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Loguj autōmatycznie po sztartniyńciu"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Elymynty przodnigo myni"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "Systym"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Ekstra"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Ô telewizyji"
diff --git a/addons/skin.estuary/language/resource.language.ta_in/strings.po b/addons/skin.estuary/language/resource.language.ta_in/strings.po
index ef2ccdb11e..6acf985262 100644
--- a/addons/skin.estuary/language/resource.language.ta_in/strings.po
+++ b/addons/skin.estuary/language/resource.language.ta_in/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "கணிணி"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.te_in/strings.po b/addons/skin.estuary/language/resource.language.te_in/strings.po
index be8e536887..72ba2feef1 100644
--- a/addons/skin.estuary/language/resource.language.te_in/strings.po
+++ b/addons/skin.estuary/language/resource.language.te_in/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.tg_tj/strings.po b/addons/skin.estuary/language/resource.language.tg_tj/strings.po
index 821a9b3a4a..1c22c631cd 100644
--- a/addons/skin.estuary/language/resource.language.tg_tj/strings.po
+++ b/addons/skin.estuary/language/resource.language.tg_tj/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr "Media"
msgctxt "#31611"
msgid "System"
msgstr "Система"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.th_th/strings.po b/addons/skin.estuary/language/resource.language.th_th/strings.po
index 4b11fac370..5c556fd234 100644
--- a/addons/skin.estuary/language/resource.language.th_th/strings.po
+++ b/addons/skin.estuary/language/resource.language.th_th/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Thai <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/th_th/>\n"
"Language: th_th\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "เข้าใช้โดยอัตโนมัติเมื่อเริ่มต้น"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "รายการเมนูหลัก"
@@ -740,6 +732,26 @@ msgctxt "#31611"
msgid "System"
msgstr "ระบบ"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "ส่วนพิเศษ"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "ข้อมูล PVR"
diff --git a/addons/skin.estuary/language/resource.language.tr_tr/strings.po b/addons/skin.estuary/language/resource.language.tr_tr/strings.po
index faf688c79f..ae9fe17b81 100644
--- a/addons/skin.estuary/language/resource.language.tr_tr/strings.po
+++ b/addons/skin.estuary/language/resource.language.tr_tr/strings.po
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: translations@kodi.tv\n"
+"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: 2023-02-12 20:48+0000\n"
"Last-Translator: queeup <queeup@zoho.com>\n"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Başlangıçta otomatik olarak oturum aç"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Ana menü ögeleri"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Sistem"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR bilgisi"
diff --git a/addons/skin.estuary/language/resource.language.uk_ua/strings.po b/addons/skin.estuary/language/resource.language.uk_ua/strings.po
index 24b1a3d884..cb9c668e58 100644
--- a/addons/skin.estuary/language/resource.language.uk_ua/strings.po
+++ b/addons/skin.estuary/language/resource.language.uk_ua/strings.po
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Автоматичний вхід при запуску"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Елементи головного меню"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Система"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Відомості PVR"
diff --git a/addons/skin.estuary/language/resource.language.uz_uz/strings.po b/addons/skin.estuary/language/resource.language.uz_uz/strings.po
index a864fa49ef..b11ccad52a 100644
--- a/addons/skin.estuary/language/resource.language.uz_uz/strings.po
+++ b/addons/skin.estuary/language/resource.language.uz_uz/strings.po
@@ -252,14 +252,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr ""
-msgctxt "#31059"
-msgid "Select + X"
-msgstr ""
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr ""
-
msgctxt "#31061"
msgid "Main menu items"
msgstr ""
@@ -751,3 +743,23 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr "Tizim"
+
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.vi_vn/strings.po b/addons/skin.estuary/language/resource.language.vi_vn/strings.po
index 286d3389ac..94bd174f90 100644
--- a/addons/skin.estuary/language/resource.language.vi_vn/strings.po
+++ b/addons/skin.estuary/language/resource.language.vi_vn/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2023-07-14 00:21+0000\n"
-"Last-Translator: Nguyễn Trung Hậu <trunghau1712@gmail.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Vietnamese <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/vi_vn/>\n"
"Language: vi_vn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "Đăng nhập tự động khi khởi động"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Chọn + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Chọn + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "Mục menu chính"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "Hệ thống"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "Vai phụ"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Chọn + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Chọn + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "Thông tin PVR"
diff --git a/addons/skin.estuary/language/resource.language.zh_cn/strings.po b/addons/skin.estuary/language/resource.language.zh_cn/strings.po
index 7f6d05cf4c..62f85826dc 100644
--- a/addons/skin.estuary/language/resource.language.zh_cn/strings.po
+++ b/addons/skin.estuary/language/resource.language.zh_cn/strings.po
@@ -7,15 +7,15 @@ msgstr ""
"Project-Id-Version: KODI Main\n"
"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-11-12 17:13+0000\n"
-"Last-Translator: yangyangdaji <1504305527@qq.com>\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
+"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Chinese (China) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/zh_cn/>\n"
"Language: zh_cn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.14.2\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "启动时自动登录"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "Select + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "Select + Start"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "主菜单项"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "系统"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "额外"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "Select + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "Select + Start"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR 信息"
diff --git a/addons/skin.estuary/language/resource.language.zh_tw/strings.po b/addons/skin.estuary/language/resource.language.zh_tw/strings.po
index b3091901dd..371c42ef9a 100644
--- a/addons/skin.estuary/language/resource.language.zh_tw/strings.po
+++ b/addons/skin.estuary/language/resource.language.zh_tw/strings.po
@@ -5,9 +5,9 @@
msgid ""
msgstr ""
"Project-Id-Version: KODI Main\n"
-"Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n"
+"Report-Msgid-Bugs-To: translations@kodi.tv\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-02-26 16:13+0000\n"
+"PO-Revision-Date: 2023-12-04 09:42+0000\n"
"Last-Translator: Christian Gade <gade@kodi.tv>\n"
"Language-Team: Chinese (Taiwan) <https://kodi.weblate.cloud/projects/kodi-add-ons-skins/skin-estuary/zh_tw/>\n"
"Language: zh_tw\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.11\n"
+"X-Generator: Weblate 5.2.1\n"
msgctxt "Addon Summary"
msgid "Estuary skin by phil65. (Kodi's default skin)"
@@ -249,14 +249,6 @@ msgctxt "#31058"
msgid "Automatic Login on startup"
msgstr "啟動時自動登入"
-msgctxt "#31059"
-msgid "Select + X"
-msgstr "選擇 + X"
-
-msgctxt "#31060"
-msgid "Select + Start"
-msgstr "選擇 + 開始"
-
msgctxt "#31061"
msgid "Main menu items"
msgstr "主選單項目"
@@ -740,6 +732,34 @@ msgctxt "#31611"
msgid "System"
msgstr "系統"
+#. Video version dialog button
+msgctxt "#31612"
+msgid "Add version"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31613"
+msgid "Add extras"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31614"
+msgid "Set default"
+msgstr ""
+
+#. Video version dialog button
+msgctxt "#31615"
+msgid "Extras"
+msgstr "額外資訊"
+
+#~ msgctxt "#31059"
+#~ msgid "Select + X"
+#~ msgstr "選擇 + X"
+
+#~ msgctxt "#31060"
+#~ msgid "Select + Start"
+#~ msgstr "選擇 + 開始"
+
#~ msgctxt "#31137"
#~ msgid "PVR info"
#~ msgstr "PVR 資訊"
diff --git a/addons/skin.estuary/media/DefaultVideoVersions.png b/addons/skin.estuary/media/DefaultVideoVersions.png
new file mode 100644
index 0000000000..893d8c4fc6
--- /dev/null
+++ b/addons/skin.estuary/media/DefaultVideoVersions.png
Binary files differ
diff --git a/addons/skin.estuary/media/flags/videocodec/theora.png b/addons/skin.estuary/media/flags/videocodec/theora.png
new file mode 100644
index 0000000000..8f1af4bb94
--- /dev/null
+++ b/addons/skin.estuary/media/flags/videocodec/theora.png
Binary files differ
diff --git a/addons/skin.estuary/media/icons/infodialogs/versions.png b/addons/skin.estuary/media/icons/infodialogs/versions.png
new file mode 100644
index 0000000000..31d65935a4
--- /dev/null
+++ b/addons/skin.estuary/media/icons/infodialogs/versions.png
Binary files differ
diff --git a/addons/skin.estuary/media/overlays/versions.png b/addons/skin.estuary/media/overlays/versions.png
new file mode 100644
index 0000000000..b4fcc37e5f
--- /dev/null
+++ b/addons/skin.estuary/media/overlays/versions.png
Binary files differ
diff --git a/addons/skin.estuary/xml/Custom_1101_SettingsList.xml b/addons/skin.estuary/xml/Custom_1101_SettingsList.xml
index 24de2dcf5b..e15a42e39d 100644
--- a/addons/skin.estuary/xml/Custom_1101_SettingsList.xml
+++ b/addons/skin.estuary/xml/Custom_1101_SettingsList.xml
@@ -160,7 +160,7 @@
<width>700</width>
<include>DialogSettingButton</include>
<label>$LOCALIZE[13376]</label>
- <label2>[COLOR grey]Select + Right Stick[/COLOR]</label2>
+ <label2>[COLOR grey]$FEATURE[select,game.controller.snes] + $FEATURE[rightstick,game.controller.default][/COLOR]</label2>
<onclick>ActivateWindow(GameVolume)</onclick>
</control>
<control type="button" id="14103">
diff --git a/addons/skin.estuary/xml/DialogFavourites.xml b/addons/skin.estuary/xml/DialogFavourites.xml
deleted file mode 100644
index 73b6309421..0000000000
--- a/addons/skin.estuary/xml/DialogFavourites.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<window>
- <defaultcontrol always="true">450</defaultcontrol>
- <include>Animation_DialogPopupOpenClose</include>
- <controls>
- <control type="group">
- <centertop>50%</centertop>
- <centerleft>50%</centerleft>
- <width>1540</width>
- <height>858</height>
- <include content="DialogBackgroundCommons">
- <param name="width" value="1540" />
- <param name="height" value="858" />
- <param name="header_label" value="$LOCALIZE[1036]" />
- <param name="header_id" value="2" />
- </include>
- <include content="UpDownArrows">
- <param name="container_id" value="450" />
- <param name="posx" value="746" />
- <param name="up_posy" value="-40" />
- <param name="down_posy" value="876" />
- </include>
- <control type="scrollbar" id="60">
- <right>0</right>
- <top>70</top>
- <width>12</width>
- <bottom>0</bottom>
- <onleft>450</onleft>
- <onright>450</onright>
- <orientation>vertical</orientation>
- </control>
- <control type="panel" id="450">
- <left>10</left>
- <top>70</top>
- <width>1600</width>
- <bottom>0</bottom>
- <onleft>60</onleft>
- <onright>60</onright>
- <onup>450</onup>
- <ondown>450</ondown>
- <preloaditems>2</preloaditems>
- <pagecontrol>60</pagecontrol>
- <scrolltime tween="sine">200</scrolltime>
- <orientation>vertical</orientation>
- <itemlayout width="300" height="380">
- <control type="group">
- <top>10</top>
- <include content="InfoWallMusicLayout">
- <param name="fallback_image" value="DefaultFavourites.png" />
- </include>
- </control>
- </itemlayout>
- <focusedlayout width="300">
- <control type="group">
- <top>10</top>
- <include content="InfoWallMusicLayout">
- <param name="fallback_image" value="DefaultFavourites.png" />
- <param name="focused" value="true" />
- </include>
- </control>
- </focusedlayout>
- </control>
- <control type="textbox">
- <left>300</left>
- <top>90</top>
- <width>900</width>
- <bottom>0</bottom>
- <aligny>center</aligny>
- <align>center</align>
- <label>$LOCALIZE[31025]</label>
- <font>font45_title</font>
- <visible>Integer.IsEqual(Container(450).NumItems,0)</visible>
- </control>
- </control>
- </controls>
-</window>
diff --git a/addons/skin.estuary/xml/DialogMusicInfo.xml b/addons/skin.estuary/xml/DialogMusicInfo.xml
index b4dd30ff03..50d07c0996 100644
--- a/addons/skin.estuary/xml/DialogMusicInfo.xml
+++ b/addons/skin.estuary/xml/DialogMusicInfo.xml
@@ -527,6 +527,13 @@
</include>
</control>
</control>
+ <control type="group">
+ <centerleft>50%</centerleft>
+ <width>1920</width>
+ <bottom>0</bottom>
+ <height>70</height>
+ <include>MediaFlags</include>
+ </control>
<include condition="Skin.HasSetting(touchmode)">TouchBackButton</include>
</controls>
</window>
diff --git a/addons/skin.estuary/xml/DialogSeekBar.xml b/addons/skin.estuary/xml/DialogSeekBar.xml
index 7fa3a1ec12..5e870977a5 100644
--- a/addons/skin.estuary/xml/DialogSeekBar.xml
+++ b/addons/skin.estuary/xml/DialogSeekBar.xml
@@ -409,15 +409,23 @@
<orientation>vertical</orientation>
<itemgap>10</itemgap>
<control type="textbox">
+ <height>294</height>
+ <label fallback="10005">$INFO[VideoPlayer.Tagline,[B],[/B][CR]]$INFO[VideoPlayer.Plot]</label>
+ <align>left</align>
+ <autoscroll delay="5000" repeat="7500" time="5000"></autoscroll>
+ <visible>Integer.IsGreaterOrEqual(Playlist.Position(video), Playlist.Length(video))</visible>
+ </control>
+ <control type="textbox">
<height>250</height>
<label fallback="10005">$INFO[VideoPlayer.Tagline,[B],[/B][CR]]$INFO[VideoPlayer.Plot]</label>
<align>left</align>
<autoscroll delay="5000" repeat="7500" time="5000"></autoscroll>
+ <visible>Integer.IsLess(Playlist.Position(video), Playlist.Length(video))</visible>
</control>
<control type="label">
<height>50</height>
<label>$VAR[OSDNextLabelVar]</label>
- <visible>Integer.IsGreater(Playlist.Length(video),1)</visible>
+ <visible>Integer.IsLess(Playlist.Position(video), Playlist.Length(video))</visible>
</control>
</control>
</control>
diff --git a/addons/skin.estuary/xml/DialogVideoInfo.xml b/addons/skin.estuary/xml/DialogVideoInfo.xml
index cfbc08d621..92f7f8d84d 100644
--- a/addons/skin.estuary/xml/DialogVideoInfo.xml
+++ b/addons/skin.estuary/xml/DialogVideoInfo.xml
@@ -616,6 +616,12 @@
<param name="visible" value="String.IsEqual(ListItem.DBType,musicvideo)" />
</include>
<include content="InfoDialogButton">
+ <param name="id" value="14" />
+ <param name="icon" value="icons/infodialogs/versions.png" />
+ <param name="label" value="$LOCALIZE[40000]" />
+ <param name="visible" value="String.IsEqual(ListItem.DBType,movie)" />
+ </include>
+ <include content="InfoDialogButton">
<param name="id" value="443" />
<param name="icon" value="icons/infodialogs/set.png" />
<param name="label" value="$LOCALIZE[20457]" />
diff --git a/addons/skin.estuary/xml/DialogVideoVersion.xml b/addons/skin.estuary/xml/DialogVideoVersion.xml
new file mode 100644
index 0000000000..9a109ca028
--- /dev/null
+++ b/addons/skin.estuary/xml/DialogVideoVersion.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<window>
+ <defaultcontrol always="true">200</defaultcontrol>
+ <onload condition="Window.IsActive(videoversionselect)">Control.SetFocus(50)</onload>
+ <include>Animation_DialogPopupOpenClose</include>
+ <controls>
+ <include condition="Window.IsActive(videoversion)">DialogVideoVersionLayout</include>
+ <include condition="Window.IsActive(videoversionselect)">DialogVideoVersionSelectLayout</include>
+ </controls>
+</window>
diff --git a/addons/skin.estuary/xml/FileManager.xml b/addons/skin.estuary/xml/FileManager.xml
index e1ae38f7e3..757bc7d231 100644
--- a/addons/skin.estuary/xml/FileManager.xml
+++ b/addons/skin.estuary/xml/FileManager.xml
@@ -77,11 +77,10 @@
<animation effect="fade" start="0" end="100" time="400">WindowOpen</animation>
<animation effect="fade" start="100" end="0" time="300">WindowClose</animation>
<control type="label">
- <right>730</right>
+ <right>380</right>
<width>400</width>
<height>48</height>
<aligny>center</aligny>
- <align>right</align>
<shadowcolor>text_shadow</shadowcolor>
<label>$INFO[Container(21).CurrentItem,, / ]$INFO[Container(21).NumItems]</label>
</control>
@@ -97,10 +96,11 @@
<shadowcolor>black</shadowcolor>
</control>
<control type="label">
- <left>730</left>
+ <left>380</left>
<width>400</width>
<height>48</height>
<aligny>center</aligny>
+ <align>right</align>
<shadowcolor>text_shadow</shadowcolor>
<label>$INFO[Container(20).CurrentItem,, / ]$INFO[Container(20).NumItems]</label>
</control>
diff --git a/addons/skin.estuary/xml/GameOSD.xml b/addons/skin.estuary/xml/GameOSD.xml
index 454a30c4ba..0c00a592d4 100644
--- a/addons/skin.estuary/xml/GameOSD.xml
+++ b/addons/skin.estuary/xml/GameOSD.xml
@@ -204,7 +204,7 @@
<item>
<description>Pause / Resume button</description>
<label>$LOCALIZE[35224]</label>
- <label2>$LOCALIZE[31059]</label2>
+ <label2>$FEATURE[select,game.controller.snes] + $FEATURE[x,game.controller.default]</label2>
<icon>osd/fullscreen/buttons/play.png</icon>
<onclick>Play</onclick>
</item>
@@ -229,7 +229,7 @@
<item>
<description>Stop button</description>
<label>$LOCALIZE[35222]</label>
- <label2>$LOCALIZE[31060]</label2>
+ <label2>$FEATURE[select,game.controller.snes] + $FEATURE[start,game.controller.default]</label2>
<icon>osd/fullscreen/buttons/stop.png</icon>
<onclick>Stop</onclick>
</item>
diff --git a/addons/skin.estuary/xml/Includes.xml b/addons/skin.estuary/xml/Includes.xml
index 4e1452ddcc..8c928e633d 100644
--- a/addons/skin.estuary/xml/Includes.xml
+++ b/addons/skin.estuary/xml/Includes.xml
@@ -7,6 +7,7 @@
<include file="Includes_MediaMenu.xml" />
<include file="Includes_Buttons.xml" />
<include file="Includes_DialogSelect.xml" />
+ <include file="Includes_DialogVideoVersion.xml" />
<include file="Includes_MusicInfo.xml" />
<include file="Includes_PVR.xml" />
<include file="View_50_List.xml" />
@@ -355,6 +356,7 @@
<param name="resolution_var">$VAR[ResolutionFlagVar]</param>
<definition>
<control type="grouplist">
+ <visible>Window.IsActive(Home) | Window.IsActive(10025)</visible>
<orientation>horizontal</orientation>
<right>20</right>
<top>0</top>
@@ -419,6 +421,112 @@
<param name="visible" value="!String.IsEmpty($PARAM[infolabel_prefix]ListItem.AudioChannels)" />
</include>
</control>
+ <control type="grouplist">
+ <visible>Container.Content(albums) | Window.IsActive(10502)</visible>
+ <orientation>horizontal</orientation>
+ <right>20</right>
+ <top>0</top>
+ <height>70</height>
+ <align>right</align>
+ <itemgap>10</itemgap>
+ <width>1900</width>
+ <usecontrolcoords>true</usecontrolcoords>
+ <control type="group">
+ <width>115</width>
+ <visible>!String.IsEmpty($PARAM[infolabel_prefix]ListItem.Duration)</visible>
+ <visible>Container.Content(albums) | Window.IsActive(DialogMusicInfo.xml) + Container.Content(songs)</visible>
+ <control type="label">
+ <width>110</width>
+ <height>60</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[$PARAM[infolabel_prefix]ListItem.Duration]</label>
+ <font>font_flag</font>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="flags/flag.png" />
+ </include>
+ </control>
+ <control type="group">
+ <width>115</width>
+ <visible>!String.IsEmpty($PARAM[infolabel_prefix]ListItem.FileExtension)</visible>
+ <control type="label">
+ <width>110</width>
+ <height>60</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[$PARAM[infolabel_prefix]ListItem.FileExtension]</label>
+ <font>font_flag</font>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="flags/flag.png" />
+ </include>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="$INFO[$PARAM[infolabel_prefix]ListItem.MusicChannels,flags/audiochannel/,.png]" />
+ <param name="visible" value="!String.IsEmpty($PARAM[infolabel_prefix]ListItem.MusicChannels)" />
+ </include>
+ <control type="group">
+ <visible>!String.IsEmpty(ListItem.Samplerate)</visible>
+ <width>115</width>
+ <control type="label">
+ <width>110</width>
+ <height>60</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.Samplerate, ,kHz]</label>
+ <font>font_flag</font>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="flags/flag.png" />
+ </include>
+ </control>
+ <control type="group">
+ <visible>!String.IsEmpty(ListItem.BitRate)</visible>
+ <width>115</width>
+ <control type="label">
+ <width>110</width>
+ <height>60</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.BitRate, ,kbps]</label>
+ <font>font_flag</font>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="flags/flag.png" />
+ </include>
+ </control>
+ <control type="group">
+ <visible>!String.IsEmpty(ListItem.MusicBitsPerSample)</visible>
+ <width>115</width>
+ <control type="label">
+ <width>110</width>
+ <height>60</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.MusicBitsPerSample, ,bit]</label>
+ <font>font_flag</font>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="flags/flag.png" />
+ </include>
+ </control>
+ <control type="group">
+ <visible>!String.IsEmpty(ListItem.BMP)</visible>
+ <width>115</width>
+ <control type="label">
+ <width>110</width>
+ <height>60</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.BMP, ,bpm]</label>
+ <font>font_flag</font>
+ </control>
+ <include content="MediaFlag">
+ <param name="texture" value="flags/flag.png" />
+ </include>
+ </control>
+ </control>
</definition>
</include>
<include name="WeatherIconHome">
@@ -1439,7 +1547,7 @@
</control>
<include content="IconButton">
<param name="control_id" value="" />
- <param name="onclick" value="ActivateWindow(favourites)" />
+ <param name="onclick" value="ActivateWindow(favouritesbrowser)" />
<param name="icon" value="icons/favourites.png" />
<param name="label" value="$LOCALIZE[10134]" />
</include>
diff --git a/addons/skin.estuary/xml/Includes_Buttons.xml b/addons/skin.estuary/xml/Includes_Buttons.xml
index 167dc2e6aa..18939417cb 100644
--- a/addons/skin.estuary/xml/Includes_Buttons.xml
+++ b/addons/skin.estuary/xml/Includes_Buttons.xml
@@ -106,6 +106,7 @@
<param name="font">font25_title</param>
<param name="onclick"></param>
<param name="visible">true</param>
+ <param name="enable">true</param>
<definition>
<control type="button" id="$PARAM[id]">
<width>$PARAM[width]</width>
@@ -119,6 +120,37 @@
<texturefocus border="40" colordiffuse="button_focus">buttons/dialogbutton-fo.png</texturefocus>
<texturenofocus border="40">buttons/dialogbutton-nofo.png</texturenofocus>
<visible>$PARAM[visible]</visible>
+ <enable>$PARAM[enable]</enable>
+ </control>
+ </definition>
+ </include>
+ <include name="DialogToggleButton">
+ <param name="width">300</param>
+ <param name="height">100</param>
+ <param name="wrapmultiline">false</param>
+ <param name="font">font25_title</param>
+ <param name="onclick"></param>
+ <param name="visible">true</param>
+ <param name="enable">true</param>
+ <param name="usealttexture">false</param>
+ <definition>
+ <control type="togglebutton" id="$PARAM[id]">
+ <width>$PARAM[width]</width>
+ <height>$PARAM[height]</height>
+ <label>$PARAM[label]</label>
+ <font>$PARAM[font]</font>
+ <textoffsetx>20</textoffsetx>
+ <onclick>noop</onclick>
+ <wrapmultiline>$PARAM[wrapmultiline]</wrapmultiline>
+ <align>center</align>
+ <aligny>center</aligny>
+ <texturefocus border="40" colordiffuse="button_focus">buttons/dialogbutton-fo.png</texturefocus>
+ <texturenofocus border="40">buttons/dialogbutton-nofo.png</texturenofocus>
+ <alttexturefocus border="40" colordiffuse="button_focus">buttons/dialogbutton-fo.png</alttexturefocus>
+ <alttexturenofocus border="40" colordiffuse="button_alt_focus">buttons/dialogbutton-fo.png</alttexturenofocus>
+ <usealttexture>$PARAM[usealttexture]</usealttexture>
+ <visible>$PARAM[visible]</visible>
+ <enable>$PARAM[enable]</enable>
</control>
</definition>
</include>
diff --git a/addons/skin.estuary/xml/Includes_DialogVideoVersion.xml b/addons/skin.estuary/xml/Includes_DialogVideoVersion.xml
new file mode 100644
index 0000000000..4f7163c048
--- /dev/null
+++ b/addons/skin.estuary/xml/Includes_DialogVideoVersion.xml
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<includes>
+ <include name="DialogVideoVersionLayout">
+ <control type="group">
+ <centertop>50%</centertop>
+ <centerleft>50%</centerleft>
+ <width>1320</width>
+ <height>710</height>
+ <include>Animation_DialogPopupVisible</include>
+ <include content="DialogBackgroundCommons">
+ <param name="width" value="1320" />
+ <param name="height" value="710" />
+ <param name="header_id" value="2" />
+ </include>
+ <include>DialogVideoVersionCommonLayout</include>
+ <control type="grouplist" id="101">
+ <left>1000</left>
+ <top>90</top>
+ <width>300</width>
+ <height>565</height>
+ <onleft condition="ControlGroup(200).HasFocus(201) | Integer.IsGreater(Container(51).NumItems,0)">100</onleft>
+ <onleft condition="ControlGroup(200).HasFocus(202) + Integer.IsEqual(Container(51).NumItems,0)">200</onleft>
+ <itemgap>dialogbuttons_itemgap</itemgap>
+ <align>top</align>
+ <scrolltime tween="quadratic">200</scrolltime>
+ <include content="DefaultDialogButton">
+ <param name="id" value="21" />
+ <param name="label" value="$LOCALIZE[208]" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="22" />
+ <param name="label" value="$LOCALIZE[31612]" />
+ <param name="visible">ControlGroup(200).HasFocus(201)</param>
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="23" />
+ <param name="label" value="$LOCALIZE[31613]" />
+ <param name="visible">ControlGroup(200).HasFocus(202)</param>
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="27" />
+ <param name="label" value="$LOCALIZE[13511]" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="24" />
+ <param name="label" value="$LOCALIZE[118]" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="26" />
+ <param name="label" value="$LOCALIZE[15015]" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="25" />
+ <param name="label" value="$LOCALIZE[31614]" />
+ <param name="visible">ControlGroup(200).HasFocus(201)</param>
+ </include>
+ </control>
+ </control>
+ </include>
+ <include name="DialogVideoVersionSelectLayout">
+ <control type="group">
+ <centertop>50%</centertop>
+ <centerleft>50%</centerleft>
+ <width>1020</width>
+ <height>710</height>
+ <include>Animation_DialogPopupVisible</include>
+ <include content="DialogBackgroundCommons">
+ <param name="width" value="1020" />
+ <param name="height" value="710" />
+ <param name="header_id" value="2" />
+ </include>
+ <include>DialogVideoVersionCommonLayout</include>
+ </control>
+ </include>
+ <include name="DialogVideoVersionCommonLayout">
+ <control type="grouplist" id="200">
+ <left>20</left>
+ <top>90</top>
+ <height>180</height>
+ <orientation>vertical</orientation>
+ <itemgap>dialogbuttons_itemgap</itemgap>
+ <onright condition="ControlGroup(200).HasFocus(201)">50</onright>
+ <onright condition="ControlGroup(200).HasFocus(202)">51</onright>
+ <include content="DialogToggleButton">
+ <param name="id" value="201" />
+ <param name="label" value="$LOCALIZE[40000]" />
+ <param name="usealttexture" value="Control.IsVisible(50)" />
+ <param name="visible">Window.IsActive(videoversion)| Window.IsActive(videoversionselect) + Integer.IsGreater(Container(50).NumItems,0)</param>
+ </include>
+ <include content="DialogToggleButton">
+ <param name="id" value="202" />
+ <param name="label" value="$LOCALIZE[31615]" />
+ <param name="usealttexture" value="Control.IsVisible(51)" />
+ <param name="visible">Window.IsActive(videoversion) | Window.IsActive(videoversionselect) + Integer.IsGreater(Container(51).NumItems,0)</param>
+ </include>
+ </control>
+ <control type="image">
+ <top>270</top>
+ <left>40</left>
+ <width>260</width>
+ <height>390</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <aspectratio>keep</aspectratio>
+ <texture fallback="DefaultVideo.png" background="true">$VAR[VideoVersionPosterVar]</texture>
+ </control>
+ <control type="group" id="100">
+ <top>90</top>
+ <left>320</left>
+ <control type="image">
+ <top>0</top>
+ <left>0</left>
+ <width>680</width>
+ <height>600</height>
+ <texture border="21">dialogs/dialog-bg.png</texture>
+ </control>
+ <control type="list" id="50">
+ <description>version list</description>
+ <visible>ControlGroup(200).HasFocus(201)</visible>
+ <top>20</top>
+ <left>20</left>
+ <height>580</height>
+ <onup>50</onup>
+ <ondown>50</ondown>
+ <onleft>200</onleft>
+ <onright>60</onright>
+ <pagecontrol>60</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <include content="DefaultSimpleListLayout">
+ <param name="width" value="640" />
+ <param name="height" value="80" />
+ <param name="list_id" value="50" />
+ <param name="align" value="left" />
+ </include>
+ </control>
+ <control type="list" id="51">
+ <description>extras list</description>
+ <visible>ControlGroup(200).HasFocus(202)</visible>
+ <top>20</top>
+ <left>20</left>
+ <onup>51</onup>
+ <ondown>51</ondown>
+ <height>580</height>
+ <onleft>200</onleft>
+ <onright>60</onright>
+ <pagecontrol>60</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <include content="DefaultSimpleListLayout">
+ <param name="width" value="640" />
+ <param name="height" value="80" />
+ <param name="list_id" value="51" />
+ <param name="align" value="left" />
+ </include>
+ </control>
+ <control type="scrollbar" id="60">
+ <left>660</left>
+ <top>20</top>
+ <width>12</width>
+ <height>560</height>
+ <onleft condition="Control.IsVisible(50)">50</onleft>
+ <onleft condition="Control.IsVisible(51)">51</onleft>
+ <onright condition="Window.IsActive(videoversion)">101</onright>
+ <orientation>vertical</orientation>
+ </control>
+ </control>
+ <control type="label">
+ <visible>Control.HasFocus(50) | Control.HasFocus(51)</visible>
+ <align>left</align>
+ <top>675</top>
+ <left>40</left>
+ <right>40</right>
+ <height>44</height>
+ <scroll>true</scroll>
+ <font>font20_title</font>
+ <textcolor>99FFFFFF</textcolor>
+ <shadowcolor>text_shadow</shadowcolor>
+ <haspath>false</haspath>
+ <label>$VAR[VideoVersionFileVar]</label>
+ <animation effect="fade" start="0" end="100" time="300" delay="300">WindowOpen</animation>
+ <animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
+ <animation effect="fade" start="0" end="100" time="300">Visible</animation>
+ <animation effect="fade" start="100" end="0" time="200">Hidden</animation>
+ </control>
+ </include>
+</includes>
diff --git a/addons/skin.estuary/xml/Includes_Games.xml b/addons/skin.estuary/xml/Includes_Games.xml
index 8f1ce2d036..06302dfd13 100644
--- a/addons/skin.estuary/xml/Includes_Games.xml
+++ b/addons/skin.estuary/xml/Includes_Games.xml
@@ -372,6 +372,7 @@
<itemlayout width="96" height="96">
<control type="gamecontroller">
<texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(game.controllerid)]</controllerid>
<controlleraddress>$INFO[ListItem.FilenameAndPath]</controlleraddress>
<controllerdiffuse>button_focus</controllerdiffuse>
</control>
@@ -379,10 +380,56 @@
<focusedlayout width="96" height="96">
<control type="gamecontroller">
<texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(game.controllerid)]</controllerid>
<controlleraddress>$INFO[ListItem.FilenameAndPath]</controlleraddress>
<controllerdiffuse>button_focus</controllerdiffuse>
</control>
</focusedlayout>
+ <!--
+ Note to skinners: this control can be populated
+ with static content for testing.
+ <content>
+ <item>
+ <icon>DefaultAddonNone.png</icon>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.snes</property>
+ </item>
+ </content>
+ -->
</control>
</control>
<control type="group">
@@ -418,6 +465,48 @@
</control>
<include>GameDialogAgentLayout</include>
</focusedlayout>
+ <!--
+ Note to skinners: this control can be populated
+ with static content for testing.
+ <content>
+ <item>
+ <label>Player 1</label>
+ </item>
+ <item>
+ <label>Player 2</label>
+ </item>
+ <item>
+ <label>Player 3</label>
+ </item>
+ <item>
+ <label>Player 4</label>
+ </item>
+ <item>
+ <label>Player 5</label>
+ </item>
+ <item>
+ <label>Player 6</label>
+ </item>
+ <item>
+ <label>Player 7</label>
+ </item>
+ <item>
+ <label>Player 8</label>
+ </item>
+ <item>
+ <label>Player 9</label>
+ </item>
+ <item>
+ <label>Player 10</label>
+ </item>
+ <item>
+ <label>Player 11</label>
+ </item>
+ <item>
+ <label>Player 12</label>
+ </item>
+ </content>
+ -->
</control>
</control>
<control type="scrollbar" id="61">
@@ -465,16 +554,65 @@
<enable>false</enable>
<itemlayout width="96" height="96">
<control type="gamecontroller">
+ <texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(game.controllerid)]</controllerid>
<peripherallocation>$INFO[ListItem.FilenameAndPath]</peripherallocation>
<controllerdiffuse>button_focus</controllerdiffuse>
</control>
</itemlayout>
<focusedlayout width="96" height="96">
<control type="gamecontroller">
+ <texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(game.controllerid)]</controllerid>
<peripherallocation>$INFO[ListItem.FilenameAndPath]</peripherallocation>
<controllerdiffuse>button_focus</controllerdiffuse>
</control>
</focusedlayout>
+ <!--
+ Note to skinners: this control can be populated
+ with static content for testing.
+ <content>
+ <item>
+ <icon>DefaultAddonNone.png</icon>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ <item>
+ <property name="game.controllerid">game.controller.default</property>
+ </item>
+ </content>
+ -->
</control>
</include>
</includes>
diff --git a/addons/skin.estuary/xml/Includes_Home.xml b/addons/skin.estuary/xml/Includes_Home.xml
index 753e184082..606e4c2070 100644
--- a/addons/skin.estuary/xml/Includes_Home.xml
+++ b/addons/skin.estuary/xml/Includes_Home.xml
@@ -378,9 +378,9 @@
</control>
</control>
</focusedlayout>
- <content target="$PARAM[widget_target]" limit="$PARAM[item_limit]" browse="$PARAM[browse_mode]">$PARAM[content_path]</content>
<include condition="$PARAM[additional_movie_items]" content="MovieSubmenuItems" />
<include condition="$PARAM[additional_tvshow_items]" content="TVShowSubmenuItems" />
+ <content target="$PARAM[widget_target]" limit="$PARAM[item_limit]" browse="$PARAM[browse_mode]">$PARAM[content_path]</content>
</control>
</definition>
</include>
diff --git a/addons/skin.estuary/xml/MyMusicNav.xml b/addons/skin.estuary/xml/MyMusicNav.xml
index c83c429e60..84dfbc8f7f 100644
--- a/addons/skin.estuary/xml/MyMusicNav.xml
+++ b/addons/skin.estuary/xml/MyMusicNav.xml
@@ -27,6 +27,14 @@
<include content="BottomBar">
<param name="info_visible" value="true" />
</include>
+ <control type="group">
+ <depth>DepthBars</depth>
+ <bottom>0</bottom>
+ <height>70</height>
+ <animation effect="fade" start="0" end="100" time="300" delay="300">WindowOpen</animation>
+ <animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
+ <include condition="!Skin.HasSetting(hide_mediaflags)">MediaFlags</include>
+ </control>
<include>MediaMenuMouseOverlay</include>
<control type="group">
<include>MediaMenuCommon</include>
diff --git a/addons/skin.estuary/xml/MyPVRRecordings.xml b/addons/skin.estuary/xml/MyPVRRecordings.xml
index 67b4de541a..020f1ff093 100644
--- a/addons/skin.estuary/xml/MyPVRRecordings.xml
+++ b/addons/skin.estuary/xml/MyPVRRecordings.xml
@@ -28,7 +28,7 @@
<param name="info_icon" value="$VAR[ListPVRRecordingsIconVar]" />
<param name="has_info_icon" value="true" />
<param name="label1" value="$INFO[ListItem.Label]" />
- <param name="label2" value="$INFO[ListItem.Label2]" />
+ <param name="label2" value="$VAR[ListLabel2Var]" />
</include>
</control>
</control>
diff --git a/addons/skin.estuary/xml/MyWeather.xml b/addons/skin.estuary/xml/MyWeather.xml
index bb7925c214..7eb733f136 100644
--- a/addons/skin.estuary/xml/MyWeather.xml
+++ b/addons/skin.estuary/xml/MyWeather.xml
@@ -68,6 +68,7 @@
<scrolltime tween="cubic" easing="out">500</scrolltime>
<include>OpenClose_Right</include>
<itemgap>-160</itemgap>
+ <visible>!String.StartsWith(Weather.Temperature,$LOCALIZE[503])</visible>
<control type="group" id="567">
<description>Weather info</description>
<height>410</height>
@@ -112,7 +113,7 @@
<top>194</top>
<aligny>center</aligny>
<height>24</height>
- <width>500</width>
+ <width>300</width>
<align>right</align>
<font>WeatherTemp</font>
<label>$INFO[Weather.Temperature]</label>
@@ -203,5 +204,39 @@
<param name="sublabel" value="$INFO[Window(weather).Property(WeatherProvider)]" />
</include>
<include>BottomBar</include>
+ <control type="group">
+ <visible>String.StartsWith(Weather.Temperature,$LOCALIZE[503])</visible>
+ <animation effect="fade" time="400">VisibleChange</animation>
+ <control type="image">
+ <texture>colors/black.png</texture>
+ <include>FullScreenDimensions</include>
+ <animation effect="fade" start="100" end="70" time="0" condition="true">Conditional</animation>
+ </control>
+ <control type="group">
+ <depth>DepthMax</depth>
+ <centerleft>50%</centerleft>
+ <centertop>50%</centertop>
+ <width>80</width>
+ <height>80</height>
+ <control type="image">
+ <centerleft>50%</centerleft>
+ <centertop>50%</centertop>
+ <width>80</width>
+ <height>80</height>
+ <aspectratio>keep</aspectratio>
+ <animation effect="rotate" end="-45" center="auto" time="200" delay="600" loop="true" reversible="false" condition="true">Conditional</animation>
+ <texture colordiffuse="button_focus">spinner.png</texture>
+ </control>
+ <control type="image">
+ <centerleft>50%</centerleft>
+ <centertop>50%</centertop>
+ <width>50</width>
+ <height>50</height>
+ <aspectratio>keep</aspectratio>
+ <animation effect="rotate" end="45" center="auto" time="200" delay="600" loop="true" reversible="false" condition="true">Conditional</animation>
+ <texture flipx="true" colordiffuse="button_focus">spinner.png</texture>
+ </control>
+ </control>
+ </control>
</controls>
</window>
diff --git a/addons/skin.estuary/xml/SettingsCategory.xml b/addons/skin.estuary/xml/SettingsCategory.xml
index cb18ef4458..c14b63584b 100644
--- a/addons/skin.estuary/xml/SettingsCategory.xml
+++ b/addons/skin.estuary/xml/SettingsCategory.xml
@@ -175,7 +175,7 @@
<left>510</left>
<bottom>25</bottom>
<right>60</right>
- <height>102</height>
+ <height>104</height>
<font>font12</font>
<align>justify</align>
<textcolor>button_focus</textcolor>
diff --git a/addons/skin.estuary/xml/Variables.xml b/addons/skin.estuary/xml/Variables.xml
index b0a7461f27..7500a69616 100644
--- a/addons/skin.estuary/xml/Variables.xml
+++ b/addons/skin.estuary/xml/Variables.xml
@@ -89,6 +89,7 @@
<value condition="String.IsEmpty(Container.PluginName) + Container.Content(tvshows) + Container.SortMethod(29)">$VAR[WatchedStatusVar]</value>
<value condition="String.IsEmpty(Container.PluginName) + String.IsEqual(ListItem.DBType,season) + Container.SortMethod(29)">$VAR[WatchedStatusVar]</value>
<value condition="String.IsEmpty(Container.PluginName) + String.IsEqual(ListItem.DBType,set) + Container.SortMethod(1)">$VAR[WatchedStatusVar]</value>
+ <value condition="ListItem.IsFolder + [Window.IsActive(TVRecordings) | Window.IsActive(RadioRecordings)]">$VAR[WatchedStatusVar]</value>
<value condition="Container.SortMethod(7) | Container.SortMethod(29)">$INFO[ListItem.Year]</value>
<value condition="!String.isempty(ListItem.Appearances)">$LOCALIZE[38026]: $INFO[ListItem.Appearances]</value>
<value condition="Window.IsActive(musicplaylist) | Window.IsActive(videoplaylist)">$INFO[ListItem.Duration]</value>
@@ -289,6 +290,7 @@
<variable name="WatchedStatusVar">
<value condition="String.IsEqual(Listitem.DBType,tvshow) | String.IsEqual(Listitem.DBType,season)">$INFO[ListItem.Property(WatchedEpisodes)]$INFO[ListItem.Property(TotalEpisodes), / ,]</value>
<value condition="String.IsEqual(Listitem.DBType,set)">$INFO[ListItem.Property(Watched)]$INFO[ListItem.Property(Total), / ,]</value>
+ <value condition="ListItem.IsFolder + [Window.IsActive(TVRecordings) | Window.IsActive(RadioRecordings)]">$INFO[ListItem.Property(WatchedEpisodes)]$INFO[ListItem.Property(TotalEpisodes), / ,]</value>
</variable>
<variable name="VideoPlayerForwardRewindVar">
<value condition="Player.Forwarding2x | Player.Rewinding2x">2x</value>
@@ -417,10 +419,10 @@
<value>$INFO[VideoPlayer.Genre]</value>
</variable>
<variable name="OSDNextLabelVar">
- <value condition="Window.IsActive(visualisation)">$INFO[MusicPlayer.offset(1).Title,[COLOR button_focus]$LOCALIZE[19031]: [/COLOR]]$INFO[MusicPlayer.offset(1).Artist, - ]$INFO[MusicPlayer.offset(1).Album, - ]$INFO[MusicPlayer.offset(1).Year, (,)]</value>
<value condition="VideoPlayer.Content(musicvideos)">$INFO[VideoPlayer.offset(1).Title,[COLOR button_focus]$LOCALIZE[19031]: [/COLOR]]$INFO[VideoPlayer.offset(1).Artist, - ]$INFO[VideoPlayer.offset(1).Album, - ]$INFO[VideoPlayer.offset(1).Year, (,)]</value>
<value condition="VideoPlayer.Content(livetv)">$INFO[VideoPlayer.NextStartTime,[COLOR button_focus]$LOCALIZE[19031]: [/COLOR]]$INFO[VideoPlayer.NextEndTime, - ]$INFO[VideoPlayer.NextTitle,: ]</value>
<value condition="VideoPlayer.Content(episodes) + Window.IsActive(fullscreenvideo)">$INFO[VideoPlayer.offset(1).TVShowtitle,[COLOR button_focus]$LOCALIZE[19031]: [/COLOR]]$INFO[VideoPlayer.offset(1).Season, - S,]$INFO[VideoPlayer.offset(1).Episode,E]$INFO[VideoPlayer.offset(1).Title, - ]</value>
+ <value condition="Window.IsActive(visualisation)">$INFO[MusicPlayer.offset(1).Title,[COLOR button_focus]$LOCALIZE[19031]: [/COLOR]]$INFO[MusicPlayer.offset(1).Artist, - ]$INFO[MusicPlayer.offset(1).Album, - ]$INFO[MusicPlayer.offset(1).Year, (,)]</value>
<value>$INFO[VideoPlayer.offset(1).Title,[COLOR button_focus]$LOCALIZE[19031]: [/COLOR]]$INFO[VideoPlayer.offset(1).Year, (,)]</value>
</variable>
<variable name="PlayerClearLogoVar">
@@ -462,6 +464,7 @@
<variable name="ListWatchedIconVar">
<value condition="ListItem.IsRecording">windows/pvr/record.png</value>
<value condition="ListItem.IsCollection">overlays/set.png</value>
+ <value condition="ListItem.HasVideoVersions">overlays/versions.png</value>
<value condition="ListItem.IsPlaying">overlays/watched/OverlayPlaying-List.png</value>
<value condition="ListItem.IsResumable">overlays/watched/resume.png</value>
<value condition="ListItem.IsFolder + String.IsEmpty(Listitem.dbtype) + !String.IsEqual(ListItem.Overlay,OverlayWatched.png) + !ListItem.IsParentFolder">overlays/folder.png</value>
@@ -473,6 +476,7 @@
<value condition="ListItem.HasReminder">icons/pvr/timers/bell.png</value>
<value condition="ListItem.HasTimer">icons/pvr/timers/recording.png</value>
<value condition="ListItem.IsCollection">overlays/set.png</value>
+ <value condition="ListItem.HasVideoVersions">overlays/versions.png</value>
<value condition="ListItem.IsPlaying">overlays/watched/OverlayPlaying-List.png</value>
<value condition="ListItem.IsResumable">overlays/watched/resume.png</value>
<value condition="ListItem.HasArchive">windows/pvr/archive.png</value>
@@ -672,4 +676,15 @@
<value condition="VideoPlayer.SubtitlesEnabled">[B]$INFO[VideoPlayer.SubtitlesLanguage][/B]</value>
<value>[B]$LOCALIZE[1223][/B]</value>
</variable>
+ <variable name="VideoVersionPosterVar">
+ <value condition="ControlGroup(200).HasFocus(201) + !String.IsEmpty(Container(50).ListItem.Art(poster))">$INFO[Container(50).ListItem.Art(poster)]</value>
+ <value condition="ControlGroup(200).HasFocus(201) + String.IsEmpty(Container(51).ListItem.Art(poster))">$INFO[Container(50).ListItem.Art(thumb)]</value>
+ <value condition="ControlGroup(200).HasFocus(202) + !String.IsEmpty(Container(51).ListItem.Art(poster))">$INFO[Container(51).ListItem.Art(poster)]</value>
+ <value condition="ControlGroup(200).HasFocus(202) + String.IsEmpty(Container(51).ListItem.Art(poster))">$INFO[Container(51).ListItem.Art(thumb)]</value>
+ </variable>
+ <variable name="VideoVersionFileVar">
+ <value condition="Control.HasFocus(50)">$INFO[Container(50).ListItem.FileNameAndPath]</value>
+ <value condition="Control.HasFocus(51)">$INFO[Container(51).ListItem.FileNameAndPath]</value>
+ <value>$INFO[ListItem.FileNameAndPath]</value>
+ </variable>
</includes>
diff --git a/addons/skin.estuary/xml/View_53_Shift.xml b/addons/skin.estuary/xml/View_53_Shift.xml
index 4c125fbbbf..1c1e908710 100644
--- a/addons/skin.estuary/xml/View_53_Shift.xml
+++ b/addons/skin.estuary/xml/View_53_Shift.xml
@@ -62,179 +62,10 @@
<preloaditems>1</preloaditems>
<viewtype label="31100">icon</viewtype>
<itemlayout width="370">
- <control type="image">
- <left>0</left>
- <top>90</top>
- <width>370</width>
- <height>480</height>
- <texture>dialogs/dialog-bg-nobo.png</texture>
- <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
- <bordersize>20</bordersize>
- <visible>String.IsEmpty(ListItem.Art(poster)) + [Container.Content(movies) | Container.Content(tvshows)]</visible>
- </control>
- <control type="image">
- <depth>DepthContentPopout</depth>
- <left>0</left>
- <top>90</top>
- <width>370</width>
- <height>480</height>
- <aspectratio aligny="center">keep</aspectratio>
- <texture fallback="DefaultVideo.png" background="true">$VAR[ShiftThumbVar]</texture>
- <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
- <bordersize>20</bordersize>
- </control>
- <control type="textbox">
- <left>20</left>
- <top>603</top>
- <width>330</width>
- <height>105</height>
- <align>center</align>
- <aligny>center</aligny>
- <label>$INFO[ListItem.Label]</label>
- </control>
- <control type="group">
- <visible>String.IsEqual(ListItem.DBtype,tvshow) | String.IsEqual(ListItem.DBtype,set) | String.IsEqual(ListItem.DBType,season)</visible>
- <control type="image">
- <left>35</left>
- <top>500</top>
- <width>298</width>
- <height>50</height>
- <texture colordiffuse="CCFFFFFF">overlays/overlayfade.png</texture>
- <visible>!String.IsEmpty(ListItem.Art(poster))</visible>
- </control>
- <control type="label">
- <left>0</left>
- <top>522</top>
- <width>292</width>
- <height>24</height>
- <label>$VAR[WatchedStatusVar]</label>
- <font>font20_title</font>
- <shadowcolor>text_shadow</shadowcolor>
- <align>right</align>
- <aligny>center</aligny>
- </control>
- <control type="image">
- <left>302</left>
- <top>522</top>
- <width>24</width>
- <height>24</height>
- <texture>lists/played-total.png</texture>
- <align>right</align>
- <aligny>center</aligny>
- </control>
- </control>
- <control type="image">
- <left>35</left>
- <top>518</top>
- <width>32</width>
- <height>32</height>
- <align>left</align>
- <aligny>center</aligny>
- <texture>$VAR[WallWatchedIconVar]</texture>
- </control>
- <control type="group">
- <left>158</left>
- <top>92</top>
- <include condition="Skin.HasSetting(circle_rating) | Skin.HasSetting(circle_userrating)">RatingCircle</include>
- </control>
- <control type="progress">
- <left>32</left>
- <top>530</top>
- <width>298</width>
- <height>1</height>
- <texturebg></texturebg>
- <midtexture colordiffuse="button_focus" border="3">progress/texturebg_alt_white.png</midtexture>
- <info>ListItem.PercentPlayed</info>
- <visible>!Integer.IsEqual(ListItem.PercentPlayed,0)</visible>
- </control>
+ <include content="ShiftImageBox" />
</itemlayout>
<focusedlayout width="370">
- <control type="image">
- <left>0</left>
- <top>90</top>
- <width>370</width>
- <height>480</height>
- <texture>dialogs/dialog-bg-nobo.png</texture>
- <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
- <bordersize>20</bordersize>
- <visible>String.IsEmpty(ListItem.Art(poster)) + [Container.Content(movies) | Container.Content(tvshows)]</visible>
- </control>
- <control type="image">
- <depth>DepthContentPopout</depth>
- <left>0</left>
- <top>90</top>
- <width>370</width>
- <height>480</height>
- <aspectratio aligny="center">keep</aspectratio>
- <texture fallback="DefaultVideo.png" background="true">$VAR[ShiftThumbVar]</texture>
- <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
- <bordersize>20</bordersize>
- </control>
- <control type="textbox">
- <left>20</left>
- <top>603</top>
- <width>330</width>
- <height>105</height>
- <align>center</align>
- <aligny>center</aligny>
- <label>$INFO[ListItem.Label]</label>
- <autoscroll time="3000" delay="3000" repeat="3000">True</autoscroll>
- </control>
- <control type="group">
- <visible>String.IsEqual(ListItem.DBtype,tvshow) | String.IsEqual(ListItem.DBtype,set) | String.IsEqual(ListItem.DBType,season)</visible>
- <control type="image">
- <left>35</left>
- <top>500</top>
- <width>298</width>
- <height>50</height>
- <texture colordiffuse="CCFFFFFF">overlays/overlayfade.png</texture>
- <visible>!String.IsEmpty(ListItem.Art(poster))</visible>
- </control>
- <control type="label">
- <left>0</left>
- <top>522</top>
- <width>292</width>
- <height>24</height>
- <label>$VAR[WatchedStatusVar]</label>
- <font>font20_title</font>
- <shadowcolor>text_shadow</shadowcolor>
- <align>right</align>
- <aligny>center</aligny>
- </control>
- <control type="image">
- <left>302</left>
- <top>522</top>
- <width>24</width>
- <height>24</height>
- <texture>lists/played-total.png</texture>
- <align>right</align>
- <aligny>center</aligny>
- </control>
- </control>
- <control type="image">
- <left>35</left>
- <top>518</top>
- <width>32</width>
- <height>32</height>
- <align>left</align>
- <aligny>center</aligny>
- <texture>$VAR[WallWatchedIconVar]</texture>
- </control>
- <control type="group">
- <left>158</left>
- <top>92</top>
- <include condition="Skin.HasSetting(circle_rating) | Skin.HasSetting(circle_userrating)">RatingCircle</include>
- </control>
- <control type="progress">
- <left>32</left>
- <top>530</top>
- <width>298</width>
- <height>1</height>
- <texturebg></texturebg>
- <midtexture colordiffuse="button_focus" border="3">progress/texturebg_alt_white.png</midtexture>
- <info>ListItem.PercentPlayed</info>
- <visible>!Integer.IsEqual(ListItem.PercentPlayed,0)</visible>
- </control>
+ <include content="ShiftImageBox" />
</focusedlayout>
</control>
</control>
@@ -260,26 +91,26 @@
<control type="panel">
<left>20</left>
<top>48</top>
- <width>1880</width>
+ <width>940</width>
<height>180</height>
<orientation>horizontal</orientation>
<visible>ListItem.IsCollection</visible>
<animation effect="fade" time="200">VisibleChange</animation>
- <focusedlayout height="40" width="628">
+ <focusedlayout height="40" width="470">
<control type="label">
<textoffsetx>10</textoffsetx>
<height>40</height>
- <width>628</width>
+ <width>460</width>
<aligny>center</aligny>
<label>$INFO[ListItem.Year,[COLOR button_focus],[/COLOR] - ]$INFO[ListItem.Title]</label>
<shadowcolor>text_shadow</shadowcolor>
</control>
</focusedlayout>
- <itemlayout height="40" width="628">
+ <itemlayout height="40" width="470">
<control type="label">
<textoffsetx>10</textoffsetx>
<height>40</height>
- <width>628</width>
+ <width>460</width>
<aligny>center</aligny>
<label>$INFO[ListItem.Year,[COLOR button_focus],[/COLOR] - ]$INFO[ListItem.Title]</label>
<shadowcolor>text_shadow</shadowcolor>
@@ -290,6 +121,68 @@
</control>
</control>
</include>
+ <include name="ShiftImageBox">
+ <control type="image">
+ <left>18</left>
+ <top>90</top>
+ <width>334</width>
+ <height>480</height>
+ <aspectratio aligny="center">stretch</aspectratio>
+ <texture>dialogs/dialog-bg-nobo.png</texture>
+ <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
+ <bordersize>20</bordersize>
+ <visible>String.IsEmpty(ListItem.Art(thumb)) + !Container.Content(albums) | String.IsEmpty(ListItem.Art(poster)) + [Container.Content(movies) | Container.Content(tvshows)]</visible>
+ </control>
+ <control type="image">
+ <depth>DepthContentPopout</depth>
+ <left>18</left>
+ <top>90</top>
+ <width>334</width>
+ <height>480</height>
+ <aspectratio aligny="center">keep</aspectratio>
+ <texture fallback="DefaultVideo.png" background="true">$VAR[ShiftThumbVar]</texture>
+ <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
+ <bordersize>20</bordersize>
+ <visible>String.IsEmpty(ListItem.Art(poster))</visible>
+ </control>
+ <control type="image">
+ <depth>DepthContentPopout</depth>
+ <left>18</left>
+ <top>90</top>
+ <width>334</width>
+ <height>480</height>
+ <aspectratio aligny="center">scale</aspectratio>
+ <texture fallback="DefaultVideo.png" background="true">$VAR[ShiftThumbVar]</texture>
+ <bordertexture border="21" infill="false">overlays/shadow.png</bordertexture>
+ <bordersize>20</bordersize>
+ <visible>!String.IsEmpty(ListItem.Art(poster))</visible>
+ </control>
+ <control type="textbox">
+ <left>20</left>
+ <top>603</top>
+ <width>330</width>
+ <height>105</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.Label]</label>
+ </control>
+ <include>IconStatusOverlay</include>
+ <control type="group">
+ <left>158</left>
+ <top>92</top>
+ <include condition="Skin.HasSetting(circle_rating) | Skin.HasSetting(circle_userrating)">RatingCircle</include>
+ </control>
+ <control type="progress">
+ <left>32</left>
+ <top>530</top>
+ <width>298</width>
+ <height>1</height>
+ <texturebg></texturebg>
+ <midtexture colordiffuse="button_focus" border="3">progress/texturebg_alt_white.png</midtexture>
+ <info>ListItem.PercentPlayed</info>
+ <visible>!Integer.IsEqual(ListItem.PercentPlayed,0)</visible>
+ </control>
+ </include>
<include name="ShiftTextbox">
<control type="group">
<animation effect="fade" time="200" start="0" end="100" condition="!String.IsEmpty(Control.GetLabel($PARAM[textbox_id]))">Conditional</animation>
@@ -304,4 +197,48 @@
</control>
</control>
</include>
+ <include name="IconStatusOverlay">
+ <control type="group">
+ <visible>String.IsEqual(ListItem.DBtype,tvshow) | String.IsEqual(ListItem.DBtype,set) | String.IsEqual(ListItem.DBType,season) | String.IsEqual(ListItem.IsCollection) | String.IsEqual(ListItem.HasVideoVersions) | String.IsEqual(ListItem.IsResumable) | Integer.IsGreater(ListItem.Playcount,0)</visible>
+ <control type="image">
+ <left>38</left>
+ <top>500</top>
+ <width>294</width>
+ <height>50</height>
+ <texture colordiffuse="CCFFFFFF">overlays/overlayfade.png</texture>
+ <visible>!String.IsEmpty(ListItem.Art(poster))</visible>
+ </control>
+ <control type="label">
+ <left>75</left>
+ <top>522</top>
+ <width>222</width>
+ <height>24</height>
+ <label>$VAR[WatchedStatusVar]</label>
+ <font>font20_title</font>
+ <shadowcolor>text_shadow</shadowcolor>
+ <align>right</align>
+ <aligny>center</aligny>
+ <visible>String.IsEqual(ListItem.DBtype,tvshow) | String.IsEqual(ListItem.DBtype,set) | String.IsEqual(ListItem.DBType,season)</visible>
+ </control>
+ <control type="image">
+ <left>302</left>
+ <top>522</top>
+ <width>24</width>
+ <height>24</height>
+ <texture>lists/played-total.png</texture>
+ <align>right</align>
+ <aligny>center</aligny>
+ <visible>String.IsEqual(ListItem.DBtype,tvshow) | String.IsEqual(ListItem.DBtype,set) | String.IsEqual(ListItem.DBType,season)</visible>
+ </control>
+ <control type="image">
+ <left>45</left>
+ <top>522</top>
+ <width>24</width>
+ <height>24</height>
+ <align>left</align>
+ <aligny>center</aligny>
+ <texture>$VAR[WallWatchedIconVar]</texture>
+ </control>
+ </control>
+ </include>
</includes>
diff --git a/addons/xbmc.gui/addon.xml b/addons/xbmc.gui/addon.xml
index 91a9340080..6f914372b5 100644
--- a/addons/xbmc.gui/addon.xml
+++ b/addons/xbmc.gui/addon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<addon id="xbmc.gui" version="5.16.0" provider-name="Team Kodi">
+<addon id="xbmc.gui" version="5.17.0" provider-name="Team Kodi">
<backwards-compatibility abi="5.15.0"/>
<requires>
<import addon="xbmc.core" version="0.1.0"/>
diff --git a/cmake/modules/FindAcbAPI.cmake b/cmake/modules/FindAcbAPI.cmake
new file mode 100644
index 0000000000..25575b4997
--- /dev/null
+++ b/cmake/modules/FindAcbAPI.cmake
@@ -0,0 +1,42 @@
+#.rst:
+# FindAcbAPI
+# --------
+# Finds the AcbAPI library
+#
+# This will define the following target:
+#
+# ACBAPI::ACBAPI - The acbAPI library
+
+if(NOT TARGET ACBAPI::ACBAPI)
+ find_package(PkgConfig)
+ if(PKG_CONFIG_FOUND)
+ pkg_check_modules(PC_ACBAPI libAcbAPI QUIET)
+ endif()
+
+ find_path(ACBAPI_INCLUDE_DIR NAMES appswitching-control-block/AcbAPI.h
+ PATHS ${PC_ACBAPI_INCLUDEDIR}
+ NO_CACHE)
+ find_library(ACBAPI_LIBRARY NAMES AcbAPI
+ PATHS ${PC_ACBAPI_LIBDIR}
+ NO_CACHE)
+
+ set(ACBAPI_VERSION ${PC_ACBAPI_VERSION})
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(AcbAPI
+ REQUIRED_VARS ACBAPI_LIBRARY ACBAPI_INCLUDE_DIR
+ VERSION_VAR ACBAPI_VERSION)
+
+ if(ACBAPI_FOUND)
+ add_library(ACBAPI::ACBAPI UNKNOWN IMPORTED)
+ set_target_properties(ACBAPI::ACBAPI PROPERTIES
+ IMPORTED_LOCATION "${ACBAPI_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${ACBAPI_INCLUDE_DIR}")
+ set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP ACBAPI::ACBAPI)
+
+ # creates an empty library to install on webOS 5+ devices
+ file(TOUCH dummy.c)
+ add_library(AcbAPI SHARED dummy.c)
+ set_target_properties(AcbAPI PROPERTIES VERSION 1.0.0 SOVERSION 1)
+ endif()
+endif()
diff --git a/cmake/modules/FindCEC.cmake b/cmake/modules/FindCEC.cmake
index b3874ff883..51b6cbb972 100644
--- a/cmake/modules/FindCEC.cmake
+++ b/cmake/modules/FindCEC.cmake
@@ -8,30 +8,116 @@
# CEC::CEC - The libCEC library
if(NOT TARGET CEC::CEC)
- find_package(PkgConfig)
- if(PKG_CONFIG_FOUND)
- pkg_check_modules(PC_CEC libcec QUIET)
- endif()
+ include(cmake/scripts/common/ModuleHelpers.cmake)
+
+ macro(buildCEC)
+ set(CEC_VERSION ${${MODULE}_VER})
+
+ set(patches "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/001-all-cmakelists.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/002-all-libceccmakelists.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/003-all-remove_git_info.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/004-win-remove_32bit_timet.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/005-win-pdbstatic.patch")
+
+ generate_patchcommand("${patches}")
+
+ set(CMAKE_ARGS -DBUILD_SHARED_LIBS=ON
+ -DSKIP_PYTHON_WRAPPER=ON
+ -DCMAKE_PLATFORM_NO_VERSIONED_SONAME=ON)
- find_path(CEC_INCLUDE_DIR NAMES libcec/cec.h libCEC/CEC.h
- PATHS ${PC_CEC_INCLUDEDIR}
- NO_CACHE)
+ if(WIN32 AND ARCH STREQUAL "x64")
+ # Disable _USE_32BIT_TIME_T for x64 win target
+ list(APPEND CMAKE_ARGS -DWIN64=ON)
+ endif()
- if(PC_CEC_VERSION)
- set(CEC_VERSION ${PC_CEC_VERSION})
- elseif(CEC_INCLUDE_DIR AND EXISTS "${CEC_INCLUDE_DIR}/libcec/version.h")
- file(STRINGS "${CEC_INCLUDE_DIR}/libcec/version.h" cec_version_str REGEX "^[\t ]+LIBCEC_VERSION_TO_UINT\\(.*\\)")
- string(REGEX REPLACE "^[\t ]+LIBCEC_VERSION_TO_UINT\\(([0-9]+), ([0-9]+), ([0-9]+)\\)" "\\1.\\2.\\3" CEC_VERSION "${cec_version_str}")
- unset(cec_version_str)
+ if(CORE_SYSTEM_NAME STREQUAL "osx")
+ set(CEC_BYPRODUCT_EXTENSION "dylib")
+ endif()
+
+ BUILD_DEP_TARGET()
+
+ if(CORE_SYSTEM_NAME STREQUAL "osx")
+ find_program(INSTALL_NAME_TOOL NAMES install_name_tool)
+ add_custom_command(TARGET cec POST_BUILD
+ COMMAND ${INSTALL_NAME_TOOL} -id ${CEC_LIBRARY} ${CEC_LIBRARY})
+ endif()
+
+ add_dependencies(cec P8Platform::P8Platform)
+ endmacro()
+
+ # We only need to check p8-platform if we have any intention to build internal
+ if(ENABLE_INTERNAL_CEC)
+ # Check for dependencies - Must be done before SETUP_BUILD_VARS
+ get_libversion_data("p8-platform" "target")
+ find_package(P8Platform ${LIB_P8-PLATFORM_VER} MODULE QUIET REQUIRED)
+ # Check if we want to force a build due to a dependency rebuild
+ get_property(LIB_FORCE_REBUILD TARGET P8Platform::P8Platform PROPERTY LIB_BUILD)
endif()
- if(NOT CEC_FIND_VERSION)
- set(CEC_FIND_VERSION 4.0.0)
+ set(MODULE_LC cec)
+
+ SETUP_BUILD_VARS()
+
+ # Check for existing libcec. If version >= LIBCEC-VERSION file version, dont build
+ find_package(libcec CONFIG
+ HINTS ${DEPENDS_PATH}/lib/cmake
+ ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
+
+ if((libcec_VERSION VERSION_LESS ${${MODULE}_VER} AND ENABLE_INTERNAL_CEC) OR LIB_FORCE_REBUILD)
+ # Build lib
+ buildCEC()
+ else()
+ # if libcec::cec target exists, it meets version requirements
+ # we only do a pkgconfig search when a suitable cmake config returns nothing
+ if(TARGET libcec::cec)
+ get_target_property(_CEC_CONFIGURATIONS libcec::cec IMPORTED_CONFIGURATIONS)
+ foreach(_cec_config IN LISTS _CEC_CONFIGURATIONS)
+ # Some non standard config (eg None on Debian)
+ # Just set to RELEASE var so select_library_configurations can continue to work its magic
+ string(TOUPPER ${_cec_config} _cec_config_UPPER)
+ if((NOT ${_cec_config_UPPER} STREQUAL "RELEASE") AND
+ (NOT ${_cec_config_UPPER} STREQUAL "DEBUG"))
+ get_target_property(CEC_LIBRARY_RELEASE libcec::cec IMPORTED_LOCATION_${_cec_config_UPPER})
+ else()
+ get_target_property(CEC_LIBRARY_${_cec_config_UPPER} libcec::cec IMPORTED_LOCATION_${_cec_config_UPPER})
+ endif()
+ endforeach()
+
+ # CEC cmake config doesnt include INTERFACE_INCLUDE_DIRECTORIES
+ find_path(CEC_INCLUDE_DIR NAMES libcec/cec.h libCEC/CEC.h
+ HINTS ${DEPENDS_PATH}/include ${PC_CEC_INCLUDEDIR}
+ ${${CORE_PLATFORM_LC}_SEARCH_CONFIG}
+ NO_CACHE)
+ set(CEC_VERSION ${libcec_VERSION})
+ else()
+ find_package(PkgConfig)
+ # Fallback to pkg-config and individual lib/include file search
+ if(PKG_CONFIG_FOUND)
+ pkg_check_modules(PC_CEC libcec QUIET)
+ endif()
+ find_library(CEC_LIBRARY_RELEASE NAMES cec
+ HINTS ${DEPENDS_PATH}/lib ${PC_CEC_LIBDIR}
+ ${${CORE_PLATFORM_LC}_SEARCH_CONFIG}
+ NO_CACHE)
+
+ find_path(CEC_INCLUDE_DIR NAMES libcec/cec.h libCEC/CEC.h
+ HINTS ${DEPENDS_PATH}/include ${PC_CEC_INCLUDEDIR}
+ ${${CORE_PLATFORM_LC}_SEARCH_CONFIG}
+ NO_CACHE)
+
+ if(PC_CEC_VERSION)
+ set(CEC_VERSION ${PC_CEC_VERSION})
+ elseif(CEC_INCLUDE_DIR AND EXISTS "${CEC_INCLUDE_DIR}/libcec/version.h")
+ file(STRINGS "${CEC_INCLUDE_DIR}/libcec/version.h" cec_version_str REGEX "^[\t ]+LIBCEC_VERSION_TO_UINT\\(.*\\)")
+ string(REGEX REPLACE "^[\t ]+LIBCEC_VERSION_TO_UINT\\(([0-9]+), ([0-9]+), ([0-9]+)\\)" "\\1.\\2.\\3" CEC_VERSION "${cec_version_str}")
+ unset(cec_version_str)
+ endif()
+ endif()
endif()
- find_library(CEC_LIBRARY NAMES cec
- PATHS ${PC_CEC_LIBDIR}
- NO_CACHE)
+ include(SelectLibraryConfigurations)
+ select_library_configurations(CEC)
+ unset(CEC_LIBRARIES)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CEC
@@ -39,11 +125,47 @@ if(NOT TARGET CEC::CEC)
VERSION_VAR CEC_VERSION)
if(CEC_FOUND)
- add_library(CEC::CEC UNKNOWN IMPORTED)
- set_target_properties(CEC::CEC PROPERTIES
- IMPORTED_LOCATION "${CEC_LIBRARY}"
- INTERFACE_INCLUDE_DIRECTORIES "${CEC_INCLUDE_DIR}"
- INTERFACE_COMPILE_DEFINITIONS HAVE_LIBCEC=1)
+ # cmake target and not building internal
+ if(TARGET libcec::cec AND NOT TARGET cec)
+ add_library(CEC::CEC ALIAS libcec::cec)
+ # We need to append in case the cmake config already has definitions
+ set_property(TARGET libcec::cec APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS HAVE_LIBCEC=1)
+ # pkgconfig target found
+ elseif(TARGET PkgConfig::PC_CEC)
+ add_library(CEC::CEC ALIAS PkgConfig::PC_CEC)
+ set_property(TARGET PkgConfig::PC_CEC APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS HAVE_LIBCEC=1)
+ # building internal or no cmake config or pkgconfig
+ else()
+ add_library(CEC::CEC UNKNOWN IMPORTED)
+ set_target_properties(CEC::CEC PROPERTIES
+ IMPORTED_LOCATION "${CEC_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${CEC_INCLUDE_DIR}"
+ INTERFACE_COMPILE_DEFINITIONS HAVE_LIBCEC=1)
+ endif()
+
+ if(TARGET cec)
+ add_dependencies(CEC::CEC cec)
+ endif()
+
+ # Add internal build target when a Multi Config Generator is used
+ # We cant add a dependency based off a generator expression for targeted build types,
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/19467
+ # therefore if the find heuristics only find the library, we add the internal build
+ # target to the project to allow user to manually trigger for any build type they need
+ # in case only a specific build type is actually available (eg Release found, Debug Required)
+ # This is mainly targeted for windows who required different runtime libs for different
+ # types, and they arent compatible
+ if(_multiconfig_generator)
+ if(NOT TARGET cec)
+ buildCEC()
+ set_target_properties(cec PROPERTIES EXCLUDE_FROM_ALL TRUE)
+ endif()
+ add_dependencies(build_internal_depends cec)
+ endif()
+
set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP CEC::CEC)
+
endif()
endif()
diff --git a/cmake/modules/FindEGL.cmake b/cmake/modules/FindEGL.cmake
index a8db654e12..1a0ce39f7c 100644
--- a/cmake/modules/FindEGL.cmake
+++ b/cmake/modules/FindEGL.cmake
@@ -29,8 +29,13 @@ if(NOT TARGET EGL::EGL)
VERSION_VAR EGL_VERSION)
if(EGL_FOUND)
+ list(APPEND GL_INTERFACES_LIST egl egl-pb)
+ set(GL_INTERFACES_LIST ${GL_INTERFACES_LIST} PARENT_SCOPE)
+
+ set(CMAKE_REQUIRED_INCLUDES "${EGL_INCLUDE_DIR}")
include(CheckIncludeFiles)
check_include_files("EGL/egl.h;EGL/eglext.h;EGL/eglext_angle.h" HAVE_EGLEXTANGLE)
+ unset(CMAKE_REQUIRED_INCLUDES)
add_library(EGL::EGL UNKNOWN IMPORTED)
set_target_properties(EGL::EGL PROPERTIES
diff --git a/cmake/modules/FindFmt.cmake b/cmake/modules/FindFmt.cmake
index 0c26398219..6a3f36eab8 100644
--- a/cmake/modules/FindFmt.cmake
+++ b/cmake/modules/FindFmt.cmake
@@ -6,42 +6,34 @@
#
# fmt::fmt - The Fmt library
-macro(buildFmt)
- if(APPLE)
- set(EXTRA_ARGS "-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}")
- endif()
-
- set(FMT_VERSION ${${MODULE}_VER})
- # fmt debug uses postfix d for all platforms
- set(FMT_DEBUG_POSTFIX d)
-
- if(WIN32 OR WINDOWS_STORE)
- set(patches "${CMAKE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/001-windows-pdb-symbol-gen.patch")
- generate_patchcommand("${patches}")
- endif()
+if(NOT TARGET fmt::fmt)
- set(CMAKE_ARGS -DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
- -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
- -DFMT_DOC=OFF
- -DFMT_TEST=OFF
- -DFMT_INSTALL=ON
- "${EXTRA_ARGS}")
+ include(cmake/scripts/common/ModuleHelpers.cmake)
- BUILD_DEP_TARGET()
-endmacro()
+ # Macro for building INTERNAL_FMT
+ macro(buildFmt)
+ if(APPLE)
+ set(EXTRA_ARGS "-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}")
+ endif()
-define_property(TARGET PROPERTY LIB_BUILD
- BRIEF_DOCS "This target will be compiling the library"
- FULL_DOCS "This target will be compiling the library")
+ set(FMT_VERSION ${${MODULE}_VER})
+ # fmt debug uses postfix d for all platforms
+ set(FMT_DEBUG_POSTFIX d)
-set(FORCE_BUILD OFF)
+ if(WIN32 OR WINDOWS_STORE)
+ set(patches "${CMAKE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/001-windows-pdb-symbol-gen.patch")
+ generate_patchcommand("${patches}")
+ endif()
-# If target exists, no need to rerun find
-# Allows a module that may be a dependency for multiple libraries to just be executed
-# once to populate all required variables/targets
-if(NOT TARGET fmt::fmt OR Fmt_FIND_REQUIRED)
+ set(CMAKE_ARGS -DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
+ -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
+ -DFMT_DOC=OFF
+ -DFMT_TEST=OFF
+ -DFMT_INSTALL=ON
+ "${EXTRA_ARGS}")
- include(cmake/scripts/common/ModuleHelpers.cmake)
+ BUILD_DEP_TARGET()
+ endmacro()
set(MODULE_LC fmt)
@@ -52,39 +44,14 @@ if(NOT TARGET fmt::fmt OR Fmt_FIND_REQUIRED)
HINTS ${DEPENDS_PATH}/lib/cmake
${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
- # Build if ENABLE_INTERNAL_FMT, or if required version in find_package call is greater
- # than already found FMT_VERSION from a previous find_package call
- if((Fmt_FIND_REQUIRED AND FMT_VERSION VERSION_LESS Fmt_FIND_VERSION AND ENABLE_INTERNAL_FMT) OR
- (FMT_VERSION VERSION_LESS ${${MODULE}_VER} AND ENABLE_INTERNAL_FMT) OR
+ if((FMT_VERSION VERSION_LESS ${${MODULE}_VER} AND ENABLE_INTERNAL_FMT) OR
((CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) AND ENABLE_INTERNAL_FMT))
-
- if(Fmt_FIND_VERSION)
- if(FMT_VERSION VERSION_LESS ${Fmt_FIND_VERSION})
- set(FORCE_BUILD ON)
- endif()
- endif()
-
- if(${FORCE_BUILD} OR FMT_VERSION VERSION_LESS ${${MODULE}_VER})
-
- # Set FORCE_BUILD to enable fmt::fmt property that build will occur
- set(FORCE_BUILD ON)
-
- buildFmt()
-
- else()
- if(NOT TARGET fmt::fmt)
- set(FMT_PKGCONFIG_CHECK ON)
- endif()
- endif()
+ # build internal module
+ buildFmt()
else()
if(NOT TARGET fmt::fmt)
- set(FMT_PKGCONFIG_CHECK ON)
- endif()
- endif()
-
- if(NOT TARGET fmt::fmt)
- if(FMT_PKGCONFIG_CHECK)
- if(PKG_CONFIG_FOUND)
+ # Do not use pkgconfig on windows
+ if(PKG_CONFIG_FOUND AND NOT WIN32)
pkg_check_modules(PC_FMT libfmt QUIET)
set(FMT_VERSION ${PC_FMT_VERSION})
endif()
@@ -103,23 +70,65 @@ if(NOT TARGET fmt::fmt OR Fmt_FIND_REQUIRED)
${${CORE_PLATFORM_LC}_SEARCH_CONFIG}
NO_CACHE)
endif()
+ endif()
- add_library(fmt::fmt UNKNOWN IMPORTED)
- if(FMT_LIBRARY_RELEASE)
- set_target_properties(fmt::fmt PROPERTIES
- IMPORTED_CONFIGURATIONS RELEASE
- IMPORTED_LOCATION_RELEASE "${FMT_LIBRARY_RELEASE}")
- endif()
- if(FMT_LIBRARY_DEBUG)
+ # fmt::fmt target exists and is of suitable versioning. the INTERNAL_FMT build
+ # is not created.
+ # We create variables based off TARGET data for use with FPHSA
+ if(TARGET fmt::fmt AND NOT TARGET fmt)
+ # This is for the case where a distro provides a non standard (Debug/Release) config type
+ # eg Debian's config file is fmtConfigTargets-none.cmake
+ # convert this back to either DEBUG/RELEASE or just RELEASE
+ # we only do this because we use find_package_handle_standard_args for config time output
+ # and it isnt capable of handling TARGETS, so we have to extract the info
+ get_target_property(_FMT_CONFIGURATIONS fmt::fmt IMPORTED_CONFIGURATIONS)
+ foreach(_fmt_config IN LISTS _FMT_CONFIGURATIONS)
+ # Some non standard config (eg None on Debian)
+ # Just set to RELEASE var so select_library_configurations can continue to work its magic
+ string(TOUPPER ${_fmt_config} _fmt_config_UPPER)
+ if((NOT ${_fmt_config_UPPER} STREQUAL "RELEASE") AND
+ (NOT ${_fmt_config_UPPER} STREQUAL "DEBUG"))
+ get_target_property(FMT_LIBRARY_RELEASE fmt::fmt IMPORTED_LOCATION_${_fmt_config_UPPER})
+ else()
+ get_target_property(FMT_LIBRARY_${_fmt_config_UPPER} fmt::fmt IMPORTED_LOCATION_${_fmt_config_UPPER})
+ endif()
+ endforeach()
+
+ get_target_property(FMT_INCLUDE_DIR fmt::fmt INTERFACE_INCLUDE_DIRECTORIES)
+ endif()
+
+ include(SelectLibraryConfigurations)
+ select_library_configurations(FMT)
+ unset(FMT_LIBRARIES)
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(Fmt
+ REQUIRED_VARS FMT_LIBRARY FMT_INCLUDE_DIR
+ VERSION_VAR FMT_VERSION)
+ if(Fmt_FOUND)
+ if(TARGET fmt OR NOT TARGET fmt::fmt)
+ if(NOT TARGET fmt::fmt)
+ add_library(fmt::fmt UNKNOWN IMPORTED)
+ endif()
+ if(FMT_LIBRARY_RELEASE)
+ set_target_properties(fmt::fmt PROPERTIES
+ IMPORTED_CONFIGURATIONS RELEASE
+ IMPORTED_LOCATION_RELEASE "${FMT_LIBRARY_RELEASE}")
+ endif()
+ if(FMT_LIBRARY_DEBUG)
+ set_target_properties(fmt::fmt PROPERTIES
+ IMPORTED_CONFIGURATIONS DEBUG
+ IMPORTED_LOCATION_DEBUG "${FMT_LIBRARY_DEBUG}")
+ endif()
set_target_properties(fmt::fmt PROPERTIES
- IMPORTED_CONFIGURATIONS DEBUG
- IMPORTED_LOCATION_DEBUG "${FMT_LIBRARY_DEBUG}")
+ INTERFACE_INCLUDE_DIRECTORIES "${FMT_INCLUDE_DIR}")
endif()
- set_target_properties(fmt::fmt PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${FMT_INCLUDE_DIR}")
if(TARGET fmt)
add_dependencies(fmt::fmt fmt)
+ # We are building as a requirement, so set LIB_BUILD property to allow calling
+ # modules to know we will be building, and they will want to rebuild as well.
+ set_target_properties(fmt::fmt PROPERTIES LIB_BUILD ON)
endif()
# Add internal build target when a Multi Config Generator is used
@@ -139,40 +148,6 @@ if(NOT TARGET fmt::fmt OR Fmt_FIND_REQUIRED)
endif()
endif()
- # If a force build is done, let any calling packages know they may want to rebuild
- if(FORCE_BUILD)
- set_target_properties(fmt::fmt PROPERTIES LIB_BUILD ON)
- endif()
-
- # This is for the case where a distro provides a non standard (Debug/Release) config type
- # eg Debian's config file is fmtConfigTargets-none.cmake
- # convert this back to either DEBUG/RELEASE or just RELEASE
- # we only do this because we use find_package_handle_standard_args for config time output
- # and it isnt capable of handling TARGETS, so we have to extract the info
- get_target_property(_FMT_CONFIGURATIONS fmt::fmt IMPORTED_CONFIGURATIONS)
- foreach(_fmt_config IN LISTS _FMT_CONFIGURATIONS)
- # Some non standard config (eg None on Debian)
- # Just set to RELEASE var so select_library_configurations can continue to work its magic
- string(TOUPPER ${_fmt_config} _fmt_config_UPPER)
- if((NOT ${_fmt_config_UPPER} STREQUAL "RELEASE") AND
- (NOT ${_fmt_config_UPPER} STREQUAL "DEBUG"))
- get_target_property(FMT_LIBRARY_RELEASE fmt::fmt IMPORTED_LOCATION_${_fmt_config_UPPER})
- else()
- get_target_property(FMT_LIBRARY_${_fmt_config_UPPER} fmt::fmt IMPORTED_LOCATION_${_fmt_config_UPPER})
- endif()
- endforeach()
-
- get_target_property(FMT_INCLUDE_DIR fmt::fmt INTERFACE_INCLUDE_DIRECTORIES)
-
- include(SelectLibraryConfigurations)
- select_library_configurations(FMT)
- unset(FMT_LIBRARIES)
-
- include(FindPackageHandleStandardArgs)
- find_package_handle_standard_args(Fmt
- REQUIRED_VARS FMT_LIBRARY FMT_INCLUDE_DIR
- VERSION_VAR FMT_VERSION)
-
# Check whether we already have fmt::fmt target added to dep property list
get_property(CHECK_INTERNAL_DEPS GLOBAL PROPERTY INTERNAL_DEPS_PROP)
list(FIND CHECK_INTERNAL_DEPS "fmt::fmt" FMT_PROP_FOUND)
@@ -181,5 +156,4 @@ if(NOT TARGET fmt::fmt OR Fmt_FIND_REQUIRED)
if(FMT_PROP_FOUND STREQUAL "-1")
set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP fmt::fmt)
endif()
-
endif()
diff --git a/cmake/modules/FindGLX.cmake b/cmake/modules/FindGLX.cmake
index 066cbb8b40..56555d4c44 100644
--- a/cmake/modules/FindGLX.cmake
+++ b/cmake/modules/FindGLX.cmake
@@ -28,6 +28,9 @@ find_package_handle_standard_args(GLX
REQUIRED_VARS GLX_LIBRARY GLX_INCLUDE_DIR)
if(GLX_FOUND)
+ list(APPEND GL_INTERFACES_LIST glx)
+ set(GL_INTERFACES_LIST ${GL_INTERFACES_LIST} PARENT_SCOPE)
+
set(GLX_LIBRARIES ${GLX_LIBRARY})
set(GLX_INCLUDE_DIRS ${GLX_INCLUDE_DIR})
set(GLX_DEFINITIONS -DHAS_GLX=1)
diff --git a/cmake/modules/FindIconv.cmake b/cmake/modules/FindIconv.cmake
index 67230c897b..1e9b54fd5a 100644
--- a/cmake/modules/FindIconv.cmake
+++ b/cmake/modules/FindIconv.cmake
@@ -29,10 +29,14 @@ if(NOT TARGET ICONV::ICONV)
REQUIRED_VARS ICONV_LIBRARY ICONV_INCLUDE_DIR HAVE_ICONV_FUNCTION)
if(ICONV_FOUND)
- add_library(ICONV::ICONV UNKNOWN IMPORTED)
- set_target_properties(ICONV::ICONV PROPERTIES
- IMPORTED_LOCATION "${ICONV_LIBRARY}"
- INTERFACE_INCLUDE_DIRECTORIES "${ICONV_INCLUDE_DIR}")
- set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP ICONV::ICONV)
+ # Libc causes grief for linux, so search if found library is libc.* and only
+ # create imported TARGET if its not
+ if(NOT ${ICONV_LIBRARY} MATCHES ".*libc\..*")
+ add_library(ICONV::ICONV UNKNOWN IMPORTED)
+ set_target_properties(ICONV::ICONV PROPERTIES
+ IMPORTED_LOCATION "${ICONV_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${ICONV_INCLUDE_DIR}")
+ set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP ICONV::ICONV)
+ endif()
endif()
endif()
diff --git a/cmake/modules/FindOpenGLES.cmake b/cmake/modules/FindOpenGLES.cmake
index 49bad7027f..5dbb42e514 100644
--- a/cmake/modules/FindOpenGLES.cmake
+++ b/cmake/modules/FindOpenGLES.cmake
@@ -42,10 +42,16 @@ if(NOT TARGET OpenGL::GLES)
endif()
endif()
- add_library(OpenGL::GLES UNKNOWN IMPORTED)
+ if(${OPENGLES_gl_LIBRARY} MATCHES ".+\.so$")
+ add_library(OpenGL::GLES SHARED IMPORTED)
+ else()
+ add_library(OpenGL::GLES UNKNOWN IMPORTED)
+ endif()
+
set_target_properties(OpenGL::GLES PROPERTIES
IMPORTED_LOCATION "${OPENGLES_gl_LIBRARY}"
- INTERFACE_INCLUDE_DIRECTORIES "${OPENGLES_INCLUDE_DIR}")
+ INTERFACE_INCLUDE_DIRECTORIES "${OPENGLES_INCLUDE_DIR}"
+ IMPORTED_NO_SONAME TRUE)
if(OPENGLES3_INCLUDE_DIR)
set_property(TARGET OpenGL::GLES APPEND PROPERTY
diff --git a/cmake/modules/FindP8Platform.cmake b/cmake/modules/FindP8Platform.cmake
new file mode 100644
index 0000000000..e80fe9768c
--- /dev/null
+++ b/cmake/modules/FindP8Platform.cmake
@@ -0,0 +1,113 @@
+# FindP8Platform
+# -------
+# Finds the P8-Platform library
+#
+# This will define the following target:
+#
+# P8Platform::P8Platform - The P8-Platform library
+
+# If find_package REQUIRED, check again to make sure any potential versions
+# supplied in the call match what we can find/build
+if(NOT P8Platform::P8Platform OR P8Platform_FIND_REQUIRED)
+ include(cmake/scripts/common/ModuleHelpers.cmake)
+
+ macro(buildlibp8platform)
+ set(patches "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/001-all-fix-c++17-support.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/002-all-fixcmakeinstall.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/003-all-cmake_tweakversion.patch")
+
+ generate_patchcommand("${patches}")
+
+ set(CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF)
+
+ # CMAKE_INSTALL_LIBDIR in p8-platform prepends project prefix, so disable sending any
+ # install_libdir to generator
+ set(P8-PLATFORM_INSTALL_LIBDIR "/lib")
+
+ set(BUILD_NAME build-${MODULE_LC})
+
+ BUILD_DEP_TARGET()
+
+ set(P8-PLATFORM_VERSION ${${MODULE}_VER})
+ endmacro()
+
+ set(MODULE_LC p8-platform)
+ SETUP_BUILD_VARS()
+
+ # Search cmake-config. Suitable all platforms
+ find_package(p8-platform CONFIG
+ HINTS ${DEPENDS_PATH}/lib/cmake
+ ${${CORE_PLATFORM_LC}_SEARCH_CONFIG})
+
+ if(p8-platform_VERSION VERSION_LESS ${${MODULE}_VER})
+ # build p8-platform lib
+ buildlibp8platform()
+ else()
+ # p8-platform cmake config is terrible for modern cmake. For now just use pkgconfig
+ # and manual find_*
+ find_package(PkgConfig)
+ # Do not use pkgconfig on windows
+ if(PKG_CONFIG_FOUND AND NOT WIN32)
+ pkg_check_modules(PC_P8PLATFORM p8-platform QUIET)
+ set(P8-PLATFORM_VERSION ${PC_P8PLATFORM_VERSION})
+ else()
+ set(P8-PLATFORM_VERSION ${p8-platform_VERSION})
+ endif()
+
+ # Hack: kodi uses a tweak version. Along with p8platform cmake config/pkgconfig
+ # returns versions without patch. Just skip for now
+ set(P8Platform_FIND_VERSION "2.1")
+ find_library(P8-PLATFORM_LIBRARY NAMES p8-platform
+ HINTS ${DEPENDS_PATH}/lib ${PC_P8PLATFORM_LIBDIR}
+ ${${CORE_PLATFORM_LC}_SEARCH_CONFIG}
+ NO_CACHE)
+ find_path(P8-PLATFORM_INCLUDE_DIR NAMES p8-platform/os.h
+ HINTS ${DEPENDS_PATH}/include ${PC_P8PLATFORM_INCLUDEDIR}
+ ${${CORE_PLATFORM_LC}_SEARCH_CONFIG}
+ NO_CACHE)
+ endif()
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(P8Platform
+ REQUIRED_VARS P8-PLATFORM_LIBRARY P8-PLATFORM_INCLUDE_DIR
+ VERSION_VAR P8-PLATFORM_VERSION)
+
+ if(P8PLATFORM_FOUND)
+ add_library(P8Platform::P8Platform UNKNOWN IMPORTED)
+ set_target_properties(P8Platform::P8Platform PROPERTIES
+ IMPORTED_LOCATION "${P8-PLATFORM_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${P8-PLATFORM_INCLUDE_DIR}")
+
+ if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ set_target_properties(P8Platform::P8Platform PROPERTIES
+ INTERFACE_LINK_LIBRARIES "-framework CoreVideo")
+ endif()
+
+ if(TARGET build-p8-platform)
+ add_dependencies(P8Platform::P8Platform build-p8-platform)
+ # If the build target exists here, set LIB_BUILD property to allow calling modules
+ # know that this will be rebuilt, and they will need to rebuild as well
+ set_target_properties(P8Platform::P8Platform PROPERTIES LIB_BUILD ON)
+ endif()
+
+ # Add internal build target when a Multi Config Generator is used
+ # We cant add a dependency based off a generator expression for targeted build types,
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/19467
+ # therefore if the find heuristics only find the library, we add the internal build
+ # target to the project to allow user to manually trigger for any build type they need
+ # in case only a specific build type is actually available (eg Release found, Debug Required)
+ # This is mainly targeted for windows who required different runtime libs for different
+ # types, and they arent compatible
+ if(_multiconfig_generator)
+ if(NOT TARGET build-p8-platform)
+ buildlibp8platform()
+ set_target_properties(build-p8-platform PROPERTIES EXCLUDE_FROM_ALL TRUE)
+ endif()
+ add_dependencies(build_internal_depends build-p8-platform)
+ endif()
+ else()
+ if(P8PLATFORM_FIND_REQUIRED)
+ message(FATAL_ERROR "P8-PLATFORM not found.")
+ endif()
+ endif()
+endif()
diff --git a/cmake/modules/FindPCRE.cmake b/cmake/modules/FindPCRE.cmake
index 3ef52a2f25..811aa3fcb2 100644
--- a/cmake/modules/FindPCRE.cmake
+++ b/cmake/modules/FindPCRE.cmake
@@ -1,11 +1,10 @@
#.rst:
# FindPCRE
# --------
-# Finds the PCRECPP library
+# Finds the PCRE library
#
# This will define the following targets:
#
-# PCRE::PCRECPP - The PCRECPP library
# PCRE::PCRE - The PCRE library
macro(buildPCRE)
@@ -44,15 +43,11 @@ macro(buildPCRE)
list(APPEND CMAKE_ARGS -DHAVE_STRTOQ=0)
endif()
- # populate PCRECPP lib without a separate module
+ # populate PCRE lib without a separate module
if(NOT CORE_SYSTEM_NAME MATCHES windows)
# Non windows platforms have a lib prefix for the lib artifact
set(_libprefix "lib")
endif()
- # regex used to get platform extension (eg lib for windows, .a for unix)
- string(REGEX REPLACE "^.*\\." "" _LIBEXT ${${MODULE}_BYPRODUCT})
- set(PCRECPP_LIBRARY_DEBUG ${DEP_LOCATION}/lib/${_libprefix}pcrecpp${${MODULE}_DEBUG_POSTFIX}.${_LIBEXT})
- set(PCRECPP_LIBRARY_RELEASE ${DEP_LOCATION}/lib/${_libprefix}pcrecpp.${_LIBEXT})
BUILD_DEP_TARGET()
endmacro()
@@ -78,25 +73,17 @@ if(NOT PCRE::pcre)
else()
if(NOT TARGET PCRE::pcre)
if(PKG_CONFIG_FOUND)
- pkg_check_modules(PC_PCRE pcre pcrecpp QUIET)
+ pkg_check_modules(PC_PCRE pcre QUIET)
endif()
- find_path(PCRE_INCLUDE_DIR pcrecpp.h
+ find_path(PCRE_INCLUDE_DIR pcre.h
HINTS ${DEPENDS_PATH}/include ${PC_PCRE_INCLUDEDIR}
${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
NO_CACHE)
- find_library(PCRECPP_LIBRARY_RELEASE NAMES pcrecpp
- HINTS ${DEPENDS_PATH}/lib ${PC_PCRE_LIBDIR}
- ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
- NO_CACHE)
find_library(PCRE_LIBRARY_RELEASE NAMES pcre
HINTS ${DEPENDS_PATH}/lib ${PC_PCRE_LIBDIR}
${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
NO_CACHE)
- find_library(PCRECPP_LIBRARY_DEBUG NAMES pcrecppd
- HINTS ${DEPENDS_PATH}/lib ${PC_PCRE_LIBDIR}
- ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
- NO_CACHE)
find_library(PCRE_LIBRARY_DEBUG NAMES pcred
HINTS ${DEPENDS_PATH}/lib ${PC_PCRE_LIBDIR}
${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
@@ -116,27 +103,14 @@ if(NOT PCRE::pcre)
endif()
endforeach()
- get_target_property(_PCRECPP_CONFIGURATIONS PCRE::pcrecpp IMPORTED_CONFIGURATIONS)
- foreach(_pcrecpp_config IN LISTS _PCRECPP_CONFIGURATIONS)
- # Just set to RELEASE var so select_library_configurations can continue to work its magic
- if((NOT ${_pcrecpp_config} STREQUAL "RELEASE") AND
- (NOT ${_pcrecpp_config} STREQUAL "DEBUG"))
- get_target_property(PCRECPP_LIBRARY_RELEASE PCRE::pcrecpp IMPORTED_LOCATION_${_pcrecpp_config})
- else()
- get_target_property(PCRECPP_LIBRARY_${_pcrecpp_config} PCRE::pcrecpp IMPORTED_LOCATION_${_pcrecpp_config})
- endif()
- endforeach()
-
# ToDo: patch PCRE cmake to include includedir in config file
- find_path(PCRE_INCLUDE_DIR pcrecpp.h
+ find_path(PCRE_INCLUDE_DIR pcre.h
HINTS ${DEPENDS_PATH}/include ${PC_PCRE_INCLUDEDIR}
${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
NO_CACHE)
set_target_properties(PCRE::pcre PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${PCRE_INCLUDE_DIR}")
- set_target_properties(PCRE::pcrecpp PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${PCRE_INCLUDE_DIR}")
endif()
endif()
@@ -145,14 +119,12 @@ if(NOT PCRE::pcre)
endif()
include(SelectLibraryConfigurations)
- select_library_configurations(PCRECPP)
select_library_configurations(PCRE)
- unset(PCRECPP_LIBRARIES)
unset(PCRE_LIBRARIES)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PCRE
- REQUIRED_VARS PCRECPP_LIBRARY PCRE_LIBRARY PCRE_INCLUDE_DIR
+ REQUIRED_VARS PCRE_LIBRARY PCRE_INCLUDE_DIR
VERSION_VAR PCRE_VERSION)
if(PCRE_FOUND)
@@ -171,33 +143,15 @@ if(NOT PCRE::pcre)
set_target_properties(PCRE::pcre PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${PCRE_INCLUDE_DIR}")
endif()
- if(NOT TARGET PCRE::pcrecpp)
- add_library(PCRE::pcrecpp UNKNOWN IMPORTED)
- if(PCRECPP_LIBRARY_RELEASE)
- set_target_properties(PCRE::pcrecpp PROPERTIES
- IMPORTED_CONFIGURATIONS RELEASE
- IMPORTED_LOCATION_RELEASE "${PCRECPP_LIBRARY_RELEASE}")
- endif()
- if(PCRECPP_LIBRARY_DEBUG)
- set_target_properties(PCRE::pcrecpp PROPERTIES
- IMPORTED_CONFIGURATIONS DEBUG
- IMPORTED_LOCATION_DEBUG "${PCRECPP_LIBRARY_DEBUG}")
- endif()
- set_target_properties(PCRE::pcrecpp PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${PCRE_INCLUDE_DIR}")
- endif()
# Wee need to explicitly add this define. The cmake config does not propagate this info
if(WIN32)
set_property(TARGET PCRE::pcre APPEND PROPERTY
INTERFACE_COMPILE_DEFINITIONS "PCRE_STATIC=1")
- set_property(TARGET PCRE::pcrecpp APPEND PROPERTY
- INTERFACE_COMPILE_DEFINITIONS "PCRE_STATIC=1")
endif()
if(TARGET pcre)
add_dependencies(PCRE::pcre pcre)
- add_dependencies(PCRE::pcrecpp pcre)
endif()
# Add internal build target when a Multi Config Generator is used
@@ -217,7 +171,6 @@ if(NOT PCRE::pcre)
endif()
set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP PCRE::pcre)
- set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP PCRE::pcrecpp)
endif()
endif()
diff --git a/cmake/modules/FindPulseAudio.cmake b/cmake/modules/FindPulseAudio.cmake
index c35a405a79..8a96588f39 100644
--- a/cmake/modules/FindPulseAudio.cmake
+++ b/cmake/modules/FindPulseAudio.cmake
@@ -62,7 +62,7 @@ if(NOT TARGET PulseAudio::PulseAudio)
set_target_properties(PulseAudio::PulseAudio PROPERTIES
IMPORTED_LOCATION "${PULSEAUDIO_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${PULSEAUDIO_INCLUDE_DIR}"
- INTERFACE_COMPILE_DEFINITIONS HAVE_LIBPULSE=1
+ INTERFACE_COMPILE_DEFINITIONS HAS_PULSEAUDIO=1
INTERFACE_LINK_LIBRARIES "PulseAudio::PulseAudioMainloop;PulseAudio::PulseAudioSimple")
set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP PulseAudio::PulseAudio)
diff --git a/cmake/modules/FindPython.cmake b/cmake/modules/FindPython.cmake
index 8258f92f0b..56d320e1a9 100644
--- a/cmake/modules/FindPython.cmake
+++ b/cmake/modules/FindPython.cmake
@@ -20,7 +20,6 @@
#
# PYTHON_FOUND - system has PYTHON
# PYTHON_VERSION - Python version number (Major.Minor)
-# PYTHON_EXECUTABLE - Python interpreter binary
# PYTHON_INCLUDE_DIRS - the python include directory
# PYTHON_LIBRARIES - The python libraries
# PYTHON_LDFLAGS - Python provided link options
@@ -76,23 +75,12 @@ if(KODI_DEPENDSBUILD)
endif()
list(APPEND Python3_LIBRARIES ${LZMA_LIBRARY} ${FFI_LIBRARY} ${EXPAT_LIBRARY} ${INTL_LIBRARY} ${GMP_LIBRARY} ${PYTHON_DEP_LIBRARIES})
-else()
- if(CORE_SYSTEM_NAME STREQUAL linux)
- if(HOST_CAN_EXECUTE_TARGET)
- find_package(Python3 ${VERSION} ${EXACT_VER} COMPONENTS Interpreter)
- else()
- find_package(Python3 COMPONENTS Interpreter)
- endif()
- endif()
endif()
if(Python3_FOUND)
list(APPEND PYTHON_DEFINITIONS -DHAS_PYTHON=1)
# These are all set for easy integration with the rest of our build system
set(PYTHON_FOUND ${Python3_FOUND})
- if(NOT PYTHON_EXECUTABLE)
- set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE} CACHE FILEPATH "Python interpreter" FORCE)
- endif()
set(PYTHON_INCLUDE_DIRS ${Python3_INCLUDE_DIRS})
set(PYTHON_LIBRARIES ${Python3_LIBRARIES})
set(PYTHON_VERSION "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}" CACHE INTERNAL "" FORCE)
diff --git a/cmake/modules/FindRapidJSON.cmake b/cmake/modules/FindRapidJSON.cmake
index 19405867bd..df34eff2b2 100644
--- a/cmake/modules/FindRapidJSON.cmake
+++ b/cmake/modules/FindRapidJSON.cmake
@@ -9,18 +9,15 @@
#
if(NOT TARGET RapidJSON::RapidJSON)
- if(ENABLE_INTERNAL_RapidJSON)
- include(cmake/scripts/common/ModuleHelpers.cmake)
-
- set(MODULE_LC rapidjson)
-
- SETUP_BUILD_VARS()
+ include(cmake/scripts/common/ModuleHelpers.cmake)
+ macro(buildrapidjson)
set(RapidJSON_VERSION ${${MODULE}_VER})
set(patches "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/001-remove_custom_cxx_flags.patch"
- "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/002-cmake-removedocs-examples.patch"
- "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/003-win-arm64.patch")
+ "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch"
+ "${CORE_SOURCE_DIR}/tools/depends/target/rapidjson/004-win-arm64.patch")
generate_patchcommand("${patches}")
@@ -29,34 +26,54 @@ if(NOT TARGET RapidJSON::RapidJSON)
-DRAPIDJSON_BUILD_TESTS=OFF
-DRAPIDJSON_BUILD_THIRDPARTY_GTEST=OFF)
- set(BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/include/rapidjson/rapidjson.h)
+ set(BUILD_BYPRODUCTS ${DEPENDS_PATH}/include/rapidjson/rapidjson.h)
BUILD_DEP_TARGET()
set(RAPIDJSON_INCLUDE_DIRS ${${MODULE}_INCLUDE_DIR})
+ endmacro()
- else()
- if(PKG_CONFIG_FOUND)
- pkg_check_modules(PC_RapidJSON RapidJSON>=1.0.2 QUIET)
- endif()
+ set(MODULE_LC rapidjson)
- if(CORE_SYSTEM_NAME STREQUAL windows OR CORE_SYSTEM_NAME STREQUAL windowsstore)
- set(RapidJSON_VERSION 1.1.0)
+ SETUP_BUILD_VARS()
+
+ if(RapidJSON_FIND_VERSION)
+ if(RapidJSON_FIND_VERSION_EXACT)
+ set(RapidJSON_FIND_SPEC "=${RapidJSON_FIND_VERSION_COMPLETE}")
+ set(RapidJSON_CONFIG_SPEC "${RapidJSON_FIND_VERSION_COMPLETE}" EXACT)
else()
- if(PC_RapidJSON_VERSION)
+ set(RapidJSON_FIND_SPEC ">=${RapidJSON_FIND_VERSION_COMPLETE}")
+ set(RapidJSON_CONFIG_SPEC "${RapidJSON_FIND_VERSION_COMPLETE}")
+ endif()
+ endif()
+
+ find_package(RapidJSON CONFIG ${RapidJSON_CONFIG_SPEC}
+ HINTS ${DEPENDS_PATH}/lib/cmake
+ ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
+
+ # Check for existing RAPIDJSON. If version >= RAPIDJSON-VERSION file version, dont build
+ # A corner case, but if a linux/freebsd user WANTS to build internal tinyxml2, build anyway
+ if((RapidJSON_VERSION VERSION_LESS ${${MODULE}_VER} AND ENABLE_INTERNAL_RapidJSON) OR
+ ((CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) AND ENABLE_INTERNAL_RapidJSON))
+ # Build internal rapidjson
+ buildrapidjson()
+ else()
+ # If RAPIDJSON_INCLUDE_DIRS exists, then the find_package command found a config
+ # and suitable version. If its not, we fall back to a pkgconfig/manual search
+ if(NOT DEFINED RAPIDJSON_INCLUDE_DIRS)
+ find_package(PkgConfig)
+ # Fallback to pkg-config and individual lib/include file search
+ # Do not use pkgconfig on windows
+ if(PKG_CONFIG_FOUND AND NOT WIN32)
+ pkg_check_modules(PC_RapidJSON RapidJSON${RapidJSON_FIND_SPEC} QUIET)
set(RapidJSON_VERSION ${PC_RapidJSON_VERSION})
- else()
- find_package(RapidJSON 1.1.0 CONFIG REQUIRED
- QUIET
- HINTS ${DEPENDS_PATH}/lib
- ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})
endif()
- endif()
- find_path(RAPIDJSON_INCLUDE_DIRS NAMES rapidjson/rapidjson.h
- HINTS ${DEPENDS_PATH}/include ${PC_RapidJSON_INCLUDEDIR}
- ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
- NO_CACHE)
+ find_path(RAPIDJSON_INCLUDE_DIRS NAMES rapidjson/rapidjson.h
+ HINTS ${DEPENDS_PATH}/include ${PC_RapidJSON_INCLUDEDIR}
+ ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG}
+ NO_CACHE)
+ endif()
endif()
include(FindPackageHandleStandardArgs)
@@ -71,6 +88,23 @@ if(NOT TARGET RapidJSON::RapidJSON)
if(TARGET rapidjson)
add_dependencies(RapidJSON::RapidJSON rapidjson)
endif()
+
+ # Add internal build target when a Multi Config Generator is used
+ # We cant add a dependency based off a generator expression for targeted build types,
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/19467
+ # therefore if the find heuristics only find the library, we add the internal build
+ # target to the project to allow user to manually trigger for any build type they need
+ # in case only a specific build type is actually available (eg Release found, Debug Required)
+ # This is mainly targeted for windows who required different runtime libs for different
+ # types, and they arent compatible
+ if(_multiconfig_generator)
+ if(NOT TARGET rapidjson)
+ buildrapidjson()
+ set_target_properties(rapidjson PROPERTIES EXCLUDE_FROM_ALL TRUE)
+ endif()
+ add_dependencies(build_internal_depends rapidjson)
+ endif()
+
set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP RapidJSON::RapidJSON)
endif()
endif()
diff --git a/cmake/modules/FindSdl.cmake b/cmake/modules/FindSdl.cmake
deleted file mode 100644
index 60959cb06c..0000000000
--- a/cmake/modules/FindSdl.cmake
+++ /dev/null
@@ -1,29 +0,0 @@
-#.rst:
-# FindSDL
-# -------
-# Finds the SDL library
-#
-# This will define the following variables::
-#
-# SDL_FOUND - system has SDL
-# SDL_INCLUDE_DIRS - the SDL include directory
-# SDL_LIBRARIES - the SDL libraries
-# SDL_DEFINITIONS - the SDL compile definitions
-
-if(PKG_CONFIG_FOUND)
- pkg_check_modules(PC_SDL sdl QUIET)
-endif()
-
-find_path(SDL_INCLUDE_DIR SDL/SDL.h PATHS ${PC_SDL_INCLUDE_DIR})
-find_library(SDL_LIBRARY NAMES SDL PATHS ${PC_SDL_LIBDIR})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(Sdl REQUIRED_VARS SDL_LIBRARY SDL_INCLUDE_DIR)
-
-if(SDL_FOUND)
- set(SDL_LIBRARIES ${SDL_LIBRARY})
- set(SDL_INCLUDE_DIRS ${SDL_INCLUDE_DIR})
- set(SDL_DEFINITIONS -DHAVE_SDL=1)
-endif()
-
-mark_as_advanced(SDL_LIBRARY SDL_INCLUDE_DIR)
diff --git a/cmake/modules/FindSmctemp.cmake b/cmake/modules/FindSmctemp.cmake
new file mode 100644
index 0000000000..a96aa3375e
--- /dev/null
+++ b/cmake/modules/FindSmctemp.cmake
@@ -0,0 +1,30 @@
+#.rst:
+# FindSmctemp
+# -------
+# Finds the smctemp library
+#
+# This will define the following imported targets::
+#
+# SMCTEMP::SMCTEMP - The smctemp library
+
+if(NOT TARGET SMCTEMP::SMCTEMP)
+
+ find_path(SMCTEMP_INCLUDE_DIR NAMES smctemp.h
+ PATHS ${PC_SMCTEMP_INCLUDEDIR} NO_CACHE)
+ find_library(SMCTEMP_LIBRARY NAMES smctemp
+ PATHS ${PC_SMCTEMP_LIBDIR} NO_CACHE)
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(Smctemp
+ REQUIRED_VARS SMCTEMP_LIBRARY SMCTEMP_INCLUDE_DIR
+ VERSION_VAR SMCTEMP_VERSION)
+
+ if(SMCTEMP_FOUND)
+ add_library(SMCTEMP::SMCTEMP UNKNOWN IMPORTED)
+ set_target_properties(SMCTEMP::SMCTEMP PROPERTIES
+ IMPORTED_LOCATION "${SMCTEMP_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${SMCTEMP_INCLUDE_DIR}")
+
+ set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP SMCTEMP::SMCTEMP)
+ endif()
+endif()
diff --git a/cmake/modules/buildtools/FindPythonInterpreter.cmake b/cmake/modules/buildtools/FindPythonInterpreter.cmake
new file mode 100644
index 0000000000..1aced21952
--- /dev/null
+++ b/cmake/modules/buildtools/FindPythonInterpreter.cmake
@@ -0,0 +1,56 @@
+# FindPython
+# --------
+# Finds Python3 Interpreter
+#
+# This module will search for a Python3 Interpreter
+#
+# --------
+#
+# the following variables influence behaviour:
+#
+# PYTHON_INTERPRETER_PATH - use external python not found in system paths
+# usage: -DPYTHON_INTERPRETER_PATH=/path/to/python3
+#
+# --------
+#
+# This will define the following variable:
+#
+# PYTHON_EXECUTABLE - The HOST python executable
+#
+
+# We limit search paths to rule out TARGET paths that will populate the default cmake/package paths
+# Note: we do not do a find_package call as it will populate targets based on HOST
+# information and pollute the TARGET python searches when required for actual target platform.
+find_program(PYTHON3_INTERPRETER_EXECUTABLE NAMES python3 python
+ HINTS ${PYTHON_INTERPRETER_PATH} ${NATIVEPREFIX}/bin
+ NO_CACHE
+ NO_PACKAGE_ROOT_PATH
+ NO_CMAKE_PATH
+ NO_CMAKE_ENVIRONMENT_PATH
+ NO_CMAKE_INSTALL_PREFIX)
+
+if(PYTHON3_INTERPRETER_EXECUTABLE)
+ execute_process(COMMAND "${PYTHON3_INTERPRETER_EXECUTABLE}" --version
+ OUTPUT_VARIABLE PYTHON3_INTERPRETER_VERSION
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ string(REGEX REPLACE "^Python (.*)" "\\1" PYTHON3_INTERPRETER_VERSION "${PYTHON3_INTERPRETER_VERSION}")
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(PythonInterpreter
+ REQUIRED_VARS PYTHON3_INTERPRETER_EXECUTABLE PYTHON3_INTERPRETER_VERSION
+ VERSION_VAR PYTHON3_INTERPRETER_VERSION)
+
+ if(PythonInterpreter_FOUND)
+ # We explicitly use a CACHE variable instead of a TARGET as execute_command is not
+ # able to use a TARGET - https://gitlab.kitware.com/cmake/cmake/-/issues/18364
+ set(PYTHON_EXECUTABLE ${PYTHON3_INTERPRETER_EXECUTABLE} CACHE FILEPATH "Host Python interpreter" FORCE)
+ else()
+ if(PythonInterpreter_FIND_REQUIRED)
+ message(FATAL_ERROR "A python3 interpreter was not found. Consider providing path using -DPYTHON_INTERPRETER_PATH=<path/to/python3>")
+ endif()
+ endif()
+else()
+ if(PythonInterpreter_FIND_REQUIRED)
+ message(FATAL_ERROR "A python3 interpreter was not found. Consider providing path using -DPYTHON_INTERPRETER_PATH=<path/to/python3>")
+ endif()
+endif()
diff --git a/cmake/platform/android/android.cmake b/cmake/platform/android/android.cmake
index 07fa72c746..26c7821f89 100644
--- a/cmake/platform/android/android.cmake
+++ b/cmake/platform/android/android.cmake
@@ -1,4 +1,5 @@
set(PLATFORM_REQUIRED_DEPS LibAndroidJNI OpenGLES EGL LibZip)
+set(PLATFORM_OPTIONAL_DEPS_EXCLUDE CEC)
set(APP_RENDER_SYSTEM gles)
list(APPEND PLATFORM_OPTIONAL_DEPS LibDovi)
diff --git a/cmake/platform/darwin_embedded/ios.cmake b/cmake/platform/darwin_embedded/ios.cmake
index aff02ecfc6..860023d2f2 100644
--- a/cmake/platform/darwin_embedded/ios.cmake
+++ b/cmake/platform/darwin_embedded/ios.cmake
@@ -1,3 +1,4 @@
list(APPEND ARCH_DEFINES -DTARGET_DARWIN_IOS)
set(${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG NO_DEFAULT_PATH CACHE STRING "")
+set(PLATFORM_OPTIONAL_DEPS_EXCLUDE CEC)
diff --git a/cmake/platform/darwin_embedded/tvos.cmake b/cmake/platform/darwin_embedded/tvos.cmake
index 62e16f5c06..f30a7a5eb4 100644
--- a/cmake/platform/darwin_embedded/tvos.cmake
+++ b/cmake/platform/darwin_embedded/tvos.cmake
@@ -1,3 +1,5 @@
list(APPEND ARCH_DEFINES -DTARGET_DARWIN_TVOS)
set(ENABLE_AIRTUNES OFF CACHE BOOL "" FORCE)
set(${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG NO_DEFAULT_PATH CACHE STRING "")
+set(PLATFORM_OPTIONAL_DEPS_EXCLUDE CEC)
+
diff --git a/cmake/platform/linux/webos.cmake b/cmake/platform/linux/webos.cmake
index c740ecd277..d646a08400 100644
--- a/cmake/platform/linux/webos.cmake
+++ b/cmake/platform/linux/webos.cmake
@@ -4,8 +4,9 @@ include(${CMAKE_SOURCE_DIR}/cmake/platform/${CORE_SYSTEM_NAME}/wayland.cmake)
# saves reworking other assumptions for linux windowing as the platform name.
list(APPEND CORE_PLATFORM_NAME_LC wayland)
-list(APPEND PLATFORM_REQUIRED_DEPS WaylandProtocolsWebOS PlayerAPIs PlayerFactory WebOSHelpers)
+list(APPEND PLATFORM_REQUIRED_DEPS WaylandProtocolsWebOS PlayerAPIs PlayerFactory WebOSHelpers AcbAPI)
list(APPEND ARCH_DEFINES -DTARGET_WEBOS)
+set(PLATFORM_OPTIONAL_DEPS_EXCLUDE CEC)
set(ENABLE_PULSEAUDIO OFF CACHE BOOL "" FORCE)
set(TARGET_WEBOS TRUE)
set(PREFER_TOOLCHAIN_PATH ${TOOLCHAIN}/${HOST}/sysroot)
diff --git a/cmake/platform/osx/osx.cmake b/cmake/platform/osx/osx.cmake
index c46bfbb12e..8b581a8c1d 100644
--- a/cmake/platform/osx/osx.cmake
+++ b/cmake/platform/osx/osx.cmake
@@ -1,3 +1,5 @@
+list(APPEND CORE_MAIN_SOURCE ${CMAKE_SOURCE_DIR}/xbmc/platform/darwin/osx/XBMCApplication.mm)
+
if(NOT APP_RENDER_SYSTEM OR APP_RENDER_SYSTEM STREQUAL "gl")
list(APPEND PLATFORM_REQUIRED_DEPS OpenGl)
set(APP_RENDER_SYSTEM gl)
@@ -7,16 +9,5 @@ else()
message(SEND_ERROR "Currently only OpenGL rendering is supported. Please set APP_RENDER_SYSTEM to \"gl\"")
endif()
-if(NOT APP_WINDOW_SYSTEM OR APP_WINDOW_SYSTEM STREQUAL sdl)
- list(APPEND SYSTEM_DEFINES -DHAS_SDL)
- list(APPEND PLATFORM_REQUIRED_DEPS Sdl)
- list(APPEND CORE_MAIN_SOURCE ${CMAKE_SOURCE_DIR}/xbmc/platform/darwin/osx/SDL/SDLMain.mm
- ${CMAKE_SOURCE_DIR}/xbmc/platform/posix/main.cpp)
-elseif(APP_WINDOW_SYSTEM STREQUAL native)
- # native windowing and input
- list(APPEND CORE_MAIN_SOURCE ${CMAKE_SOURCE_DIR}/xbmc/platform/darwin/osx/XBMCApplication.mm)
-else()
- message(SEND_ERROR "Only SDL or native windowing options are supported.")
-endif()
-
set(${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG NO_DEFAULT_PATH CACHE STRING "")
+list(APPEND PLATFORM_REQUIRED_DEPS Smctemp)
diff --git a/cmake/platform/windows/windows.cmake b/cmake/platform/windows/windows.cmake
index a5e13a9c5c..b3dc4d24e4 100644
--- a/cmake/platform/windows/windows.cmake
+++ b/cmake/platform/windows/windows.cmake
@@ -1,3 +1,5 @@
set(PLATFORM_REQUIRED_DEPS D3DX11Effects Detours)
set(APP_RENDER_SYSTEM dx11)
list(APPEND PLATFORM_DEFINES -DNTDDI_VERSION=NTDDI_WINBLUE -D_WIN32_WINNT=_WIN32_WINNT_WINBLUE)
+
+set(${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG NO_DEFAULT_PATH CACHE STRING "")
diff --git a/cmake/platform/windowsstore/windowsstore.cmake b/cmake/platform/windowsstore/windowsstore.cmake
index b0c2992d99..6fa4687720 100644
--- a/cmake/platform/windowsstore/windowsstore.cmake
+++ b/cmake/platform/windowsstore/windowsstore.cmake
@@ -1,2 +1,5 @@
set(PLATFORM_REQUIRED_DEPS D3DX11Effects)
+set(PLATFORM_OPTIONAL_DEPS_EXCLUDE CEC)
set(APP_RENDER_SYSTEM dx11)
+
+set(${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG NO_DEFAULT_PATH CACHE STRING "")
diff --git a/cmake/scripts/android/ArchSetup.cmake b/cmake/scripts/android/ArchSetup.cmake
index 49ba396194..d76ce93a16 100644
--- a/cmake/scripts/android/ArchSetup.cmake
+++ b/cmake/scripts/android/ArchSetup.cmake
@@ -17,7 +17,6 @@ else()
if(CPU STREQUAL armeabi-v7a)
set(ARCH arm)
set(NEON True)
- set(NEON_FLAGS "-mfpu=neon")
elseif(CPU STREQUAL arm64-v8a)
set(ARCH aarch64)
set(NEON True)
diff --git a/cmake/scripts/common/Macros.cmake b/cmake/scripts/common/Macros.cmake
index 5e88639cb5..967010bc54 100644
--- a/cmake/scripts/common/Macros.cmake
+++ b/cmake/scripts/common/Macros.cmake
@@ -245,11 +245,23 @@ function(copy_file_to_buildtree file)
COMMAND ${CMAKE_COMMAND} -DBUNDLEDIR=${_bundle_dir}
-P ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake)
set_target_properties(export-files PROPERTIES FOLDER "Build Utilities")
+ # Add comment to ensure ExportFiles.cmake is created even if not used.
+ file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake "# Export files to build tree\n")
endif()
if(${CORE_SYSTEM_NAME} MATCHES "windows")
- file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake
- "file(COPY \"${file}\" DESTINATION \"\$\{BUNDLEDIR\}/${outdir}\")\n" )
+ # if DEPENDS_PATH in fille
+ if(${file} MATCHES ${DEPENDS_PATH})
+ file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake
+"file(GLOB filenames ${file})
+foreach(filename \$\{filenames\})
+ file(COPY \"\$\{filename\}\" DESTINATION \"\$\{BUNDLEDIR\}/${outdir}\")
+endforeach()\n"
+ )
+ else()
+ file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake
+ "file(COPY \"${file}\" DESTINATION \"\$\{BUNDLEDIR\}/${outdir}\")\n" )
+ endif()
else()
if(NOT file STREQUAL ${CMAKE_BINARY_DIR}/${outfile})
if(NOT IS_SYMLINK "${file}")
@@ -319,11 +331,16 @@ function(copy_files_from_filelist_to_buildtree pattern)
list(GET dir -1 dest)
endif()
- # If the full path to an existing file is specified then add that single file.
- # Don't recursively add all files with the given name.
- if(EXISTS ${CMAKE_SOURCE_DIR}/${src} AND (NOT IS_DIRECTORY ${CMAKE_SOURCE_DIR}/${src} OR DIR_OPTION))
+ if((${CMAKE_SOURCE_DIR}/${src} MATCHES ${DEPENDS_PATH}) OR
+ (EXISTS ${CMAKE_SOURCE_DIR}/${src} AND (NOT IS_DIRECTORY ${CMAKE_SOURCE_DIR}/${src} OR DIR_OPTION)))
+ # If the path is in DEPENDS_PATH, pass through as is. This will be handled in a build time
+ # glob of the location. This insures any dependencies built at build time can be bundled if
+ # required.
+ # OR If the full path to an existing file is specified then add that single file.
+ # Don't recursively add all files with the given name.
set(files ${src})
else()
+ # Static path contents, so we can just glob at generation time
file(GLOB_RECURSE files RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/${src})
endif()
diff --git a/cmake/scripts/common/ModuleHelpers.cmake b/cmake/scripts/common/ModuleHelpers.cmake
index d38e5afe7c..6f44ae3d35 100644
--- a/cmake/scripts/common/ModuleHelpers.cmake
+++ b/cmake/scripts/common/ModuleHelpers.cmake
@@ -223,7 +223,6 @@ macro(BUILD_DEP_TARGET)
if(CMAKE_ARGS)
set(CMAKE_ARGS CMAKE_ARGS ${CMAKE_ARGS}
- -DCMAKE_INSTALL_LIBDIR=lib
-DPROJECTSOURCE=${PROJECTSOURCE}
"-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}")
@@ -239,6 +238,12 @@ macro(BUILD_DEP_TARGET)
endif()
endif()
+ if(DEFINED ${MODULE}_INSTALL_LIBDIR)
+ list(APPEND CMAKE_ARGS -DCMAKE_INSTALL_LIBDIR=${${MODULE}_INSTALL_LIBDIR})
+ else()
+ list(APPEND CMAKE_ARGS -DCMAKE_INSTALL_LIBDIR=lib)
+ endif()
+
if(${MODULE}_INSTALL_PREFIX)
list(APPEND CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${${MODULE}_INSTALL_PREFIX})
else()
@@ -326,6 +331,14 @@ macro(BUILD_DEP_TARGET)
set(BUILD_IN_SOURCE BUILD_IN_SOURCE ${BUILD_IN_SOURCE})
endif()
+ # Change extension of BYPRODUCT
+ # eg, Macos uses dylib for shared libs, but all other unix platforms use .so
+ if(${MODULE}_BYPRODUCT_EXTENSION)
+ string(REGEX REPLACE "\\.[^.]*$" "" _LIBNAME ${${MODULE}_BYPRODUCT})
+ set(${MODULE}_BYPRODUCT "${_LIBNAME}.${${MODULE}_BYPRODUCT_EXTENSION}")
+ unset(_LIBNAME)
+ endif()
+
# Set Library names.
if(DEFINED ${MODULE}_DEBUG_POSTFIX)
set(_POSTFIX ${${MODULE}_DEBUG_POSTFIX})
@@ -425,3 +438,10 @@ macro(PATCH_LF_CHECK patch)
endif()
unset(patch_content_hex)
endmacro()
+
+# Custom property that we can track to allow us to notify to dependency find modules
+# that a dependency of that find module is being built, and therefore that higher level
+# dependency should also be built regardless of success in lib searches
+define_property(TARGET PROPERTY LIB_BUILD
+ BRIEF_DOCS "This target will be compiling the library"
+ FULL_DOCS "This target will be compiling the library")
diff --git a/cmake/scripts/darwin_embedded/ArchSetup.cmake b/cmake/scripts/darwin_embedded/ArchSetup.cmake
index b3258bebfd..2079c221cb 100644
--- a/cmake/scripts/darwin_embedded/ArchSetup.cmake
+++ b/cmake/scripts/darwin_embedded/ArchSetup.cmake
@@ -26,7 +26,8 @@ endif()
if(NOT APP_RENDER_SYSTEM OR APP_RENDER_SYSTEM STREQUAL "gles")
set(PLATFORM_REQUIRED_DEPS OpenGLES)
set(APP_RENDER_SYSTEM gles)
- list(APPEND SYSTEM_DEFINES -DGL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED)
+ list(APPEND SYSTEM_DEFINES -DGL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
+ -DGLES_SILENCE_DEPRECATION)
else()
message(SEND_ERROR "Currently only OpenGLES rendering is supported. Please set APP_RENDER_SYSTEM to \"gles\"")
endif()
diff --git a/cmake/scripts/darwin_embedded/Install.cmake b/cmake/scripts/darwin_embedded/Install.cmake
index fb05a1528a..724fad1259 100644
--- a/cmake/scripts/darwin_embedded/Install.cmake
+++ b/cmake/scripts/darwin_embedded/Install.cmake
@@ -20,7 +20,7 @@ if(CORE_PLATFORM_NAME_LC STREQUAL tvos)
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${ENTITLEMENTS_OUT_PATH})
else()
- set(BUNDLE_RESOURCES ${CMAKE_SOURCE_DIR}/media/splash.jpg
+ set(BUNDLE_RESOURCES ${CMAKE_SOURCE_DIR}/media/applaunch_screen.png
${CMAKE_SOURCE_DIR}/tools/darwin/packaging/media/ios/rounded/AppIcon29x29.png
${CMAKE_SOURCE_DIR}/tools/darwin/packaging/media/ios/rounded/AppIcon29x29@2x.png
${CMAKE_SOURCE_DIR}/tools/darwin/packaging/media/ios/rounded/AppIcon40x40.png
diff --git a/cmake/scripts/freebsd/ArchSetup.cmake b/cmake/scripts/freebsd/ArchSetup.cmake
index 87f4f0c9cc..9e128df162 100644
--- a/cmake/scripts/freebsd/ArchSetup.cmake
+++ b/cmake/scripts/freebsd/ArchSetup.cmake
@@ -44,3 +44,5 @@ if(NOT USE_INTERNAL_LIBS)
set(USE_INTERNAL_LIBS OFF)
endif()
endif()
+
+list(APPEND AUDIO_BACKENDS_LIST "oss")
diff --git a/cmake/scripts/linux/Install.cmake b/cmake/scripts/linux/Install.cmake
index 8f0941cdc9..fd73bea9ed 100644
--- a/cmake/scripts/linux/Install.cmake
+++ b/cmake/scripts/linux/Install.cmake
@@ -3,16 +3,6 @@ if(X_FOUND)
else()
set(USE_X11 0)
endif()
-if(TARGET OpenGL::GLES)
- set(USE_OPENGL 1)
-else()
- set(USE_OPENGL 0)
-endif()
-if(TARGET OpenGL::GLES)
- set(USE_OPENGLES 1)
-else()
- set(USE_OPENGLES 0)
-endif()
# CMake config
set(APP_BINARY ${APP_NAME_LC}${APP_BINARY_SUFFIX})
@@ -204,6 +194,7 @@ install(FILES ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/scripts/${APP_NAME}Config.cm
COMPONENT kodi-addon-dev)
if(ENABLE_EVENTCLIENTS)
+ find_package(PythonInterpreter REQUIRED)
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(prefix=''))"
OUTPUT_VARIABLE PYTHON_LIB_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
# Install kodi-eventclients-common BT python files
diff --git a/cmake/scripts/osx/Install.cmake b/cmake/scripts/osx/Install.cmake
index 5807428384..9c62a7a996 100644
--- a/cmake/scripts/osx/Install.cmake
+++ b/cmake/scripts/osx/Install.cmake
@@ -63,9 +63,8 @@ add_custom_target(dmg
${CMAKE_BINARY_DIR}/tools/darwin/packaging/osx/Codesign.command
COMMAND "CODESIGNING_FOLDER_PATH=$<TARGET_BUNDLE_DIR:${APP_NAME_LC}>"
"APP=$<TARGET_BUNDLE_DIR:${APP_NAME_LC}>"
- "DEV_ACCOUNT=${DEV_ACCOUNT}"
- "DEV_ACCOUNT_PASSWORD=${DEV_ACCOUNT_PASSWORD}"
- "DEV_TEAM=${DEV_TEAM}"
+ "NOTARYTOOL_KEYCHAIN_PROFILE=${NOTARYTOOL_KEYCHAIN_PROFILE}"
+ "NOTARYTOOL_KEYCHAIN_PATH=${NOTARYTOOL_KEYCHAIN_PATH}"
"EXPANDED_CODE_SIGN_IDENTITY_NAME=${CODE_SIGN_IDENTITY}"
"PLATFORM_NAME=${PLATFORM}"
"XCODE_BUILDTYPE=${CMAKE_CFG_INTDIR}"
diff --git a/cmake/scripts/webos/Install.cmake b/cmake/scripts/webos/Install.cmake
index ab894aa9df..03dd7ce183 100644
--- a/cmake/scripts/webos/Install.cmake
+++ b/cmake/scripts/webos/Install.cmake
@@ -29,7 +29,8 @@ set(APP_INSTALL_DIRS ${CMAKE_BINARY_DIR}/addons
${CMAKE_BINARY_DIR}/system
${CMAKE_BINARY_DIR}/userdata)
set(APP_TOOLCHAIN_FILES ${TOOLCHAIN}/${HOST}/sysroot/lib/libatomic.so.1
- ${TOOLCHAIN}/${HOST}/sysroot/lib/libcrypt.so.1)
+ ${TOOLCHAIN}/${HOST}/sysroot/lib/libcrypt.so.1
+ ${CMAKE_BINARY_DIR}/libAcbAPI.so.1)
set(BIN_ADDONS_DIR ${DEPENDS_PATH}/addons)
file(WRITE ${CMAKE_BINARY_DIR}/install.cmake "
@@ -53,7 +54,7 @@ file(WRITE ${CMAKE_BINARY_DIR}/install.cmake "
# Copy files to the location expected by the webOS packaging scripts.
add_custom_target(bundle
- DEPENDS ${APP_NAME_LC} ${CMAKE_BINARY_DIR}/missing_libs.txt
+ DEPENDS ${APP_NAME_LC} ${CMAKE_BINARY_DIR}/missing_libs.txt AcbAPI
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/install.cmake
)
diff --git a/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt
index 3d6e6ea0b4..67b44f92d1 100644
--- a/cmake/treedata/common/subdirs.txt
+++ b/cmake/treedata/common/subdirs.txt
@@ -41,6 +41,7 @@ xbmc/speech speech
xbmc/storage storage
xbmc/threads threads
xbmc/utils utils
+xbmc/utils/guilib utils_guilib
xbmc/view view
xbmc/weather weather
xbmc/windowing windowing
diff --git a/cmake/treedata/osx/subdirs.txt b/cmake/treedata/osx/subdirs.txt
index b56f268cae..f524d22dd3 100644
--- a/cmake/treedata/osx/subdirs.txt
+++ b/cmake/treedata/osx/subdirs.txt
@@ -7,7 +7,6 @@ xbmc/platform/darwin/osx platform/osx
xbmc/platform/darwin/osx/network platform/darwin/osx/network
xbmc/platform/darwin/osx/peripherals platform/osx/peripherals
xbmc/platform/darwin/osx/powermanagement platform/darwin/osx/powermanagement
-xbmc/platform/darwin/osx/SDL platform/osx/SDL
xbmc/platform/darwin/osx/storage platform/osx/storage
xbmc/platform/darwin/peripherals platform/darwin/peripherals
xbmc/platform/darwin/utils platform/darwin/utils
@@ -19,4 +18,3 @@ xbmc/platform/posix/threads platform/posix/threads
xbmc/platform/posix/utils platform/posix/utils
xbmc/windowing/osx windowing/osx
xbmc/windowing/osx/OpenGL windowing/osx/OpenGL
-xbmc/windowing/osx/SDL windowing/osx/SDL
diff --git a/docs/CODE_GUIDELINES.md b/docs/CODE_GUIDELINES.md
index c7cf298042..25d64574e4 100644
--- a/docs/CODE_GUIDELINES.md
+++ b/docs/CODE_GUIDELINES.md
@@ -69,6 +69,8 @@ In the repository root directory, there is a [`.clang-format`](https://github.co
When you create a pull request, the PR build job will run `clang-format` on your commits and provide patches for any parts that don't satisfy the current `.clang-format` rules. You should apply these patches and amend your pull request accordingly.
+The coding guidelines should be met by every code change, be it editing existing code, adding new code to existing source files, or adding completely new source files. For changes in existing files, at least the changed code needs to pass the clang-format check.
+
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.
**[back to top](#table-of-contents)**
@@ -88,7 +90,7 @@ The `ColumnLimit` in `.clang-format` is set to `100` which defines line length (
Curly braces always go on a new line.
```cpp
-for (int i = 0; i < t; i++)
+for (int i = 0; i < t; ++i)
{
[...]
}
@@ -201,7 +203,7 @@ a = (b + c) * d;
Control statement keywords have to be separated from opening parentheses by one space.
```cpp
while (true);
-for (int i = 0; i < x; i++);
+for (int i = 0; i < x; ++i);
```
When conditions are used without parentheses, it is preferable to add a new line, to make the next block of code more readable.
```cpp
@@ -819,6 +821,25 @@ for (const auto& : var)
```
Remove `const` if the value has to be modified. Do not use references to fundamental types that are not modified.
+In traditional for loops, for the `increment statement` of the loop, use prefix increment/decrement operator, not postfix.
+
+✅ Good:
+```cpp
+[...]
+for (int i = 0; i < 100; ++i)
+{
+ [...]
+}
+```
+
+❌ Bad:
+```cpp
+for (int i = 0; i < 100; i++)
+{
+ [...]
+}
+```
+
### 12.6. Include guards
Use `#pragma once`.
diff --git a/docs/README.Android.md b/docs/README.Android.md
index 5b3de45c88..f796f163cf 100644
--- a/docs/README.Android.md
+++ b/docs/README.Android.md
@@ -9,7 +9,7 @@ It should work if you're using macOS. If that is the case, read **[macOS specifi
1. **[Document conventions](#1-document-conventions)**
2. **[Install the required packages](#2-install-the-required-packages)**
3. **[Prerequisites](#3-prerequisites)**
- 3.1. **[Extract Android SDK and NDK](#31-extract-android-sdk-and-ndk)**
+ 3.1. **[Extract Android SDK](#31-extract-android-sdk)**
3.2. **[Configure Android SDK](#32-configure-android-sdk)**
3.3. **[Create a key to sign debug APKs](#33-create-a-key-to-sign-debug-apks)**
4. **[Get the source code](#4-get-the-source-code)**
@@ -70,9 +70,8 @@ Building Kodi for Android requires Android NDK revision 20b. For the SDK just us
Kodi CI/CD platforms currently use r21e for build testing and releases, so we recommend using r21e for the most tested build experience
* **[Android SDK](https://developer.android.com/studio/index.html)** (Look for `Get just the command line tools`)
-* **[Android NDK](https://developer.android.com/ndk/downloads/index.html)**
-### 3.1. Extract Android SDK and NDK
+### 3.1. Extract Android SDK
Create needed directories:
```
mkdir -p $HOME/android-tools/android-sdk-linux
@@ -85,11 +84,6 @@ unzip $HOME/Downloads/commandlinetools-linux-6200805_latest.zip -d $HOME/android
**NOTE:** Since we're using the latest SDK Command line tools available, filename can change over time. Adapt the `unzip` command accordingly.
-Extract Android NDK:
-```
-unzip $HOME/Downloads/android-ndk-r21e-linux-x86_64.zip -d $HOME/android-tools
-```
-
### 3.2. Configure Android SDK
Before Android SDK can be used, you need to accept the licenses and configure it:
```
@@ -97,7 +91,8 @@ cd $HOME/android-tools/android-sdk-linux/cmdline-tools/bin
./sdkmanager --sdk_root=$(pwd)/../.. --licenses
./sdkmanager --sdk_root=$(pwd)/../.. platform-tools
./sdkmanager --sdk_root=$(pwd)/../.. "platforms;android-33"
-./sdkmanager --sdk_root=$(pwd)/../.. "build-tools;30.0.3"
+./sdkmanager --sdk_root=$(pwd)/../.. "build-tools;33.0.1"
+./sdkmanager --sdk_root=$(pwd)/../.. "ndk;21.4.7075529"
```
### 3.3. Create a key to sign debug APKs
@@ -131,22 +126,22 @@ cd $HOME/kodi/tools/depends
Configure build for aarch64:
```
-./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=aarch64-linux-android --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-ndk-r21e --prefix=$HOME/android-tools/xbmc-depends
+./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=aarch64-linux-android --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-sdk-linux/ndk/21.4.7075529 --prefix=$HOME/android-tools/xbmc-depends
```
Or configure build for arm:
```
-./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=arm-linux-androideabi --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-ndk-r21e --prefix=$HOME/android-tools/xbmc-depends
+./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=arm-linux-androideabi --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-sdk-linux/ndk/21.4.7075529 --prefix=$HOME/android-tools/xbmc-depends
```
Or configure build for x86:
```
-./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=i686-linux-android --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-ndk-r21e --prefix=$HOME/android-tools/xbmc-depends
+./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=i686-linux-android --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-sdk-linux/ndk/21.4.7075529 --prefix=$HOME/android-tools/xbmc-depends
```
Or configure build for x86_64:
```
-./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=x86_64-linux-android --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-ndk-r21e --prefix=$HOME/android-tools/xbmc-depends
+./configure --with-tarballs=$HOME/android-tools/xbmc-tarballs --host=x86_64-linux-android --with-sdk-path=$HOME/android-tools/android-sdk-linux --with-ndk-path=$HOME/android-tools/android-sdk-linux/ndk/21.4.7075529 --prefix=$HOME/android-tools/xbmc-depends
```
> **Note:** Android x86 and x86_64 are not maintained and are not 100% sure that everything works correctly!
diff --git a/docs/README.Linux.md b/docs/README.Linux.md
index af7515a19f..584df2962c 100644
--- a/docs/README.Linux.md
+++ b/docs/README.Linux.md
@@ -124,6 +124,10 @@ Internal dependencies that are based on cmake upstream (currently crossguid, ffm
**Note:** fstrcmp requires libtool
+### 3.3. External Dependencies
+
+Building with GBM windowing (including `gbm` in the `CORE_PLATFORM_NAME` cmake config) requires `libdisplay-info` for EDID parsing. This is currently a hard dependency for GBM windowing and no internal build option is available. Some distributions may already package it but if not it is available to build from source here: https://gitlab.freedesktop.org/emersion/libdisplay-info
+
**[back to top](#table-of-contents)** | **[back to section top](#3-install-the-required-packages)**
## 4. Build Kodi
diff --git a/docs/README.macOS.md b/docs/README.macOS.md
index 9dc2522ca8..fc6db1844d 100644
--- a/docs/README.macOS.md
+++ b/docs/README.macOS.md
@@ -114,11 +114,6 @@ make -j$(getconf _NPROCESSORS_ONLN)
./configure --host=x86_64-apple-darwin --with-platform=macos --with-sdk=10.14
```
-Developers can also select the legacy SDL windowing/input handling with the following
-```
-./configure --host=x86_64-apple-darwin --with-platform=macos --with-windowsystem=sdl
-```
-
### 4.1. Advanced Configure Options
@@ -187,11 +182,6 @@ Developers can also select the legacy SDL windowing/input handling with the foll
**Apple Specific:**
```
---with-windowsystem=<native:sdl>
-```
- Windowing system to use (default is native windowing when not provided). arm64 MacOS does not support SDL windowing.
-
-```
--with-sdk=<sdknumber>
```
specify sdk platform version.
@@ -241,11 +231,6 @@ Generate Xcode project as per configure command in **[Configure and build tools
make -C tools/depends/target/cmakebuildsys BUILD_DIR=$HOME/kodi-build GEN=Xcode
```
-To explicitly select the windowing/input system to use do the following (default is to use native if not provided)
-```
-make -C tools/depends/target/cmakebuildsys BUILD_DIR=$HOME/kodi-build GEN=Xcode APP_WINDOW_SYSTEM=sdl
-```
-
**TIP:** BUILD_DIR can be omitted, and project will be created in $HOME/kodi/build
Change all relevant paths onwards if omitted.
diff --git a/media/applaunch_screen.png b/media/applaunch_screen.png
new file mode 100644
index 0000000000..91717c358b
--- /dev/null
+++ b/media/applaunch_screen.png
Binary files differ
diff --git a/media/splash.jpg b/media/splash.jpg
index da548b33d9..c0a2e4be86 100644
--- a/media/splash.jpg
+++ b/media/splash.jpg
Binary files differ
diff --git a/media/splash_webOS.png b/media/splash_webOS.png
deleted file mode 100644
index 55d0274d8b..0000000000
--- a/media/splash_webOS.png
+++ /dev/null
Binary files differ
diff --git a/project/BuildDependencies/scripts/0_package.target-win32.list b/project/BuildDependencies/scripts/0_package.target-win32.list
index 53b11774d8..4d531330bb 100644
--- a/project/BuildDependencies/scripts/0_package.target-win32.list
+++ b/project/BuildDependencies/scripts/0_package.target-win32.list
@@ -21,7 +21,6 @@ libass-0.17.1-win32-v142-20230619.7z
libbdplus-0.1.2-win32-v141-20200105.7z
libbluray-1.3.4-win32-v142-20230728.7z
libcdio-2.1.0-win32-v141-20200112.7z
-libcec-4.0.4-win32-v141-20200105.7z
libfribidi-1.0.8-win32-v141-20200105.7z
libiconv-1.16-win10-win32-v141-20200105.7z
libjpeg-turbo-2.0.3-win32-v141-20200105.7z
diff --git a/project/BuildDependencies/scripts/0_package.target-x64.list b/project/BuildDependencies/scripts/0_package.target-x64.list
index 28675528de..eacfd4629d 100644
--- a/project/BuildDependencies/scripts/0_package.target-x64.list
+++ b/project/BuildDependencies/scripts/0_package.target-x64.list
@@ -20,7 +20,6 @@ libass-0.17.1-x64-v142-20230619.7z
libbdplus-0.1.2-x64-v141-20200105.7z
libbluray-1.3.4-x64-v142-20230728.7z
libcdio-2.1.0-x64-v141-20200112.7z
-libcec-4.0.4-x64-v141-20200105.7z
libfribidi-1.0.8-x64-v141-20200105.7z
libiconv-1.16-x64-v141-20200105.7z
libmicrohttpd-0.9.69-x64-v141-20200105.7z
diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml
index 1a192160c9..ac0033d7f8 100644
--- a/system/keymaps/keyboard.xml
+++ b/system/keymaps/keyboard.xml
@@ -672,15 +672,6 @@
<blue>Blue</blue>
</keyboard>
</Teletext>
- <Favourites>
- <keyboard>
- <backspace>Close</backspace>
- <browser_back>Close</browser_back>
- <u>MoveItemUp</u>
- <d>MoveItemDown</d>
- <backspace mod="longpress">ActivateWindow(Home)</backspace>
- </keyboard>
- </Favourites>
<FavouritesBrowser>
<keyboard>
<u>MoveItemUp</u>
diff --git a/system/keymaps/remote.xml b/system/keymaps/remote.xml
index c122b99188..10f802d035 100644
--- a/system/keymaps/remote.xml
+++ b/system/keymaps/remote.xml
@@ -50,7 +50,7 @@
<volumeplus>VolumeUp</volumeplus>
<volumeminus>VolumeDown</volumeminus>
<mute>Mute</mute>
- <power>ShutDown()</power>
+ <power>ActivateWindow(ShutdownMenu)</power>
<myvideo>ActivateWindow(Videos)</myvideo>
<mymusic>ActivateWindow(Music)</mymusic>
<mypictures>ActivateWindow(Pictures)</mypictures>
@@ -564,11 +564,6 @@
<teletext>Back</teletext>
</remote>
</Teletext>
- <Favourites>
- <remote>
- <back>Close</back>
- </remote>
- </Favourites>
<FullscreenLiveTV>
<remote>
<left>StepBack</left>
diff --git a/system/library/video/movies/versions.xml b/system/library/video/movies/versions.xml
new file mode 100644
index 0000000000..57ca2b4024
--- /dev/null
+++ b/system/library/video/movies/versions.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<node order="120" type="filter">
+ <label>40000</label>
+ <icon>DefaultVideoVersions.png</icon>
+ <content>movies</content>
+ <group>videoversions</group>
+</node>
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index d9218c470d..6330fafad4 100755
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -60,6 +60,16 @@
</constraints>
<control type="list" format="string" />
</setting>
+ <setting id="winsystem.ishdrdisplay" type="boolean" label="13436" help="36299">
+ <dependencies>
+ <dependency type="visible">
+ <condition on="property" name="ishdrdisplay" />
+ </dependency>
+ </dependencies>
+ <level>2</level>
+ <default>true</default>
+ <control type="toggle" />
+ </setting>
<setting id="videoplayer.usedisplayasclock" type="boolean" label="13510" help="36166">
<level>1</level>
<default>false</default>
@@ -163,16 +173,6 @@
<default>true</default>
<control type="toggle" />
</setting>
- <setting id="winsystem.ishdrdisplay" type="boolean" label="13436" help="36299">
- <dependencies>
- <dependency type="visible">
- <condition on="property" name="ishdrdisplay" />
- </dependency>
- </dependencies>
- <level>2</level>
- <default>true</default>
- <control type="toggle" />
- </setting>
<setting id="videoplayer.highprecision" type="boolean" label="13418" help="39195">
<requirement>HAS_DX</requirement>
<level>2</level>
@@ -877,6 +877,16 @@
<default>false</default>
<control type="toggle" />
</setting>
+ <setting id="videolibrary.ignorevideoversions" type="boolean" label="40202" help="40203">
+ <level>1</level>
+ <default>false</default>
+ <control type="toggle" />
+ </setting>
+ <setting id="videolibrary.ignorevideoextras" type="boolean" label="40204" help="40205">
+ <level>1</level>
+ <default>false</default>
+ <control type="toggle" />
+ </setting>
<setting id="videolibrary.cleanup" type="action" label="14247" help="36148">
<level>2</level>
<control type="button" format="action" />
@@ -1015,6 +1025,22 @@
</constraints>
<control type="list" format="string" />
</setting>
+ <setting id="myvideos.selectdefaultversion" type="boolean" label="40200" help="40201">
+ <level>0</level>
+ <default>false</default>
+ <control type="toggle" />
+ </setting>
+ <setting id="myvideos.playaction" type="integer" label="22076" help="36204">
+ <level>0</level>
+ <default>1</default> <!-- PLAY_ACTION_PLAY_OR_RESUME -->
+ <constraints>
+ <options>
+ <option label="22077">1</option> <!-- PLAY_ACTION_PLAY_OR_RESUME -->
+ <option label="22078">2</option> <!-- PLAY_ACTION_RESUME -->
+ </options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
<setting id="myvideos.usetags" type="boolean" label="21343" help="21344">
<level>2</level>
<default>false</default>
@@ -1075,6 +1101,11 @@
<default>false</default>
<control type="toggle" />
</setting>
+ <setting id="videolibrary.showvideoversionsasfolder" type="boolean" label="40206" help="40207">
+ <level>1</level>
+ <default>false</default>
+ <control type="toggle" />
+ </setting>
<setting id="myvideos.flatten" type="boolean" label="20456" help="36183">
<level>2</level>
<default>false</default>
@@ -2406,6 +2437,16 @@
<control type="toggle" />
</setting>
</group>
+ <group id="4" label="37053">
+ <setting id="smb.chunksize" type="integer" label="37056" help="37057">
+ <level>2</level>
+ <default>128</default>
+ <constraints>
+ <options>filechunksizes</options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
+ </group>
</category>
<category id="nfs" label="1201" help="36356">
<requirement>HAS_FILESYSTEM_NFS</requirement>
@@ -2421,6 +2462,69 @@
<control type="spinner" format="integer" />
</setting>
</group>
+ <group id="2" label="37053">
+ <setting id="nfs.chunksize" type="integer" label="37054" help="37055">
+ <level>2</level>
+ <default>128</default>
+ <constraints>
+ <options>filechunksizes</options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
+ </group>
+ </category>
+ <category id="filecache" label="37101" help="37102">
+ <group id="1" label="16000">
+ <setting id="filecache.buffermode" type="integer" label="37103" help="37104">
+ <level>2</level>
+ <default>4</default>
+ <constraints>
+ <options>filecachebuffermodes</options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
+ <setting id="filecache.memorysize" type="integer" label="37105" help="37106">
+ <level>2</level>
+ <default>20</default>
+ <dependencies>
+ <dependency type="enable">
+ <condition setting="filecache.buffermode" operator="!is">3</condition>
+ </dependency>
+ </dependencies>
+ <constraints>
+ <options>filecachememorysizes</options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
+ <setting id="filecache.readfactor" type="integer" label="37107" help="37108">
+ <level>2</level>
+ <default>400</default>
+ <dependencies>
+ <dependency type="enable">
+ <condition setting="filecache.buffermode" operator="!is">3</condition>
+ </dependency>
+ </dependencies>
+ <constraints>
+ <options>filecachereadfactors</options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
+ </group>
+ <group id="2" label="37053">
+ <setting id="filecache.chunksize" type="integer" label="37053" help="37109">
+ <level>2</level>
+ <default>131072</default>
+ <dependencies>
+ <dependency type="enable">
+ <condition setting="filecache.buffermode" operator="!is">3</condition>
+ </dependency>
+ </dependencies>
+ <constraints>
+ <options>filecachechunksizes</options>
+ </constraints>
+ <control type="list" format="string" />
+ </setting>
+ </group>
</category>
<category id="weather" label="8" help="36316">
<group id="1" label="16000">
diff --git a/tools/Linux/kodi.metainfo.xml.in b/tools/Linux/kodi.metainfo.xml.in
index 4c78665728..31cba093b0 100644
--- a/tools/Linux/kodi.metainfo.xml.in
+++ b/tools/Linux/kodi.metainfo.xml.in
@@ -5,7 +5,7 @@
<project_license>GPL-2.0-only GPL-2.0-or-later LGPL-2.1-or-later MIT BSD-3-Clause BSD-4-Clause</project_license>
<metadata_license>CC0-1.0</metadata_license>
<developer_name>@DEV_NAME@</developer_name>
- <summary>The ultimate entertainment center</summary>
+ <summary>Ultimate entertainment center</summary>
<url type="homepage">https://kodi.tv/</url>
<url type="donation">https://kodi.tv/contribute/donate</url>
<url type="bugtracker">https://github.com/xbmc/xbmc/issues</url>
@@ -78,6 +78,7 @@
</screenshot>
</screenshots>
<releases>
+ <release date="2023-10-17" version="21.0~b1-Omega" type="development"><url>https://kodi.tv/article/kodi-omega-beta-1/</url></release>
<release date="2023-09-07" version="21.0~a3-Omega" type="development"><url>https://kodi.tv/article/kodi-omega-alpha-3/</url></release>
<release date="2023-06-17" version="21.0~a2-Omega" type="development"><url>https://kodi.tv/article/kodi-omega-alpha-2/</url></release>
<release date="2023-04-16" version="21.0~a1-Omega" type="development"><url>https://kodi.tv/article/kodi-omega-alpha-1/</url></release>
diff --git a/tools/Linux/kodi.sh.in b/tools/Linux/kodi.sh.in
index 5dc1dd5260..52d4646db5 100644
--- a/tools/Linux/kodi.sh.in
+++ b/tools/Linux/kodi.sh.in
@@ -182,6 +182,8 @@ if [ -n "${KODI_AE_SINK}" ]; then
ENV_ARGS="--audio-backend=pulseaudio"
elif [ "${KODI_AE_SINK}" = "ALSA" ]; then
ENV_ARGS="--audio-backend=alsa"
+ elif [ "${KODI_AE_SINK}" = "OSS" ]; then
+ ENV_ARGS="--audio-backend=oss"
elif [ "${KODI_AE_SINK}" = "SNDIO" ]; then
ENV_ARGS="--audio-backend=sndio"
elif [ "${KODI_AE_SINK}" = "ALSA+PULSE" ]; then
@@ -189,6 +191,20 @@ if [ -n "${KODI_AE_SINK}" ]; then
fi
fi
+if [ -n "${KODI_GL_INTERFACE}" ]; then
+
+ echo "KODI_GL_INTERFACE env variable is deprecated and will be removed in the future."
+ echo "Use the --gl-interface command line switch instead."
+
+ if [ "${KODI_GL_INTERFACE}" = "GLX" ]; then
+ ENV_ARGS="--gl-interface=glx"
+ elif [ "${KODI_GL_INTERFACE}" = "EGL" ]; then
+ ENV_ARGS="--gl-interface=egl"
+ elif [ "${KODI_GL_INTERFACE}" = "EGL_PB" ]; then
+ ENV_ARGS="--gl-interface=egl-pb"
+ fi
+fi
+
LOOP=1
while [ $(( $LOOP )) = "1" ]
do
diff --git a/tools/android/packaging/Makefile.in b/tools/android/packaging/Makefile.in
index 514b655efb..78728dd4f4 100644
--- a/tools/android/packaging/Makefile.in
+++ b/tools/android/packaging/Makefile.in
@@ -62,13 +62,13 @@ python: | xbmc/assets
res:
mkdir -p xbmc/res xbmc/res/raw xbmc/res/values images
@echo "native_arch=$(CPU)" > xbmc/res/raw/xbmc.properties
- cp -fp $(CMAKE_SOURCE_DIR)/media/splash.* xbmc/res/drawable/
+ cp -fp $(CMAKE_SOURCE_DIR)/media/applaunch_screen.png xbmc/res/drawable/
cp -fp media/drawable-hdpi/ic_launcher.png xbmc/res/drawable-hdpi/ic_launcher.png
cp -fp media/drawable-ldpi/ic_launcher.png xbmc/res/drawable-ldpi/ic_launcher.png
cp -fp media/drawable-mdpi/ic_launcher.png xbmc/res/drawable-mdpi/ic_launcher.png
cp -fp media/drawable-xhdpi/ic_launcher.png xbmc/res/drawable-xhdpi/ic_launcher.png
cp -fp media/drawable-xxhdpi/ic_launcher.png xbmc/res/drawable-xxhdpi/ic_launcher.png
- cp -fp $(CMAKE_SOURCE_DIR)/media/splash.* xbmc/res/drawable-xxxhdpi/
+ cp -fp $(CMAKE_SOURCE_DIR)/media/applaunch_screen.png xbmc/res/drawable-xxxhdpi/
cp -fp media/drawable-xxxhdpi/ic_launcher.png xbmc/res/drawable-xxxhdpi/ic_launcher.png
cp -fp media/drawable-xhdpi/banner.png xbmc/res/drawable-xhdpi/banner.png
cp -fp $(CMAKE_SOURCE_DIR)/media/icon80x80.png xbmc/res/drawable/ic_recommendation_80dp.png
@@ -116,8 +116,8 @@ apk-clean:
rm -rf xbmc/obj
rm -rf xbmc/res/raw
rm -rf xbmc/res/values
- rm -f xbmc/res/drawable/splash.*
- rm -f xbmc/res/drawable-xxxhdpi/splash.*
+ rm -f xbmc/res/drawable/applaunch_screen.png
+ rm -f xbmc/res/drawable-xxxhdpi/applaunch_screen.png
rm -rf assets
.PHONY: force libs assets python sharedapk res package
diff --git a/tools/android/packaging/xbmc/res/layout/activity_splash.xml b/tools/android/packaging/xbmc/res/layout/activity_splash.xml
index eedd3a4668..42e2e29333 100644
--- a/tools/android/packaging/xbmc/res/layout/activity_splash.xml
+++ b/tools/android/packaging/xbmc/res/layout/activity_splash.xml
@@ -11,7 +11,7 @@
android:adjustViewBounds="true"
android:layout_alignParentTop="true"
android:scaleType="centerCrop"
- android:src="@drawable/splash" />
+ android:src="@drawable/applaunch_screen" />
<RelativeLayout
android:layout_width="wrap_content"
diff --git a/tools/android/packaging/xbmc/res/values-ar-rsa/strings.xml b/tools/android/packaging/xbmc/res/values-ar-rsa/strings.xml
index efe0a1d8ee..6b4d47ebad 100644
--- a/tools/android/packaging/xbmc/res/values-ar-rsa/strings.xml
+++ b/tools/android/packaging/xbmc/res/values-ar-rsa/strings.xml
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- </resources> \ No newline at end of file
+ <string name="suggestion_channel">اقتراحات</string>
+</resources> \ No newline at end of file
diff --git a/tools/buildsteps/osx-arm64/configure-xbmc b/tools/buildsteps/osx-arm64/configure-xbmc
index 2fed17071c..28fb65084d 100755
--- a/tools/buildsteps/osx-arm64/configure-xbmc
+++ b/tools/buildsteps/osx-arm64/configure-xbmc
@@ -2,4 +2,4 @@ WORKSPACE=${WORKSPACE:-$( cd $(dirname $0)/../../.. ; pwd -P )}
XBMC_PLATFORM_DIR=osx-arm64
. $WORKSPACE/tools/buildsteps/defaultenv
-make -C $WORKSPACE/tools/depends/target/cmakebuildsys APP_WINDOW_SYSTEM=native CMAKE_EXTRA_ARGUMENTS="-D CODE_SIGN_IDENTITY='$CODE_SIGN_IDENTITY' -D DEV_ACCOUNT='$DEV_ACCOUNT' -D DEV_ACCOUNT_PASSWORD='$DEV_ACCOUNT_PASSWORD' -D DEV_TEAM='$DEV_TEAM'"
+make -C $WORKSPACE/tools/depends/target/cmakebuildsys CMAKE_EXTRA_ARGUMENTS="-D CODE_SIGN_IDENTITY='$CODE_SIGN_IDENTITY' -D NOTARYTOOL_KEYCHAIN_PROFILE='$NOTARYTOOL_KEYCHAIN_PROFILE' -D NOTARYTOOL_KEYCHAIN_PATH='$NOTARYTOOL_KEYCHAIN_PATH'"
diff --git a/tools/buildsteps/osx64/configure-xbmc b/tools/buildsteps/osx64/configure-xbmc
index 98736c5384..2643cc9e3f 100755
--- a/tools/buildsteps/osx64/configure-xbmc
+++ b/tools/buildsteps/osx64/configure-xbmc
@@ -2,4 +2,4 @@ WORKSPACE=${WORKSPACE:-$( cd $(dirname $0)/../../.. ; pwd -P )}
XBMC_PLATFORM_DIR=osx64
. $WORKSPACE/tools/buildsteps/defaultenv
-make -C $WORKSPACE/tools/depends/target/cmakebuildsys CMAKE_EXTRA_ARGUMENTS="-D CODE_SIGN_IDENTITY='$CODE_SIGN_IDENTITY' -D DEV_ACCOUNT='$DEV_ACCOUNT' -D DEV_ACCOUNT_PASSWORD='$DEV_ACCOUNT_PASSWORD' -D DEV_TEAM='$DEV_TEAM'"
+make -C $WORKSPACE/tools/depends/target/cmakebuildsys CMAKE_EXTRA_ARGUMENTS="-D CODE_SIGN_IDENTITY='$CODE_SIGN_IDENTITY' -D NOTARYTOOL_KEYCHAIN_PROFILE='$NOTARYTOOL_KEYCHAIN_PROFILE' -D NOTARYTOOL_KEYCHAIN_PATH='$NOTARYTOOL_KEYCHAIN_PATH'"
diff --git a/tools/buildsteps/windows/patches/0005-ffmpeg-windows-dxva2-check-nullptr-surface.patch b/tools/buildsteps/windows/patches/0005-ffmpeg-windows-dxva2-check-nullptr-surface.patch
new file mode 100644
index 0000000000..b3e572859b
--- /dev/null
+++ b/tools/buildsteps/windows/patches/0005-ffmpeg-windows-dxva2-check-nullptr-surface.patch
@@ -0,0 +1,12 @@
+diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c
+--- a/libavcodec/dxva2.c
++++ b/libavcodec/dxva2.c
+@@ -777,7 +777,7 @@ unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx,
+ #if CONFIG_D3D11VA
+ if (avctx->pix_fmt == AV_PIX_FMT_D3D11)
+ return (intptr_t)frame->data[1];
+- if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) {
++ if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD && surface) {
+ D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
+ ID3D11VideoDecoderOutputView_GetDesc((ID3D11VideoDecoderOutputView*) surface, &viewDesc);
+ return viewDesc.Texture2D.ArraySlice;
diff --git a/tools/darwin/Support/copyframeworks-darwin_embedded.command b/tools/darwin/Support/copyframeworks-darwin_embedded.command
index 1818224136..c960586d81 100755
--- a/tools/darwin/Support/copyframeworks-darwin_embedded.command
+++ b/tools/darwin/Support/copyframeworks-darwin_embedded.command
@@ -95,27 +95,6 @@ echo "Checking addons *.so for dylib dependencies"
check_xbmc_dylib_depends "$XBMC_HOME"/addons "*.so"
echo "Checking xbmc/DllPaths_generated.h for dylib dependencies"
-for a in $(grep .dylib "$BUILD_ROOT"/xbmc/DllPaths_generated.h | awk '{print $3}' | sed s/\"//g) ; do
+for a in $(grep .so "$BUILD_ROOT"/xbmc/DllPaths_generated.h | awk '{print $3}' | sed s/\"//g) ; do
check_dyloaded_depends $a
done
-
-echo "Checking $TARGET_FRAMEWORKS for missing dylib dependencies"
-REWIND="1"
-while [ $REWIND = "1" ]
-do
- let REWIND="0"
- for b in "$TARGET_FRAMEWORKS/"*dylib* ; do
- #echo " Processing $b"
- for a in $(otool -L "$b" | grep "$EXTERNAL_LIBS" | awk ' { print $1 } ') ; do
- #echo "Processing $a"
- if [ ! -f "$TARGET_FRAMEWORKS/$(basename $a)" ]; then
- echo " Packaging $a"
- cp -f "$a" "$TARGET_FRAMEWORKS/"
- chmod u+w "$TARGET_FRAMEWORKS/$(basename $a)"
- let REWIND="1"
- fi
- install_name_tool -change "$a" "$DYLIB_NAMEPATH/$(basename $a)" "$TARGET_FRAMEWORKS/$(basename $b)"
- done
- done
-done
-
diff --git a/tools/darwin/packaging/osx/mkdmg-osx.sh.in b/tools/darwin/packaging/osx/mkdmg-osx.sh.in
index cded94d728..c4edd0d9d8 100755
--- a/tools/darwin/packaging/osx/mkdmg-osx.sh.in
+++ b/tools/darwin/packaging/osx/mkdmg-osx.sh.in
@@ -78,7 +78,7 @@ echo "done"
# codesign and notarize dmg
if [ "$EXPANDED_CODE_SIGN_IDENTITY_NAME" ]; then
codesign --verbose=4 --sign "$EXPANDED_CODE_SIGN_IDENTITY_NAME" "$dmgPath"
- if ! ./notarize.sh "$dmgPath" "$APP/Contents/Info.plist" && [ "$isReleaseBuild" = 1 ]; then
+ if ! ./notarize.sh "$dmgPath" && [ "$isReleaseBuild" = 1 ]; then
exit 1
fi
fi
diff --git a/tools/darwin/packaging/osx/notarize.sh b/tools/darwin/packaging/osx/notarize.sh
index 1c8f132258..8f6b089cf8 100755
--- a/tools/darwin/packaging/osx/notarize.sh
+++ b/tools/darwin/packaging/osx/notarize.sh
@@ -1,67 +1,20 @@
#!/usr/bin/env bash
-# credits: https://scriptingosx.com/2019/09/notarize-a-command-line-tool/
+# credits:
+# https://scriptingosx.com/2019/09/notarize-a-command-line-tool/
+# https://developer.apple.com/documentation/technotes/tn3147-migrating-to-the-latest-notarization-tool
-if [[ -z "$DEV_ACCOUNT" || -z "$DEV_ACCOUNT_PASSWORD" ]]; then
+set -e
+
+if [[ -z "$NOTARYTOOL_KEYCHAIN_PROFILE" ]]; then
echo "skipping notarization"
exit 0
fi
-notarizefile() { # $1: path to file to notarize, $2: identifier
- filepath=${1:?"need a filepath"}
- identifier=${2:?"need an identifier"}
-
- # upload file
- echo "uploading $filepath for notarization"
- altoolOutput=$(xcrun altool \
- --notarize-app \
- --type osx \
- --file "$filepath" \
- --primary-bundle-id "$identifier" \
- --username "$DEV_ACCOUNT" \
- --password "$DEV_ACCOUNT_PASSWORD" \
- ${DEV_TEAM:+--asc-provider "$DEV_TEAM"} 2>&1)
-
- requestUUID=$(echo "$altoolOutput" | awk '/RequestUUID/ { print $NF; }')
-
- if [[ $requestUUID == "" ]]; then
- echo "Failed to upload:"
- echo "$altoolOutput"
- return 1
- fi
- echo "requestUUID: $requestUUID, waiting..."
-
- # wait for status to be not "in progress" any more
- request_status="in progress"
- while [[ "$request_status" == "in progress" ]]; do
- sleep 60
- altoolOutput=$(xcrun altool \
- --notarization-info "$requestUUID" \
- --username "$DEV_ACCOUNT" \
- --password "$DEV_ACCOUNT_PASSWORD" 2>&1)
- request_status=$(echo "$altoolOutput" | awk -F ': ' '/Status:/ { print $2; }' )
- done
-
- # print status information
- echo "$altoolOutput"
-
- if [[ $request_status != "success" ]]; then
- echo "warning: could not notarize $filepath"
- notarizationFailed=1
- fi
-
- LogFileURL=$(echo "$altoolOutput" | awk -F ': ' '/LogFileURL:/ { print $2; }')
- if [[ "$LogFileURL" ]]; then
- echo -e "\nnotarization details:"
- curl "$LogFileURL"
- echo
- fi
- if [[ $notarizationFailed == 1 ]]; then
- return 1
- fi
- return 0
-}
-
dmg="$1"
-notarizefile "$dmg" $(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$2") \
- && xcrun stapler staple "$dmg"
+xcrun notarytool submit \
+ --keychain-profile "$NOTARYTOOL_KEYCHAIN_PROFILE" \
+ ${NOTARYTOOL_KEYCHAIN_PATH:+--keychain "$NOTARYTOOL_KEYCHAIN_PATH"} \
+ --wait --timeout '1h' \
+ "$dmg" 2>&1
+xcrun stapler staple "$dmg"
diff --git a/tools/depends/Makefile.include.in b/tools/depends/Makefile.include.in
index e91bf152f4..1903fecd85 100644
--- a/tools/depends/Makefile.include.in
+++ b/tools/depends/Makefile.include.in
@@ -26,7 +26,6 @@ ARCH_DEFINES=@ARCH_DEFINES@
NATIVE_ARCH_DEFINES=@NATIVE_ARCH_DEFINES@
TARGET_PLATFORM=@target_platform@
RENDER_SYSTEM=@app_rendersystem@
-WINDOW_SYSTEM=@app_winsystem@
SHA512SUM=@SHA512SUM@
SHA256SUM=@SHA256SUM@
SHASUM=@SHASUM@
diff --git a/tools/depends/configure.ac b/tools/depends/configure.ac
index 957a9e89c9..b09b586dc5 100644
--- a/tools/depends/configure.ac
+++ b/tools/depends/configure.ac
@@ -102,11 +102,6 @@ AC_ARG_WITH([rendersystem],
[render system to use])],
[app_rendersystem=$withval])
-AC_ARG_WITH([windowsystem],
- [AS_HELP_STRING([--with-windowsystem],
- [Windowing system to use])],
- [app_winsystem=$withval])
-
AC_ARG_WITH([target-cflags],
[AS_HELP_STRING([--with-target-cflags],
[C compiler flags (target)])],
@@ -323,7 +318,7 @@ case $host in
use_cpu="armeabi-v7a"
fi
if test "x$use_cpu" = "xarmeabi-v7a"; then
- platform_cflags="${platform_cflags} -march=armv7-a -mtune=cortex-a9 -mfloat-abi=softfp -mfpu=neon"
+ platform_cflags="${platform_cflags} -mtune=cortex-a9 -mfloat-abi=softfp"
fi
platform_ldflags="${platform_ldflags} -Wl,--exclude-libs,libunwind.a"
meson_cpu="arm"
@@ -333,7 +328,7 @@ case $host in
use_cpu="arm64-v8a"
fi
if test "x$use_cpu" = "xarm64-v8a"; then
- platform_cflags="${platform_cflags} -march=armv8-a -mtune=cortex-a53"
+ platform_cflags="${platform_cflags} -mtune=cortex-a53"
fi
meson_cpu="aarch64"
;;
@@ -462,27 +457,16 @@ case $host in
fi
target_minver="10.14"
-
- # check provided window system is valid_sdk
- # if no window system supplied, default to native.
- if test -n "$app_winsystem"; then
- if test "$app_winsystem" != "native" && test "$app_winsystem" != "sdl"; then
- AC_MSG_ERROR(Window system must be native or sdl)
- fi
- else
- app_winsystem=native
- fi
;;
aarch64-apple-darwin*)
MC_CHECK_NOT_CPU([$use_cpu], "*86")
case $platform_os in
darwin_embedded)
- target_minver="11.0"
+ target_minver="12.0"
;;
osx)
target_minver="11.0"
- app_winsystem=native
;;
*)
AC_MSG_ERROR(invalid platform for architecture ($host))
@@ -586,7 +570,7 @@ if test "$platform_os" = "android"; then
AC_MSG_ERROR("SDK path is required for android")
fi
- if [ ! test -f $use_sdk_path/tools/bin/sdkmanager ]; then
+ if [ ! test -f $use_sdk_path/tools/bin/sdkmanager ] && [ ! test -f $use_sdk_path/cmdline-tools/bin/sdkmanager ]; then
AC_MSG_ERROR(verify sdk path)
fi
@@ -734,7 +718,6 @@ AC_SUBST(host_includes)
AC_SUBST(host_sysroot)
AC_SUBST(host_cxxflags)
AC_SUBST(app_rendersystem)
-AC_SUBST(app_winsystem)
AC_SUBST(ffmpeg_options)
[
diff --git a/tools/depends/native/Makefile b/tools/depends/native/Makefile
index c0489a2119..39cf54edaa 100644
--- a/tools/depends/native/Makefile
+++ b/tools/depends/native/Makefile
@@ -85,7 +85,7 @@ python3: $(EXPAT) $(LIBFFI) pkg-config zlib openssl autoconf-archive
swig: pcre
tar: xz automake
TexturePacker: cmake libpng liblzo2 giflib libjpeg-turbo
-wayland-scanner: expat pkg-config
+wayland-scanner: expat ninja pkg-config
waylandpp-scanner: cmake pugixml
# python installs are not thread safe when using easy_install method.
diff --git a/tools/depends/native/openssl/Makefile b/tools/depends/native/openssl/Makefile
index 189acf0855..8f4abd7f61 100644
--- a/tools/depends/native/openssl/Makefile
+++ b/tools/depends/native/openssl/Makefile
@@ -1,14 +1,6 @@
-include ../../Makefile.include
+include ../../Makefile.include OPENSSL-VERSION ../../download-files.include
PLATFORM=$(NATIVEPLATFORM)
-DEPS = ../../Makefile.include Makefile ../../download-files.include
-
-# lib name, version
-LIBNAME=openssl
-VERSION=1.1.1k
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=73cd042d4056585e5a9dd7ab68e7c7310a3a4c783eafa07ab0b560e7462b924e4376436a6d38a155c687f6942a881cfc0c1b9394afcde1d8c46bf396e7d51121
-include ../../download-files.include
+DEPS = ../../Makefile.include Makefile OPENSSL-VERSION ../../download-files.include
# configuration settings
CONFIGURE=MACHINE=$(PLATFORM) ./config no-shared zlib no-asm --prefix=$(NATIVEPREFIX) --with-zlib-include=$(NATIVEPREFIX)/include --with-zlib-lib=$(NATIVEPREFIX)/lib
diff --git a/tools/depends/native/openssl/OPENSSL-VERSION b/tools/depends/native/openssl/OPENSSL-VERSION
new file mode 100644
index 0000000000..1712a0c3da
--- /dev/null
+++ b/tools/depends/native/openssl/OPENSSL-VERSION
@@ -0,0 +1,4 @@
+LIBNAME=openssl
+VERSION=1.1.1w
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=b4c625fe56a4e690b57b6a011a225ad0cb3af54bd8fb67af77b5eceac55cc7191291d96a660c5b568a08a2fbf62b4612818e7cca1bb95b2b6b4fc649b0552b6d
diff --git a/tools/depends/native/python3/PYTHON3-VERSION b/tools/depends/native/python3/PYTHON3-VERSION
index 290e333fe8..c5e1760fb2 100644
--- a/tools/depends/native/python3/PYTHON3-VERSION
+++ b/tools/depends/native/python3/PYTHON3-VERSION
@@ -1,4 +1,4 @@
LIBNAME=Python
-VERSION=3.11.2
+VERSION=3.11.7
ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
-SHA512=5684ec7eae2dce26facc54d448ccdb6901bbfa1cab03abbe8fd34e4268a2b701daa13df15903349492447035be78380d473389e8703b4e910a65b088d2462e8b
+SHA512=11e06f2ffe1f66888cb5b4e9f607de815294d6863a77eda6ec6d7c724ef158df9f51881f4a956d4a6fa973c2fb6fd031d495e3496e9b0bb53793fb1cc8434c63
diff --git a/tools/depends/native/wayland-scanner/Makefile b/tools/depends/native/wayland-scanner/Makefile
index 2cbf8d51c1..fc71bb30c0 100644
--- a/tools/depends/native/wayland-scanner/Makefile
+++ b/tools/depends/native/wayland-scanner/Makefile
@@ -1,30 +1,37 @@
-include ../../Makefile.include
+include ../../Makefile.include WAYLAND-SCANNER-VERSION ../../download-files.include
PREFIX=$(NATIVEPREFIX)
PLATFORM=$(NATIVEPLATFORM)
-DEPS =../../Makefile.include Makefile ../../download-files.include
-
-APPNAME=wayland-scanner
-PROJECTNAME=wayland
-VERSION=1.17.0
-SOURCE=$(PROJECTNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.xz
-SHA512=c5051aab5ff078b368c196ecfedb33ccd961265bb914845d7ed81de361bb86ae18299575baa6c4eceb0d82cf8b495e8293f31b51d1cbc05d84af0a199ab3f946
-include ../../download-files.include
+DEPS =../../Makefile.include Makefile WAYLAND-SCANNER-VERSION ../../download-files.include
# configuration settings
-CONFIGURE=./configure --prefix=$(PREFIX) --disable-libraries --disable-documentation --disable-dtd-validation
+CONFIGURE = $(NATIVEPREFIX)/bin/python3 $(NATIVEPREFIX)/bin/meson setup \
+ --prefix $(PREFIX) \
+ --libdir $(PREFIX)/lib \
+ --buildtype=release \
+ -Dlibraries=false \
+ -Dtests=false \
+ -Ddocumentation=false \
+ -Ddtd_validation=false
+
+export CC=$(CC_BINARY_FOR_BUILD)
+export CXX=$(CXX_BINARY_FOR_BUILD)
+export CFLAGS=$(NATIVE_CFLAGS)
+export CXXFLAGS=$(NATIVE_CXXFLAGS)
+export LDFLAGS=$(NATIVE_LDFLAGS)
+
+export PKG_CONFIG_LIBDIR=$(PREFIX)/lib/pkgconfig
all: .installed-$(PLATFORM)
-
$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)
cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); $(CONFIGURE)
+ cd $(PLATFORM); rm -rf build; mkdir -p build
+ cd $(PLATFORM); $(CONFIGURE) . build
.installed-$(PLATFORM): $(PLATFORM)
- $(MAKE) -C $(PLATFORM)
- $(MAKE) -C $(PLATFORM) install
+ cd $(PLATFORM)/build; $(NATIVEPREFIX)/bin/ninja -v
+ cd $(PLATFORM)/build; $(NATIVEPREFIX)/bin/ninja -v install
touch $@
clean:
diff --git a/tools/depends/native/wayland-scanner/WAYLAND-SCANNER-VERSION b/tools/depends/native/wayland-scanner/WAYLAND-SCANNER-VERSION
new file mode 100644
index 0000000000..580ede5ca1
--- /dev/null
+++ b/tools/depends/native/wayland-scanner/WAYLAND-SCANNER-VERSION
@@ -0,0 +1,4 @@
+LIBNAME=wayland
+VERSION=1.22.0
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
+SHA512=fb1974efc8433e97254eb83fe28974198f2b4d8246418eb3d34ce657055461e0c97bc06dd52e5066ae91bbe05bac611dc49a0937ba226ac6388d5a47241efb12
diff --git a/tools/depends/native/waylandpp-scanner/Makefile b/tools/depends/native/waylandpp-scanner/Makefile
index ccfc8caa91..93816f4793 100644
--- a/tools/depends/native/waylandpp-scanner/Makefile
+++ b/tools/depends/native/waylandpp-scanner/Makefile
@@ -1,15 +1,7 @@
-include ../../Makefile.include
+include ../../Makefile.include WAYLANDPP-SCANNER-VERSION ../../download-files.include
PREFIX=$(NATIVEPREFIX)
PLATFORM=$(NATIVEPLATFORM)
-DEPS =../../Makefile.include Makefile ../../download-files.include 001-fix-gcc13-build.patch
-
-# lib name, version
-LIBNAME=waylandpp
-VERSION=0.2.8
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=bf1b8a9e69b87547fc65989b9eaff88a442d8b2f01f5446cef960000b093390b1e557536837fbf38bb6d9a4f93e3985ea34c3253f94925b0f571b4606c980832
-include ../../download-files.include
+DEPS =../../Makefile.include Makefile WAYLANDPP-SCANNER-VERSION ../../download-files.include 001-fix-gcc13-build.patch
CMAKE_OPTIONS := -DBUILD_DOCUMENTATION=OFF \
-DBUILD_LIBRARIES=OFF \
diff --git a/tools/depends/native/waylandpp-scanner/WAYLANDPP-SCANNER-VERSION b/tools/depends/native/waylandpp-scanner/WAYLANDPP-SCANNER-VERSION
new file mode 100644
index 0000000000..b87fb9bbe7
--- /dev/null
+++ b/tools/depends/native/waylandpp-scanner/WAYLANDPP-SCANNER-VERSION
@@ -0,0 +1,4 @@
+LIBNAME=waylandpp
+VERSION=1.0.0
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=64b59d073a0593ecf442362eb63ec0a9dfeaa1ad1d56b5955cb0c159fd01dc45e012b926811c6ca0dc12d4bb2e640eabc2e778ab7d28de2098eb694d26f01039
diff --git a/tools/depends/target/Makefile b/tools/depends/target/Makefile
index c968d71f8d..bdfebac073 100644
--- a/tools/depends/target/Makefile
+++ b/tools/depends/target/Makefile
@@ -23,7 +23,6 @@ DEPENDS = \
harfbuzz \
libass \
libbluray \
- libcec \
libffi \
libgcrypt \
libgpg-error \
@@ -40,7 +39,6 @@ DEPENDS = \
nettle \
nghttp2 \
openssl \
- p8-platform \
python3 \
pythonmodule-pil \
pythonmodule-pycryptodome \
@@ -57,9 +55,8 @@ else
endif
ifeq ($(OS),darwin_embedded)
- EXCLUDED_DEPENDS = libcec libusb gtest
+ EXCLUDED_DEPENDS = libusb gtest
ifeq ($(TARGET_PLATFORM),appletvos)
- DEPENDS += boblight
EXCLUDED_DEPENDS += libshairplay libplist
endif
DEPENDS += darwin-embedded-entitlements
@@ -67,15 +64,11 @@ endif
ifeq ($(OS),osx)
EXCLUDED_DEPENDS = libusb
- ifneq ($(CPU),arm64)
- ifeq ($(WINDOW_SYSTEM),sdl)
- DEPENDS += libsdl
- endif
- endif
+ DEPENDS += smctemp
endif
ifeq ($(OS),android)
- EXCLUDED_DEPENDS = libcec libusb gtest
+ EXCLUDED_DEPENDS = libusb gtest
DEPENDS += dummy-libxbmc libdovi libuuid
PYMODULE_DEPS = dummy-libxbmc
LIBUUID = libuuid
@@ -157,7 +150,7 @@ $(DOWNLOAD_TARGETS):
download: $(DOWNLOAD_TARGETS)
crossguid: $(LIBUUID)
-curl: openssl nghttp2
+curl: openssl nghttp2 $(ZLIB)
dbus: expat
ffmpeg: $(ICONV) $(ZLIB) bzip2 gnutls dav1d $(LIBVA)
fontconfig: freetype2 expat $(ICONV) $(LIBUUID)
@@ -169,7 +162,6 @@ libass: fontconfig fribidi harfbuzz libpng freetype2 expat $(ICONV)
libbluray: fontconfig freetype2 $(ICONV) udfread libxml2
libcdio-gplv3: $(ICONV)
libcdio: $(ICONV)
-libcec: p8-platform
libdisplay-info: hwdata
libevdev: libudev
libgcrypt: libgpg-error
@@ -182,7 +174,7 @@ libxml2: $(ZLIB)
libxslt: libgcrypt libxml2
libzip: bzip2 gnutls $(ZLIB)
mariadb: openssl $(ICONV) $(ZLIB)
-mesa: libdrm $(MESA_DEPS)
+mesa: libdrm $(MESA_DEPS) $(ZLIB)
nettle: gmp
openssl: $(ZLIB)
python3: expat gettext libxml2 sqlite3 openssl libffi bzip2 xz $(ICONV)
diff --git a/tools/depends/target/Toolchain.cmake.in b/tools/depends/target/Toolchain.cmake.in
index a1d6b548a1..ffed3ce3f1 100644
--- a/tools/depends/target/Toolchain.cmake.in
+++ b/tools/depends/target/Toolchain.cmake.in
@@ -48,7 +48,7 @@ if(CORE_SYSTEM_NAME STREQUAL darwin_embedded)
set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
if(CORE_PLATFORM_NAME STREQUAL tvos)
set(CMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "3")
- set(CMAKE_XCODE_ATTRIBUTE_TVOS_DEPLOYMENT_TARGET 11.0)
+ set(CMAKE_XCODE_ATTRIBUTE_TVOS_DEPLOYMENT_TARGET 12.0)
else()
set(CMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2")
set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 11.0)
diff --git a/tools/depends/target/boblight/01-fix_fpermissive.patch b/tools/depends/target/boblight/01-fix_fpermissive.patch
deleted file mode 100644
index aab4788620..0000000000
--- a/tools/depends/target/boblight/01-fix_fpermissive.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/src/util/misc.cpp
-+++ b/src/util/misc.cpp
-@@ -64,7 +64,7 @@
- //convert . or , to the current locale for correct conversion of ascii float
- void ConvertFloatLocale(std::string& strfloat)
- {
-- static struct lconv* locale = localeconv();
-+ static const struct lconv* locale = localeconv();
-
- size_t pos = strfloat.find_first_of(",.");
-
diff --git a/tools/depends/target/boblight/02-fixandroid.patch b/tools/depends/target/boblight/02-fixandroid.patch
deleted file mode 100644
index ff4b5b1077..0000000000
--- a/tools/depends/target/boblight/02-fixandroid.patch
+++ /dev/null
@@ -1,35 +0,0 @@
---- a/src/lib/boblight.h
-+++ b/src/lib/boblight.h
-@@ -61,7 +61,7 @@
- //gets a functionpointer from dlsym, and returns char* from dlerror if it didn't work
- #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) \
- name = BOBLIGHT_CAST(returnvalue (*) arguments)(dlsym(p_boblight, #name)); \
-- { char* error = dlerror(); if (error) return error; }
-+ { char* error = (char *)dlerror(); if (error) return error; }
-
- void* p_boblight = NULL; //where we put the lib
-
-@@ -79,7 +79,7 @@
-
- p_boblight = dlopen(filename, RTLD_NOW);
- if (p_boblight == NULL)
-- return dlerror();
-+ return (char *)dlerror();
-
- //generate dlsym lines
- #include "boblight-functions.h"
---- a/src/util/daemonize.cpp
-+++ b/src/util/daemonize.cpp
-@@ -35,10 +35,12 @@
- if (setsid() < 0)
- fprintf(stderr, "setsid(): %s", GetErrno().c_str());
-
-+#if !defined(__ANDROID__)
- //route stdout and stderr to /dev/null
- fclose(stdout);
- stdout = fopen("/dev/null", "w");
- fclose(stderr);
- stderr = fopen("/dev/null", "w");
-+#endif
- }
-
diff --git a/tools/depends/target/boblight/03-fixtvos.patch b/tools/depends/target/boblight/03-fixtvos.patch
deleted file mode 100644
index 13f8025568..0000000000
--- a/tools/depends/target/boblight/03-fixtvos.patch
+++ /dev/null
@@ -1,42 +0,0 @@
---- a/src/device/deviceltbl.cpp
-+++ b/src/device/deviceltbl.cpp
-@@ -51,7 +51,7 @@
-
- bool CDeviceLtbl::WriteOutput()
- {
-- uint8_t prefix[4] = {0x55, 0xAA, 0x00, m_channels.size()};
-+ uint8_t prefix[4] = {0x55, 0xAA, 0x00, (uint8_t)m_channels.size()};
-
- //get the channel values from the clienshandler
- int64_t now = GetTimeUs();
-@@ -114,7 +114,7 @@
- uint8_t buff[512];
- uint8_t prefix[2] = {0x55, 0xAA};
- uint8_t open[2] = {0x83, 0x00};
-- uint8_t getvalues[4] = {0x81, 0x02, 0x00, m_channels.size()};
-+ uint8_t getvalues[4] = {0x81, 0x02, 0x00, (uint8_t)m_channels.size()};
-
- if (m_isopened)
- return true; //nothing to do here
---- a/src/util/daemonize.cpp
-+++ b/src/util/daemonize.cpp
-@@ -25,7 +25,7 @@
- void Daemonize()
- {
- //fork a child process
-- pid_t pid = fork();
-+ pid_t pid = -1;//fork();
- if (pid == -1)
- fprintf(stderr, "fork(): %s", GetErrno().c_str());
- else if (pid > 0)
---- a/src/util/tcpsocket.cpp
-+++ b/src/util/tcpsocket.cpp
-@@ -31,8 +31,6 @@
- #include "tcpsocket.h"
- #include "misc.h"
-
--using namespace std;
--
- void CTcpData::SetData(uint8_t* data, int size, bool append)
- {
- CopyData(reinterpret_cast<char*>(data), size, append);
diff --git a/tools/depends/target/boblight/Makefile b/tools/depends/target/boblight/Makefile
deleted file mode 100644
index 641061cd38..0000000000
--- a/tools/depends/target/boblight/Makefile
+++ /dev/null
@@ -1,57 +0,0 @@
-include ../../Makefile.include
-DEPS = ../../Makefile.include Makefile 01-fix_fpermissive.patch 02-fixandroid.patch 03-fixtvos.patch ../../download-files.include
-
-#hint for building a fat lib - "lipo -arch i386 libboblight-i386.dylib -arch x86_64 libboblight-x86_64.dylib -output libboblight-fat.dylib"
-
-# lib name, version
-LIBNAME=libboblight
-VERSION=r478
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=382e0b0f1ef2fca676cd64ec4190d3cfb791fed0f9477af8436e461cebfbc268058abc1fbba97a0337d3152a9b292580160b42157b4076d59b3847071deb1881
-include ../../download-files.include
-
-# configuration settings
-CONFIGURE=./configure --prefix=$(PREFIX) \
- --without-opengl \
- --without-portaudio \
- --without-x11 \
- --without-libusb
-
-LIBDYLIB=$(PLATFORM)/src/.libs/$(LIBNAME).a
-
-all: $(LIBDYLIB) .installed-$(PLATFORM)
-
-
-$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
- rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)
- cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); patch -p1 -i ../01-fix_fpermissive.patch
- cd $(PLATFORM); patch -p1 -i ../02-fixandroid.patch
- cd $(PLATFORM); autoreconf -vif
- cd $(PLATFORM); $(CONFIGURE)
-ifeq ($(CPU),arm64)
- cd $(PLATFORM); patch -p1 -i ../03-fixtvos.patch
- cd $(PLATFORM); sed -ie "s|-bind_at_load||" ./libtool
- cd $(PLATFORM); sed -ie "s|-bind_at_load||" ./ltmain.sh
-endif
-
-$(LIBDYLIB): $(PLATFORM)
- $(MAKE) -C $(PLATFORM)
-
-.installed-$(PLATFORM): $(LIBDYLIB)
-ifeq ($(OS),darwin_embedded)
-ifeq ($(TARGET_PLATFORM),appletvos)
- #deploy into source tree for tvos - we distribute libboblight with the bundle...
- cp $(PLATFORM)/src/.libs/libboblight.0.dylib $(CMAKE_SOURCE_DIR)/system/libboblight-tvos.0.dylib
-endif
-else
- echo "libboblight isn't a dependency of XBMC and won't be installed"
-endif
- touch $@
-clean:
- $(MAKE) -C $(PLATFORM) clean
- rm -r .installed-$(PLATFORM)
-
-distclean::
- rm -rf $(PLATFORM) .installed-$(PLATFORM)
diff --git a/tools/depends/target/cec/001-all-cmakelists.patch b/tools/depends/target/cec/001-all-cmakelists.patch
new file mode 100644
index 0000000000..370c3a0fe1
--- /dev/null
+++ b/tools/depends/target/cec/001-all-cmakelists.patch
@@ -0,0 +1,40 @@
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -6,6 +6,7 @@
+ set(LIBCEC_VERSION_PATCH 7)
+
+ # cec-client
++if(ENABLE_CLIENT)
+ add_subdirectory(src/cec-client)
+ add_dependencies(cec-client cec)
+
+@@ -15,6 +16,7 @@
+
+ # pyCecClient
+ add_subdirectory(src/pyCecClient)
++endif()
+
+ # libCEC
+ add_subdirectory(src/libcec)
+@@ -25,6 +27,7 @@
+
+ # windows specific files
+ if(WIN32)
++if(ENABLE_CLIENT)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project/nsis/libcec-version.nsh.in
+ ${CMAKE_CURRENT_SOURCE_DIR}/project/nsis/libcec-version.nsh)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharp/LibCecSharp.rc.in
+@@ -40,3 +43,13 @@
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/CecSharpTester/netfx/CecSharpTester.csproj.in
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/CecSharpTester/netfx/CecSharpTester.csproj)
+ endif()
++endif()
++
++# handle version file
++include(CMakePackageConfigHelpers)
++write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
++ VERSION "${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}"
++ COMPATIBILITY AnyNewerVersion)
++
++install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
++ DESTINATION lib/cmake/${PROJECT_NAME})
diff --git a/tools/depends/target/cec/002-all-libceccmakelists.patch b/tools/depends/target/cec/002-all-libceccmakelists.patch
new file mode 100644
index 0000000000..a5b44a6db0
--- /dev/null
+++ b/tools/depends/target/cec/002-all-libceccmakelists.patch
@@ -0,0 +1,42 @@
+--- a/src/libcec/CMakeLists.txt
++++ b/src/libcec/CMakeLists.txt
+@@ -175,7 +175,7 @@
+ ${CMAKE_INSTALL_PREFIX}/include)
+
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libcec.pc
+- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
++ DESTINATION lib/pkgconfig)
+ endif()
+
+ # install headers
+@@ -193,11 +193,25 @@
+ # libCEC shared target
+ add_library(cec SHARED ${CEC_SOURCES})
+ install(TARGETS cec
+- DESTINATION ${LIB_DESTINATION})
++ EXPORT libcec
++ RUNTIME DESTINATION bin
++ ARCHIVE DESTINATION lib
++ LIBRARY DESTINATION lib)
++
++install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/libcec-config.cmake
++ DESTINATION lib/cmake/libcec)
++
+ set_target_properties(cec PROPERTIES VERSION ${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}
+ SOVERSION ${LIBCEC_VERSION_MAJOR})
+ target_link_libraries(cec ${cec_depends})
+
++install(EXPORT libcec
++ NAMESPACE
++ libcec::
++ DESTINATION
++ lib/cmake/libcec
++)
++
+ if (MSVC)
+ # generate pdb in release mode too
+ set_target_properties(cec
+--- /dev/null
++++ b/src/libcec/cmake/libcec-config.cmake
+@@ -0,0 +1 @@
++include(${CMAKE_CURRENT_LIST_DIR}/libcec.cmake)
diff --git a/tools/depends/target/libcec/remove_git_info.patch b/tools/depends/target/cec/003-all-remove_git_info.patch
index 4ade84bba4..5927552edb 100644
--- a/tools/depends/target/libcec/remove_git_info.patch
+++ b/tools/depends/target/cec/003-all-remove_git_info.patch
@@ -15,4 +15,4 @@
+ endif()
# add compilation date to compile info
- find_program(HAVE_DATE_BIN date /bin /usr/bin /usr/local/bin)
+ STRING(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S" UTC)
diff --git a/tools/depends/target/cec/004-win-remove_32bit_timet.patch b/tools/depends/target/cec/004-win-remove_32bit_timet.patch
new file mode 100644
index 0000000000..4fe2a2f5db
--- /dev/null
+++ b/tools/depends/target/cec/004-win-remove_32bit_timet.patch
@@ -0,0 +1,10 @@
+--- a/src/libcec/cmake/CheckPlatformSupport.cmake
++++ b/src/libcec/cmake/CheckPlatformSupport.cmake
+@@ -49,7 +49,6 @@
+
+ if("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "X86")
+ set(LIB_INFO "${LIB_INFO} (x86)")
+- add_definitions(-D_USE_32BIT_TIME_T)
+ # force python2 for eventghost
+ set(PYTHON_USE_VERSION 2)
+ elseif("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "x64")
diff --git a/tools/depends/target/cec/005-win-pdbstatic.patch b/tools/depends/target/cec/005-win-pdbstatic.patch
new file mode 100644
index 0000000000..9dfdc17c66
--- /dev/null
+++ b/tools/depends/target/cec/005-win-pdbstatic.patch
@@ -0,0 +1,58 @@
+--- a/src/libcec/CMakeLists.txt
++++ b/src/libcec/CMakeLists.txt
+@@ -213,15 +213,26 @@
+
+ if (MSVC)
+ # generate pdb in release mode too
++ # Tell linker to include symbol data
++ target_compile_options(cec PRIVATE $<$<CONFIG:RELEASE>:/Zi>)
++
++ # Tell linker to include symbol data
++ set_target_properties(cec PROPERTIES
++ LINK_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF"
++ )
+ set_target_properties(cec
+ PROPERTIES
+ COMPILE_PDB_NAME_DEBUG cec${CMAKE_DEBUG_POSTFIX}
+ COMPILE_PDB_NAME_RELEASE cec
+ COMPILE_PDB_NAME_MINSIZEREL cec
+ COMPILE_PDB_NAME_RELWITHDEBINFO cec)
++
++ # install generated pdb
++ install(FILES $<TARGET_PDB_FILE:cec> DESTINATION lib)
+ endif(MSVC)
+
+ if(WIN32)
++if(ENABLE_STATIC)
+ # libCEC static target used by .net wrappers
+ add_library(cec-static STATIC ${CEC_SOURCES})
+ install(TARGETS cec-static
+@@ -243,6 +243,12 @@
+
+ if (MSVC)
+ # generate pdb in release mode too
++ target_compile_options(cec-static PRIVATE $<$<CONFIG:RELEASE>:/Zi>)
++
++ # Tell linker to include symbol data
++ set_target_properties(cec-static PROPERTIES
++ LINK_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF"
++ )
+ set_target_properties(cec-static
+ PROPERTIES
+ COMPILE_PDB_NAME_DEBUG cec-static${CMAKE_DEBUG_POSTFIX}
+@@ -256,12 +256,10 @@
+ COMPILE_PDB_NAME_MINSIZEREL cec-static
+ COMPILE_PDB_NAME_RELWITHDEBINFO cec-static)
+
+- # install generated pdb
+- install(FILES $<TARGET_FILE_DIR:cec>/cec.pdb
+- DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+- install(FILES $<TARGET_FILE_DIR:cec-static>/cec-static.pdb
+- DESTINATION "${CMAKE_INSTALL_LIBDIR}")
++ install(FILES $<TARGET_PDB_FILE:cec-static>
++ DESTINATION lib)
+ endif(MSVC)
++endif()
+ endif(WIN32)
+
+ include(cmake/DisplayPlatformSupport.cmake)
diff --git a/tools/depends/target/cec/CEC-VERSION b/tools/depends/target/cec/CEC-VERSION
new file mode 100644
index 0000000000..1a726d3bfe
--- /dev/null
+++ b/tools/depends/target/cec/CEC-VERSION
@@ -0,0 +1,6 @@
+LIBNAME=libcec
+VERSION=4.0.7
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=424540a45f9cae3d5dcccc615d487c45033f9cdeb665b8176832495597e2cd58ef7681e13b52f6a32f8a40e1146c04a1a383f5785ea1e731c5c517a9a7843a81
+BYPRODUCT=libcec.so
+BYPRODUCT_WIN=cec.lib
diff --git a/tools/depends/target/cec/Makefile b/tools/depends/target/cec/Makefile
new file mode 100644
index 0000000000..403f4f03f1
--- /dev/null
+++ b/tools/depends/target/cec/Makefile
@@ -0,0 +1,47 @@
+include ../../Makefile.include CEC-VERSION ../../download-files.include
+DEPS = ../../Makefile.include Makefile CEC-VERSION ../../download-files.include \
+ 001-all-cmakelists.patch \
+ 002-all-libceccmakelists.patch \
+ 003-all-remove_git_info.patch \
+ 004-win-remove_32bit_timet.patch \
+ 005-win-pdbstatic.patch
+
+LIBDYLIB=$(PLATFORM)/build/src/$(LIBNAME)/$(BYPRODUCT)
+ifeq (darwin, $(findstring darwin, $(HOST)))
+ LIBDYLIB=$(PLATFORM)/build/src/$(LIBNAME)/$(BYPRODUCT_MAC)
+endif
+
+CMAKE_OPTIONS=-DBUILD_SHARED_LIBS=1 \
+ -DSKIP_PYTHON_WRAPPER:STRING=1 \
+ -DCMAKE_PLATFORM_NO_VERSIONED_SONAME=1 \
+ -DCMAKE_INSTALL_PREFIX=$(PREFIX) \
+ -DCMAKE_BUILD_TYPE=Debug
+
+all: .installed-$(PLATFORM)
+
+$(PLATFORM): $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE) $(DEPS)
+ rm -rf $(PLATFORM); mkdir -p $(PLATFORM)/build
+ cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
+ cd $(PLATFORM); patch -p1 -i ../001-all-cmakelists.patch
+ cd $(PLATFORM); patch -p1 -i ../002-all-libceccmakelists.patch
+ cd $(PLATFORM); patch -p1 -i ../003-all-remove_git_info.patch
+ cd $(PLATFORM); patch -p1 -i ../004-win-remove_32bit_timet.patch
+ cd $(PLATFORM); patch -p1 -i ../005-win-pdbstatic.patch
+ cd $(PLATFORM)/build; $(CMAKE) ${CMAKE_OPTIONS} ..
+
+$(LIBDYLIB): $(PLATFORM)
+ $(MAKE) -C $(PLATFORM)/build
+
+.installed-$(PLATFORM): $(LIBDYLIB)
+ $(MAKE) -C $(PLATFORM)/build install
+ifeq (darwin, $(findstring darwin, $(HOST)))
+ install_name_tool -id $(PREFIX)/lib/$(LIBNAME).dylib $(PREFIX)/lib/$(LIBNAME).dylib
+endif
+ touch $@
+
+clean:
+ rm -rf $(PLATFORM) .installed-$(PLATFORM)
+
+distclean::
+ rm -rf $(PLATFORM) .installed-$(PLATFORM)
+
diff --git a/tools/depends/target/cmakebuildsys/Makefile b/tools/depends/target/cmakebuildsys/Makefile
index 446d25218d..52941fa014 100644
--- a/tools/depends/target/cmakebuildsys/Makefile
+++ b/tools/depends/target/cmakebuildsys/Makefile
@@ -28,21 +28,13 @@ else
endif
endif
-ifeq ($(OS),osx)
- ifeq ($(APP_WINDOW_SYSTEM),sdl)
- WINDOWSYSTEM=-DAPP_WINDOW_SYSTEM=sdl
- else
- WINDOWSYSTEM=-DAPP_WINDOW_SYSTEM=native
- endif
-endif
-
ifeq ($(BUILD_DIR),)
BUILD_DIR=$(CMAKE_SOURCE_DIR)/build
endif
all:
mkdir -p $(BUILD_DIR)
- cd $(BUILD_DIR); $(CMAKE) $(CMAKE_BUILD_ARGUMENTS) $(WINDOWSYSTEM) -DENABLE_INTERNAL_CROSSGUID=ON -DENABLE_INTERNAL_FFMPEG=OFF $(CMAKE_EXTRA_ARGUMENTS) $(CMAKE_SOURCE_DIR)
+ cd $(BUILD_DIR); $(CMAKE) $(CMAKE_BUILD_ARGUMENTS) -DENABLE_INTERNAL_CROSSGUID=ON -DENABLE_INTERNAL_FFMPEG=OFF $(CMAKE_EXTRA_ARGUMENTS) $(CMAKE_SOURCE_DIR)
kodi:
$(MAKE) -C $(BUILD_DIR)
diff --git a/tools/depends/target/curl/CURL-VERSION b/tools/depends/target/curl/CURL-VERSION
index 10caf28174..42bdd1d64f 100644
--- a/tools/depends/target/curl/CURL-VERSION
+++ b/tools/depends/target/curl/CURL-VERSION
@@ -1,5 +1,5 @@
LIBNAME=curl
-VERSION=8.1.2
+VERSION=8.4.0
ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
-SHA512=532ab96eba6dea66d272f3be56f5af5c5da922480f9a10e203de98037c311f12f8145ba6bf813831e42815e068874ccfd108f84f7650743f5dbb3ebc3bc9c4f4
+SHA512=7027dbf3b759b39d6ec9c4da58fadd254e84bb93bff599541b3bc3135bad4c2955c6237d7ddd60973f9f1a6948bc32d7e312985fb50658bc958b9f22fee74f2b
BYPRODUCT=libcurl.a
diff --git a/tools/depends/target/curl/Makefile b/tools/depends/target/curl/Makefile
index 76ddb64e97..896a67ddf4 100644
--- a/tools/depends/target/curl/Makefile
+++ b/tools/depends/target/curl/Makefile
@@ -3,9 +3,32 @@ DEPS = ../../Makefile.include Makefile CURL-VERSION ../../download-files.include
# configuration settings
CONFIGURE=cp -f $(CONFIG_SUB) $(CONFIG_GUESS) .; \
- ./configure --prefix=$(PREFIX) --disable-shared --disable-ldap \
- --without-libssh2 --disable-ntlm-wb --enable-ipv6 --without-librtmp \
- --without-libidn2 --with-ca-fallback --with-ssl=$(PREFIX) --with-nghttp2=$(PREFIX)
+ ./configure --prefix=$(PREFIX) \
+ --disable-shared \
+ --disable-ldap \
+ --without-libssh2 \
+ --disable-ntlm-wb \
+ --enable-ipv6 \
+ --without-librtmp \
+ --without-libidn2 \
+ --with-ca-fallback \
+ --with-ssl=$(PREFIX) \
+ --with-nghttp2=$(PREFIX) \
+ --with-zlib \
+ --without-libpsl \
+ --without-zstd \
+ --without-brotli \
+ --without-gssapi \
+ --without-gsasl \
+ --without-hyper \
+ --without-ngtcp2 \
+ --without-nghttp3 \
+ --without-quiche \
+ --without-msh3 \
+ --without-gnutls \
+ --without-nss \
+ --without-mbedtls \
+ --without-wolfssl
LIBDYLIB=$(PLATFORM)/lib/.libs/$(BYPRODUCT)
diff --git a/tools/depends/target/ffmpeg/FFMPEG-VERSION b/tools/depends/target/ffmpeg/FFMPEG-VERSION
index d085978413..f2ba09402e 100644
--- a/tools/depends/target/ffmpeg/FFMPEG-VERSION
+++ b/tools/depends/target/ffmpeg/FFMPEG-VERSION
@@ -1,5 +1,5 @@
LIBNAME=ffmpeg
-VERSION=6.0
+VERSION=6.0.1
ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
-SHA512=b3214328f2792364353f38c6b46a71d4970c071d8656b20780abf0e02950167d0933eae825c09102cf8fb19fc679ac444bf1f41a448b624eaa5ea6b0f0bdf4f5
+SHA512=945e34840092dc0fd3824eb1af2be79868af2afb4fe13159b19a9bcfc464cc4d53243c13ff065199290e9393ddbf4b1c5c8abccf83a31a31d6c7490e499fd1fc
diff --git a/tools/depends/target/ffmpeg/Makefile b/tools/depends/target/ffmpeg/Makefile
index 82f86ef008..0a00827ba2 100644
--- a/tools/depends/target/ffmpeg/Makefile
+++ b/tools/depends/target/ffmpeg/Makefile
@@ -35,12 +35,6 @@ else
BASE_URL := http://mirrors.kodi.tv/build-deps/sources
endif
-ifeq ($(OS), android)
- ifeq ($(findstring arm64, $(CPU)), arm64)
- CMAKE_ARGS+= -DENABLE_NEON=YES
- endif
-endif
-
include ../../download-files.include
all: .installed-$(PLATFORM)
diff --git a/tools/depends/target/libcec/Makefile b/tools/depends/target/libcec/Makefile
deleted file mode 100644
index 884e9bf1f5..0000000000
--- a/tools/depends/target/libcec/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-include ../../Makefile.include
-DEPS = ../../Makefile.include Makefile remove_git_info.patch ../../download-files.include
-
-# lib name, version
-LIBNAME=libcec
-VERSION_MAJOR=4
-VERSION_MINOR=0
-VERSION_PATCH=7
-
-VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=424540a45f9cae3d5dcccc615d487c45033f9cdeb665b8176832495597e2cd58ef7681e13b52f6a32f8a40e1146c04a1a383f5785ea1e731c5c517a9a7843a81
-include ../../download-files.include
-
-LIBDYLIB=$(PLATFORM)/build/src/$(LIBNAME)/$(LIBNAME).so
-ifeq (darwin, $(findstring darwin, $(HOST)))
- LIBDYLIB=$(PLATFORM)/build/src/$(LIBNAME)/$(LIBNAME).dylib
-endif
-
-all: .installed-$(PLATFORM)
-
-
-$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
- rm -rf $(PLATFORM); mkdir -p $(PLATFORM)/build
- cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); patch -p1 -i ../remove_git_info.patch
- cd $(PLATFORM)/build; $(CMAKE) -DBUILD_SHARED_LIBS=1 -DSKIP_PYTHON_WRAPPER:STRING=1 -DCMAKE_INSTALL_LIBDIR=$(PREFIX)/lib -DCMAKE_PLATFORM_NO_VERSIONED_SONAME=1 ..
-
-$(LIBDYLIB): $(PLATFORM)
- $(MAKE) -C $(PLATFORM)/build
-
-.installed-$(PLATFORM): $(LIBDYLIB)
- $(MAKE) -C $(PLATFORM)/build install
-ifeq (darwin, $(findstring darwin, $(HOST)))
- install_name_tool -id $(PREFIX)/lib/$(LIBNAME).dylib $(PREFIX)/lib/$(LIBNAME).dylib
-endif
- touch $@
-
-clean:
- rm -rf $(PLATFORM) .installed-$(PLATFORM)
-
-distclean::
- rm -rf $(PLATFORM) .installed-$(PLATFORM)
-
diff --git a/tools/depends/target/libsdl/01-SDL_SetWidthHeight.patch b/tools/depends/target/libsdl/01-SDL_SetWidthHeight.patch
deleted file mode 100644
index 1ecb5a3973..0000000000
--- a/tools/depends/target/libsdl/01-SDL_SetWidthHeight.patch
+++ /dev/null
@@ -1,28 +0,0 @@
---- a/include/SDL_video.h
-+++ b/include/SDL_video.h
-@@ -323,6 +323,11 @@
- */
- extern DECLSPEC SDL_Rect ** SDLCALL SDL_ListModes(SDL_PixelFormat *format, Uint32 flags);
-
-+/**
-+* Alter the width and height of the current surface to the given sizes.
-+*/
-+extern DECLSPEC void SDLCALL SDL_SetWidthHeight(int width, int height);
-+
- /**
- * Set up a video mode with the specified width, height and bits-per-pixel.
- *
---- a/src/video/SDL_video.c
-+++ b/src/video/SDL_video.c
-@@ -1976,3 +1976,11 @@
- return(0);
- }
- }
-+
-+void SDL_SetWidthHeight(int width, int height)
-+{
-+ if (current_video != NULL && current_video->screen != NULL) {
-+ current_video->screen->w = width;
-+ current_video->screen->h = height;
-+ }
-+}
diff --git a/tools/depends/target/libsdl/02-OSX_interpretKeyEvents.patch b/tools/depends/target/libsdl/02-OSX_interpretKeyEvents.patch
deleted file mode 100644
index 0a6ef088b5..0000000000
--- a/tools/depends/target/libsdl/02-OSX_interpretKeyEvents.patch
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/src/video/quartz/SDL_QuartzEvents.m
-+++ b/src/video/quartz/SDL_QuartzEvents.m
-@@ -345,7 +345,11 @@
- the scancode/keysym.
- */
- if (SDL_TranslateUNICODE && state == SDL_PRESSED) {
-- [field_edit interpretKeyEvents:[NSArray arrayWithObject:event]];
-+ NSResponder *firstResponder = [[NSApp keyWindow] firstResponder];
-+ if ([NSStringFromClass([firstResponder class]) isEqual:@"OSXTextInputResponder"])
-+ [firstResponder interpretKeyEvents:[NSArray arrayWithObject:event]];
-+ else
-+ [field_edit interpretKeyEvents:[NSArray arrayWithObject:event]];
- chars = [ event characters ];
- numChars = [ chars length ];
- if (numChars > 0)
diff --git a/tools/depends/target/libsdl/03-mavericks-compile.patch b/tools/depends/target/libsdl/03-mavericks-compile.patch
deleted file mode 100644
index 1c00140a7f..0000000000
--- a/tools/depends/target/libsdl/03-mavericks-compile.patch
+++ /dev/null
@@ -1,12 +0,0 @@
---- a/src/video/quartz/SDL_QuartzVideo.h
-+++ b/src/video/quartz/SDL_QuartzVideo.h
-@@ -91,7 +91,9 @@
- CGDirectDisplayID display; /* 0 == main display (only support single display) */
- const void *mode; /* current mode of the display */
- const void *save_mode; /* original mode of the display */
-+#ifdef CGDirectPaletteRef
- CGDirectPaletteRef palette; /* palette of an 8-bit display */
-+#endif
- NSOpenGLContext *gl_context; /* OpenGL rendering context */
- NSGraphicsContext *nsgfx_context; /* Cocoa graphics context */
- Uint32 width, height, bpp; /* frequently used data about the display */
diff --git a/tools/depends/target/libsdl/04-fix_external_screen_crash.patch b/tools/depends/target/libsdl/04-fix_external_screen_crash.patch
deleted file mode 100644
index 82bb364b70..0000000000
--- a/tools/depends/target/libsdl/04-fix_external_screen_crash.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/src/video/quartz/SDL_QuartzWindow.m
-+++ b/src/video/quartz/SDL_QuartzWindow.m
-@@ -87,7 +87,7 @@
- SDL_VideoDevice *this = (SDL_VideoDevice*)current_video;
-
- /* make sure pixels are fully opaque */
-- if (! ( SDL_VideoSurface->flags & SDL_OPENGL ) )
-+ if ( SDL_VideoSurface && ! ( SDL_VideoSurface->flags & SDL_OPENGL ) )
- QZ_SetPortAlphaOpaque ();
-
- /* save current visible SDL surface */
diff --git a/tools/depends/target/libsdl/Makefile b/tools/depends/target/libsdl/Makefile
deleted file mode 100644
index d776b89370..0000000000
--- a/tools/depends/target/libsdl/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-include ../../Makefile.include
-DEPS = ../../Makefile.include Makefile 01-SDL_SetWidthHeight.patch 02-OSX_interpretKeyEvents.patch 03-mavericks-compile.patch 04-fix_external_screen_crash.patch ../../download-files.include
-
-# lib name, version
-LIBNAME=SDL
-VERSION=1.2.15
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=ac392d916e6953b0925a7cbb0f232affea33339ef69b47a0a7898492afb9784b93138986df53d6da6d3e2ad79af1e9482df565ecca30f89428be0ae6851b1adc
-include ../../download-files.include
-
-# configuration settings
-CONFIGURE=./configure --prefix=$(PREFIX) --disable-video-directfb
-ifneq ($(OS),linux)
-CONFIGURE += --without-x --disable-video-x11
-endif
-
-LIBDYLIB=$(PLATFORM)/build/.libs/lib$(LIBNAME).a
-
-all: .installed-$(PLATFORM)
-
-
-$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
- rm -rf $(PLATFORM); mkdir -p $(PLATFORM)
- cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); patch -p1 -i ../01-SDL_SetWidthHeight.patch
- cd $(PLATFORM); patch -p1 -i ../02-OSX_interpretKeyEvents.patch
- cd $(PLATFORM); patch -p1 -i ../03-mavericks-compile.patch
- cd $(PLATFORM); patch -p1 -i ../04-fix_external_screen_crash.patch
- cd $(PLATFORM); ./autogen.sh
- cd $(PLATFORM); $(CONFIGURE)
-
-$(LIBDYLIB): $(PLATFORM)
- $(MAKE) -j 1 -C $(PLATFORM)
-
-.installed-$(PLATFORM): $(LIBDYLIB)
- $(MAKE) -C $(PLATFORM) install
- touch $@
-
-clean:
- $(MAKE) -C $(PLATFORM) clean
- rm -f .installed-$(PLATFORM)
-
-distclean::
- rm -rf $(PLATFORM) .installed-$(PLATFORM)
diff --git a/tools/depends/target/openssl/Makefile b/tools/depends/target/openssl/Makefile
index 5f2f2c0609..1a3dceb0b7 100644
--- a/tools/depends/target/openssl/Makefile
+++ b/tools/depends/target/openssl/Makefile
@@ -1,13 +1,6 @@
-include ../../Makefile.include
-DEPS = ../../Makefile.include Makefile 001-android-getauxvalrevert.patch 16-kodi.conf ../../download-files.include
-
-# lib name, version
-LIBNAME=openssl
-VERSION=1.1.1n
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=1937796736613dcf4105a54e42ecb61f95a1cea74677156f9459aea0f2c95159359e766089632bf364ee6b0d28d661eb9957bce8fecc9d2436378d8d79e8d0a4
-include ../../download-files.include
+include ../../Makefile.include OPENSSL-VERSION ../../download-files.include
+DEPS = ../../Makefile.include Makefile OPENSSL-VERSION ../../download-files.include \
+ 001-android-getauxvalrevert.patch 16-kodi.conf
# configuration settings
ifeq ($(OS), linux)
diff --git a/tools/depends/target/openssl/OPENSSL-VERSION b/tools/depends/target/openssl/OPENSSL-VERSION
new file mode 100644
index 0000000000..1712a0c3da
--- /dev/null
+++ b/tools/depends/target/openssl/OPENSSL-VERSION
@@ -0,0 +1,4 @@
+LIBNAME=openssl
+VERSION=1.1.1w
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=b4c625fe56a4e690b57b6a011a225ad0cb3af54bd8fb67af77b5eceac55cc7191291d96a660c5b568a08a2fbf62b4612818e7cca1bb95b2b6b4fc649b0552b6d
diff --git a/tools/depends/target/p8-platform/0001-fix-c++17-support.patch b/tools/depends/target/p8-platform/001-all-fix-c++17-support.patch
index 25fbe483fe..25fbe483fe 100644
--- a/tools/depends/target/p8-platform/0001-fix-c++17-support.patch
+++ b/tools/depends/target/p8-platform/001-all-fix-c++17-support.patch
diff --git a/tools/depends/target/p8-platform/002-all-fixcmakeinstall.patch b/tools/depends/target/p8-platform/002-all-fixcmakeinstall.patch
new file mode 100644
index 0000000000..be22576415
--- /dev/null
+++ b/tools/depends/target/p8-platform/002-all-fixcmakeinstall.patch
@@ -0,0 +1,29 @@
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -41,7 +41,10 @@
+ set_target_properties(p8-platform PROPERTIES VERSION ${p8-platform_VERSION_MAJOR}.${p8-platform_VERSION_MINOR}.${p8-platform_VERSION_PATCH}
+ SOVERSION ${p8-platform_VERSION_MAJOR})
+
+-install(TARGETS p8-platform DESTINATION ${CMAKE_INSTALL_LIBDIR})
++install(TARGETS p8-platform
++ RUNTIME DESTINATION bin
++ ARCHIVE DESTINATION lib
++ LIBRARY DESTINATION lib)
+ install(FILES src/os.h DESTINATION include/p8-platform)
+ IF(WIN32)
+ INSTALL(FILES src/windows/dlfcn-win32.h
+@@ -74,12 +77,12 @@
+ IF(NOT WIN32)
+ configure_file(p8-platform.pc.in p8-platform.pc @ONLY)
+ install(FILES ${CMAKE_BINARY_DIR}/p8-platform.pc
+- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
++ DESTINATION lib/pkgconfig)
+ ENDIF(NOT WIN32)
+
+ # config mode
+ configure_file (p8-platform-config.cmake.in
+ p8-platform-config.cmake @ONLY)
+ install(FILES ${CMAKE_BINARY_DIR}/p8-platform-config.cmake
+- DESTINATION ${CMAKE_INSTALL_LIBDIR}/p8-platform)
++ DESTINATION lib/cmake/p8-platform)
+
diff --git a/tools/depends/target/p8-platform/003-all-cmake_tweakversion.patch b/tools/depends/target/p8-platform/003-all-cmake_tweakversion.patch
new file mode 100644
index 0000000000..24b9a65654
--- /dev/null
+++ b/tools/depends/target/p8-platform/003-all-cmake_tweakversion.patch
@@ -0,0 +1,21 @@
+--- a/p8-platform-config.cmake.in
++++ b/p8-platform-config.cmake.in
+@@ -9,7 +9,7 @@
+ # p8-platform_LIBRARY_DIRS - directories in which the libraries are situated
+ #
+ # propagate these properties from one build system to the other
+-set (p8-platform_VERSION "@p8-platform_VERSION_MAJOR@.@p8-platform_VERSION_MINOR@")
++set (p8-platform_VERSION "@p8-platform_VERSION_MAJOR@.@p8-platform_VERSION_MINOR@.@p8-platform_VERSION_PATCH@.@p8-platform_VERSION_TWEAK@")
+ set (p8-platform_INCLUDE_DIRS @p8-platform_INCLUDE_DIRS@ @CMAKE_INSTALL_PREFIX@/include)
+ set (p8-platform_LIBRARY_DIRS "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@")
+ set (p8-platform_LINKER_FLAGS "@p8-platform_LINKER_FLAGS@")
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -14,6 +14,7 @@
+ set(p8-platform_VERSION_MAJOR 2)
+ set(p8-platform_VERSION_MINOR 1)
+ set(p8-platform_VERSION_PATCH 0)
++set(p8-platform_VERSION_TWEAK 1)
+
+ set(CMAKE_POSITION_INDEPENDENT_CODE on)
+
diff --git a/tools/depends/target/p8-platform/Makefile b/tools/depends/target/p8-platform/Makefile
index 649c638b9a..4d3fd00cb4 100644
--- a/tools/depends/target/p8-platform/Makefile
+++ b/tools/depends/target/p8-platform/Makefile
@@ -1,15 +1,10 @@
-include ../../Makefile.include
-DEPS = ../../Makefile.include Makefile 0001-fix-c++17-support.patch ../../download-files.include
+include ../../Makefile.include P8-PLATFORM-VERSION ../../download-files.include
+DEPS = ../../Makefile.include Makefile P8-PLATFORM-VERSION ../../download-files.include \
+ 001-all-fix-c++17-support.patch \
+ 002-all-fixcmakeinstall.patch \
+ 003-all-cmake_tweakversion.patch
-# lib name, version
-LIBNAME=p8-platform
-VERSION=2.1.0.1
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=10f8e3ca8ea7a48923a4cc57d47015b56b4bdbf78997ba77abd9fc2f929198fda16dfb869ba69eec393ab4b635be916c3ed9a07d6989bf8265aa055794e84bf7
-include ../../download-files.include
-
-LIBDYLIB=$(PLATFORM)/build/lib$(LIBNAME).a
+LIBDYLIB=$(PLATFORM)/build/$(BYPRODUCT)
all: .installed-$(PLATFORM)
@@ -17,8 +12,10 @@ all: .installed-$(PLATFORM)
$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
rm -rf $(PLATFORM); mkdir -p $(PLATFORM)/build
cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); patch -p1 -i ../0001-fix-c++17-support.patch
- cd $(PLATFORM)/build; $(CMAKE) -DBUILD_SHARED_LIBS=0 -DCMAKE_INSTALL_LIBDIR=$(PREFIX)/lib ..
+ cd $(PLATFORM); patch -p1 -i ../001-all-fix-c++17-support.patch
+ cd $(PLATFORM); patch -p1 -i ../002-all-fixcmakeinstall.patch
+ cd $(PLATFORM); patch -p1 -i ../003-all-cmake_tweakversion.patch
+ cd $(PLATFORM)/build; $(CMAKE) -DBUILD_SHARED_LIBS=0 ..
$(LIBDYLIB): $(PLATFORM)
$(MAKE) -C $(PLATFORM)/build
diff --git a/tools/depends/target/p8-platform/P8-PLATFORM-VERSION b/tools/depends/target/p8-platform/P8-PLATFORM-VERSION
new file mode 100644
index 0000000000..f9ac55968e
--- /dev/null
+++ b/tools/depends/target/p8-platform/P8-PLATFORM-VERSION
@@ -0,0 +1,6 @@
+LIBNAME=p8-platform
+VERSION=2.1.0.1
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=10f8e3ca8ea7a48923a4cc57d47015b56b4bdbf78997ba77abd9fc2f929198fda16dfb869ba69eec393ab4b635be916c3ed9a07d6989bf8265aa055794e84bf7
+BYPRODUCT=libp8-platform.a
+BYPRODUCT_WIN=p8-platform.lib
diff --git a/tools/depends/target/pcre/004-win-pdb.patch b/tools/depends/target/pcre/004-win-pdb.patch
index 2815ca2559..7c1fb26c74 100644
--- a/tools/depends/target/pcre/004-win-pdb.patch
+++ b/tools/depends/target/pcre/004-win-pdb.patch
@@ -1,20 +1,18 @@
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
-@@ -969,12 +969,14 @@
+@@ -969,12 +969,12 @@
PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
IF(MSVC AND INSTALL_MSVC_PDB)
- INSTALL(FILES ${PROJECT_BINARY_DIR}/pcre.pdb
- ${PROJECT_BINARY_DIR}/pcreposix.pdb
+ INSTALL(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/pcre.pdb
-+ ${PROJECT_BINARY_DIR}/$<CONFIG>/pcrecpp.pdb
+ ${PROJECT_BINARY_DIR}/$<CONFIG>/pcreposix.pdb
DESTINATION bin
CONFIGURATIONS RelWithDebInfo)
- INSTALL(FILES ${PROJECT_BINARY_DIR}/pcred.pdb
- ${PROJECT_BINARY_DIR}/pcreposixd.pdb
+ INSTALL(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/pcred.pdb
-+ ${PROJECT_BINARY_DIR}/$<CONFIG>/pcrecppd.pdb
+ ${PROJECT_BINARY_DIR}/$<CONFIG>/pcreposixd.pdb
DESTINATION bin
CONFIGURATIONS Debug)
diff --git a/tools/depends/target/python3/PYTHON3-VERSION b/tools/depends/target/python3/PYTHON3-VERSION
index 45eb16c7df..c5e1760fb2 100644
--- a/tools/depends/target/python3/PYTHON3-VERSION
+++ b/tools/depends/target/python3/PYTHON3-VERSION
@@ -1,4 +1,4 @@
LIBNAME=Python
-VERSION=3.11.5
+VERSION=3.11.7
ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
-SHA512=93fa640bedcea449060caac8aa691aa315a19f172fd9f0422183d17749c3512d4ecac60e7599f9ef14e3cdb3c8b4b060e484c9061b1e7ee8d958200d6041e408
+SHA512=11e06f2ffe1f66888cb5b4e9f607de815294d6863a77eda6ec6d7c724ef158df9f51881f4a956d4a6fa973c2fb6fd031d495e3496e9b0bb53793fb1cc8434c63
diff --git a/tools/depends/target/pythonmodule-pil/Makefile b/tools/depends/target/pythonmodule-pil/Makefile
index 4037675e0e..c1384e4fb4 100644
--- a/tools/depends/target/pythonmodule-pil/Makefile
+++ b/tools/depends/target/pythonmodule-pil/Makefile
@@ -58,6 +58,7 @@ BUILD_OPTS=--plat-name $(OS)-$(CPU) --disable-jpeg2000 --disable-webp --disable-
export CC CFLAGS
export PYTHONXINCLUDE=$(PREFIX)/include/python$(PYTHON_VERSION)
export LDSHARED LDFLAGS PYTHONPATH
+export SETUPTOOLS_EXT_SUFFIX=.so
all: .installed-$(PLATFORM)
@@ -89,9 +90,6 @@ else ifeq ($(OS),darwin_embedded)
cd $(PILPATH) && rm -rf Pillow-*.egg
else ifeq ($(TARGET_PLATFORM),webos)
cd $(PILPATHLIB) && unzip -o $(PILPATH)/Pillow-*.egg && rm -f $(PILPATH)/Pillow-*.egg
- cd $(PILPATHLIB)/PIL && \
- sed $(SED_FLAG) -e 's/import sys, pkg_resources,/import sys, /' \
- -re "/__file__/ s|=.*(_imaging.*.so).*$$|= 'lib/python3/PIL/\1'|" _imaging*.py
endif
touch $@
diff --git a/tools/depends/target/pythonmodule-pycryptodome/Makefile b/tools/depends/target/pythonmodule-pycryptodome/Makefile
index 3916b58d1a..48b61ae574 100644
--- a/tools/depends/target/pythonmodule-pycryptodome/Makefile
+++ b/tools/depends/target/pythonmodule-pycryptodome/Makefile
@@ -24,6 +24,7 @@ endif
export CC CFLAGS
export LDSHARED LDFLAGS
export PYTHONPATH=$(PYTHON_SITE_PKG)
+export SETUPTOOLS_EXT_SUFFIX=.so
all: .installed-$(PLATFORM)
diff --git a/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch b/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch
new file mode 100644
index 0000000000..937d19e8e3
--- /dev/null
+++ b/tools/depends/target/rapidjson/002-cmake-standardise_config_installpath.patch
@@ -0,0 +1,15 @@
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -94,11 +94,7 @@
+ SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "Directory where lib will install")
+ SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}" CACHE PATH "Path to the documentation")
+
+-IF(UNIX OR CYGWIN)
+- SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}")
+-ELSEIF(WIN32)
+- SET(_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake")
+-ENDIF()
++SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}")
+ SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake fiels are installed in")
+
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/tools/depends/target/rapidjson/002-cmake-removedocs-examples.patch b/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch
index 4bf06da59c..4bf06da59c 100644
--- a/tools/depends/target/rapidjson/002-cmake-removedocs-examples.patch
+++ b/tools/depends/target/rapidjson/003-cmake-removedocs-examples.patch
diff --git a/tools/depends/target/rapidjson/003-win-arm64.patch b/tools/depends/target/rapidjson/004-win-arm64.patch
index 2d045dcaf6..2d045dcaf6 100644
--- a/tools/depends/target/rapidjson/003-win-arm64.patch
+++ b/tools/depends/target/rapidjson/004-win-arm64.patch
diff --git a/tools/depends/target/rapidjson/Makefile b/tools/depends/target/rapidjson/Makefile
index 233d3a3571..1e0a2d2e75 100644
--- a/tools/depends/target/rapidjson/Makefile
+++ b/tools/depends/target/rapidjson/Makefile
@@ -1,8 +1,9 @@
-include ../../Makefile.include
include RAPIDJSON-VERSION
DEPS = Makefile RAPIDJSON-VERSION 001-remove_custom_cxx_flags.patch ../../download-files.include \
- 002-cmake-removedocs-examples.patch \
- 003-win-arm64.patch
+ 002-cmake-standardise_config_installpath.patch \
+ 003-cmake-removedocs-examples.patch \
+ 004-win-arm64.patch
CMAKE_OPTIONS=-DRAPIDJSON_HAS_STDSTRING=ON -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF -DRAPIDJSON_BUILD_THIRDPARTY_GTEST=OFF
@@ -42,8 +43,9 @@ endif
cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
cd $(PLATFORM); rm -rf build; mkdir -p build
cd $(PLATFORM); patch -p1 -i ../001-remove_custom_cxx_flags.patch
- cd $(PLATFORM); patch -p1 -i ../002-cmake-removedocs-examples.patch
- cd $(PLATFORM); patch -p1 -i ../003-win-arm64.patch
+ cd $(PLATFORM); patch -p1 -i ../002-cmake-standardise_config_installpath.patch
+ cd $(PLATFORM); patch -p1 -i ../003-cmake-removedocs-examples.patch
+ cd $(PLATFORM); patch -p1 -i ../004-win-arm64.patch
cd $(PLATFORM)/build; $(CMAKE) $(CMAKE_OPTIONS) ..
.installed-$(PLATFORM): $(PLATFORM)
diff --git a/tools/depends/target/smctemp/Makefile b/tools/depends/target/smctemp/Makefile
new file mode 100644
index 0000000000..b257625403
--- /dev/null
+++ b/tools/depends/target/smctemp/Makefile
@@ -0,0 +1,30 @@
+include ../../Makefile.include SMCTEMP-VERSION ../../download-files.include
+DEPS = ../../Makefile.include SMCTEMP-VERSION Makefile ../../download-files.include \
+
+LIBDYLIB=$(PLATFORM)/$(BYPRODUCT)
+
+ifeq ($(CPU), arm64)
+ CXXFLAGS += -DARCH_TYPE_ARM64
+else
+ CXXFLAGS += -DARCH_TYPE_X86_64
+endif
+
+all: .installed-$(PLATFORM)
+
+$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
+ rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)
+ cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
+
+$(LIBDYLIB): $(PLATFORM)
+ $(MAKE) CXX="$(CXX)" CXXFLAGS="$(CXXFLAGS)" AR=$(AR) RANLIB=$(RANLIB) -C $(PLATFORM) staticlib
+
+.installed-$(PLATFORM): $(LIBDYLIB)
+ $(MAKE) DEST_PREFIX=$(PREFIX) -C $(PLATFORM) installstaticlib
+ touch $@
+
+clean:
+ $(MAKE) -C $(PLATFORM) clean
+ rm -f .installed-$(PLATFORM)
+
+distclean::
+ rm -rf $(PLATFORM) .installed-$(PLATFORM)
diff --git a/tools/depends/target/smctemp/SMCTEMP-VERSION b/tools/depends/target/smctemp/SMCTEMP-VERSION
new file mode 100644
index 0000000000..4eb38567cb
--- /dev/null
+++ b/tools/depends/target/smctemp/SMCTEMP-VERSION
@@ -0,0 +1,5 @@
+LIBNAME=smctemp
+VERSION=0.2.1
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=8671836ed3f16122ffc84a1e91b463c8405526b1500fa9f5816a9f9eff1fd598c86958e894d2b8b25ae798da57b536766729b28dcc6b7c69fe2dc3c818f18290
+BYPRODUCT=libsmctemp.a
diff --git a/tools/depends/target/wayland-protocols/Makefile b/tools/depends/target/wayland-protocols/Makefile
index 0364867fdf..e3cc1b62ca 100644
--- a/tools/depends/target/wayland-protocols/Makefile
+++ b/tools/depends/target/wayland-protocols/Makefile
@@ -3,10 +3,10 @@ DEPS =Makefile ../../download-files.include
# lib name, version
LIBNAME=wayland-protocols
-VERSION=1.24
+VERSION=1.32
SOURCE=$(LIBNAME)-$(VERSION)
ARCHIVE=$(SOURCE).tar.xz
-SHA512=4b1122517db56f48a5fafd4bd0cb7f94faef6fdd2d80e6cec17e5a6bafbaf2f5a71b958ed12e6d13965494885c209b2fb6dd8331487b39c6f251e71f1e770a15
+SHA512=90bbd52daf342b98823ddeed04e349ae242d2eaf925ab8d603cceb36c980c83b5681bb890961e0d49584cb5c2e60a33abf8821770c6ab87956383630bd5b7966
ifeq ($(PLATFORM),)
# Building stand-alone
diff --git a/tools/depends/target/wayland/Makefile b/tools/depends/target/wayland/Makefile
index c079d9c1d6..0a0f616125 100644
--- a/tools/depends/target/wayland/Makefile
+++ b/tools/depends/target/wayland/Makefile
@@ -1,16 +1,33 @@
-include ../../Makefile.include
-DEPS =../../Makefile.include Makefile ../../download-files.include
+include ../../Makefile.include WAYLAND-VERSION ../../download-files.include
+DEPS =../../Makefile.include Makefile WAYLAND-VERSION ../../download-files.include
-# lib name, version
-LIBNAME=wayland
-VERSION=1.18.0
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.xz
-SHA512=e30199e30c2bbd361ee695b4f3f7a4e264f10ed8f46f2c90762b5739fc578ae757dc39aa0258d8fbf0ed418553470bccd4b2730ed9705481cfccdab5de96a8fc
-include ../../download-files.include
+MESON_BUILD_TYPE=release
+
+ifeq ($(DEBUG_BUILD), yes)
+ MESON_BUILD_TYPE=debug
+endif
# configuration settings
-CONFIGURE=./configure --prefix=$(PREFIX) --with-host-scanner --disable-documentation --disable-dtd-validation
+CONFIGURE = $(NATIVEPREFIX)/bin/python3 $(NATIVEPREFIX)/bin/meson setup \
+ --prefix $(PREFIX) \
+ --libdir $(PREFIX)/lib \
+ --buildtype=$(MESON_BUILD_TYPE) \
+ -Dscanner=false \
+ -Dtests=false \
+ -Ddocumentation=false \
+ -Ddtd_validation=false
+
+ifeq ($(CROSS_COMPILING), yes)
+ CONFIGURE += --cross-file $(PREFIX)/share/cross-file.meson
+endif
+
+export CC
+export CXX
+export CFLAGS
+export CXXFLAGS
+export LDFLAGS
+
+export PKG_CONFIG_LIBDIR=$(PREFIX)/lib/pkgconfig
all: .installed-$(PLATFORM)
@@ -18,14 +35,17 @@ all: .installed-$(PLATFORM)
$(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE)
rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)
cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
- cd $(PLATFORM); $(CONFIGURE)
+ cd $(PLATFORM); rm -rf build; mkdir -p build
+ # symlink native wayland scanner pkg-config to allow meson to find
+ rm -f $(PREFIX)/lib/pkgconfig/wayland-scanner.pc
+ ln -sf $(NATIVEPREFIX)/lib/pkgconfig/wayland-scanner.pc $(PREFIX)/lib/pkgconfig/wayland-scanner.pc
+ cd $(PLATFORM); $(CONFIGURE) . build
.installed-$(PLATFORM): $(PLATFORM)
- $(MAKE) -C $(PLATFORM)
- $(MAKE) -C $(PLATFORM) install
+ cd $(PLATFORM)/build; $(NATIVEPREFIX)/bin/ninja -v
+ cd $(PLATFORM)/build; $(NATIVEPREFIX)/bin/ninja -v install
- # remove the target wayland scanner from the sysroot. We only want to use the native one
- rm -f $(PREFIX)/bin/wayland-scanner
+ # symlink native wayland scanner
rm -f $(PREFIX)/lib/pkgconfig/wayland-scanner.pc
ln -sf $(NATIVEPREFIX)/lib/pkgconfig/wayland-scanner.pc $(PREFIX)/lib/pkgconfig/wayland-scanner.pc
touch $@
diff --git a/tools/depends/target/wayland/WAYLAND-VERSION b/tools/depends/target/wayland/WAYLAND-VERSION
new file mode 100644
index 0000000000..580ede5ca1
--- /dev/null
+++ b/tools/depends/target/wayland/WAYLAND-VERSION
@@ -0,0 +1,4 @@
+LIBNAME=wayland
+VERSION=1.22.0
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.xz
+SHA512=fb1974efc8433e97254eb83fe28974198f2b4d8246418eb3d34ce657055461e0c97bc06dd52e5066ae91bbe05bac611dc49a0937ba226ac6388d5a47241efb12
diff --git a/tools/depends/target/waylandpp/Makefile b/tools/depends/target/waylandpp/Makefile
index 4631a4668e..6a4689e6f8 100644
--- a/tools/depends/target/waylandpp/Makefile
+++ b/tools/depends/target/waylandpp/Makefile
@@ -1,12 +1,6 @@
-include ../../Makefile.include
-DEPS =Makefile ../../download-files.include 001-fix-gcc13-build.patch
-
-# lib name, version
-LIBNAME=waylandpp
-VERSION=0.2.8
-SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tar.gz
-SHA512=bf1b8a9e69b87547fc65989b9eaff88a442d8b2f01f5446cef960000b093390b1e557536837fbf38bb6d9a4f93e3985ea34c3253f94925b0f571b4606c980832
+include WAYLANDPP-VERSION ../../download-files.include
+DEPS =Makefile ../../download-files.include WAYLANDPP-VERSION 001-fix-gcc13-build.patch
LIBDYLIB=$(PLATFORM)/build/libwayland-client++.so
@@ -31,7 +25,6 @@ endif
CMAKE_OPTIONS := -DBUILD_DOCUMENTATION=OFF -DBUILD_LIBRARIES=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_EXAMPLES=OFF $(CMAKE_OPTIONS)
BUILDDIR = $(PLATFORM)/build
-include ../../download-files.include
all: .installed-$(PLATFORM)
diff --git a/tools/depends/target/waylandpp/WAYLANDPP-VERSION b/tools/depends/target/waylandpp/WAYLANDPP-VERSION
new file mode 100644
index 0000000000..b87fb9bbe7
--- /dev/null
+++ b/tools/depends/target/waylandpp/WAYLANDPP-VERSION
@@ -0,0 +1,4 @@
+LIBNAME=waylandpp
+VERSION=1.0.0
+ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+SHA512=64b59d073a0593ecf442362eb63ec0a9dfeaa1ad1d56b5955cb0c159fd01dc45e012b926811c6ca0dc12d4bb2e640eabc2e778ab7d28de2098eb694d26f01039
diff --git a/tools/webOS/packaging/appinfo.json.in b/tools/webOS/packaging/appinfo.json.in
index 300a52be6b..72c7629403 100644
--- a/tools/webOS/packaging/appinfo.json.in
+++ b/tools/webOS/packaging/appinfo.json.in
@@ -7,8 +7,9 @@
"title": "@APP_NAME@",
"icon": "icon.png",
"largeIcon": "largeIcon.png",
- "splashBackground": "media/splash_webOS.png",
+ "splashBackground": "media/applaunch_screen.png",
"spinnerOnLaunch": false,
"handlesRelaunch": true,
- "nativeLifeCycleInterfaceVersion": 2
+ "nativeLifeCycleInterfaceVersion": 2,
+ "requiredMemory": 500
}
diff --git a/version.txt b/version.txt
index d68e225fbf..51ccc791e8 100644
--- a/version.txt
+++ b/version.txt
@@ -8,9 +8,9 @@ COPYRIGHT_YEARS 2005-2021
WEBSITE http://kodi.tv
VERSION_MAJOR 21
VERSION_MINOR 0
-VERSION_TAG ALPHA3
-VERSION_CODE 20.90.301
-ADDON_API 20.90.301
+VERSION_TAG BETA1
+VERSION_CODE 20.90.801
+ADDON_API 20.90.801
ADDON_REPOS repository.xbmc.org|https://mirrors.kodi.tv
APP_PACKAGE org.xbmc.kodi
PACKAGE_IDENTITY XBMCFoundation.Kodi
diff --git a/xbmc/CompileInfo.cpp.in b/xbmc/CompileInfo.cpp.in
index 6fb14de995..30571fd881 100644
--- a/xbmc/CompileInfo.cpp.in
+++ b/xbmc/CompileInfo.cpp.in
@@ -111,6 +111,11 @@ std::vector<std::string> CCompileInfo::GetAvailableAudioBackends()
return StringUtils::Split("@AUDIO_BACKENDS@", ' ');
}
+std::vector<std::string> CCompileInfo::GetAvailableGlInterfaces()
+{
+ return StringUtils::Split("@GL_INTERFACES@", ' ');
+}
+
// Return version of python built against as format MAJOR.MINOR
std::string CCompileInfo::GetPythonVersion()
{
diff --git a/xbmc/CompileInfo.h b/xbmc/CompileInfo.h
index 32542b1edb..5556b17d25 100644
--- a/xbmc/CompileInfo.h
+++ b/xbmc/CompileInfo.h
@@ -32,6 +32,7 @@ public:
static const char* GetVersionCode();
static std::vector<std::string> GetAvailableWindowSystems();
static std::vector<std::string> GetAvailableAudioBackends();
+ static std::vector<std::string> GetAvailableGlInterfaces();
static std::vector<ADDON::RepoInfo> LoadOfficialRepoInfos();
static std::string GetPythonVersion();
static std::vector<std::string> GetWebserverExtraWhitelist();
diff --git a/xbmc/ContextMenuManager.cpp b/xbmc/ContextMenuManager.cpp
index 75fa2bb514..7aed939162 100644
--- a/xbmc/ContextMenuManager.cpp
+++ b/xbmc/ContextMenuManager.cpp
@@ -28,6 +28,7 @@
#include "utils/log.h"
#include "video/ContextMenus.h"
+#include <algorithm>
#include <iterator>
#include <mutex>
@@ -61,13 +62,16 @@ void CContextMenuManager::Init()
std::unique_lock<CCriticalSection> lock(m_criticalSection);
m_items = {
std::make_shared<CONTEXTMENU::CVideoBrowse>(),
+ std::make_shared<CONTEXTMENU::CVideoChooseVersion>(),
std::make_shared<CONTEXTMENU::CVideoResume>(),
std::make_shared<CONTEXTMENU::CVideoPlay>(),
+ std::make_shared<CONTEXTMENU::CVideoPlayUsing>(),
std::make_shared<CONTEXTMENU::CVideoPlayAndQueue>(),
std::make_shared<CONTEXTMENU::CVideoPlayNext>(),
std::make_shared<CONTEXTMENU::CVideoQueue>(),
std::make_shared<CONTEXTMENU::CMusicBrowse>(),
std::make_shared<CONTEXTMENU::CMusicPlay>(),
+ std::make_shared<CONTEXTMENU::CMusicPlayUsing>(),
std::make_shared<CONTEXTMENU::CMusicPlayNext>(),
std::make_shared<CONTEXTMENU::CMusicQueue>(),
std::make_shared<CONTEXTMENU::CAddonInfo>(),
@@ -89,12 +93,17 @@ void CContextMenuManager::Init()
std::make_shared<CONTEXTMENU::CVideoRemoveResumePoint>(),
std::make_shared<CONTEXTMENU::CEjectDisk>(),
std::make_shared<CONTEXTMENU::CEjectDrive>(),
+ std::make_shared<CONTEXTMENU::CFavouritesTargetBrowse>(),
+ std::make_shared<CONTEXTMENU::CFavouritesTargetResume>(),
+ std::make_shared<CONTEXTMENU::CFavouritesTargetPlay>(),
+ std::make_shared<CONTEXTMENU::CFavouritesTargetInfo>(),
std::make_shared<CONTEXTMENU::CMoveUpFavourite>(),
std::make_shared<CONTEXTMENU::CMoveDownFavourite>(),
std::make_shared<CONTEXTMENU::CChooseThumbnailForFavourite>(),
std::make_shared<CONTEXTMENU::CRenameFavourite>(),
std::make_shared<CONTEXTMENU::CRemoveFavourite>(),
std::make_shared<CONTEXTMENU::CAddRemoveFavourite>(),
+ std::make_shared<CONTEXTMENU::CFavouritesTargetContextMenu>(),
};
ReloadAddonItems();
@@ -200,11 +209,26 @@ bool CContextMenuManager::IsVisible(
return menuItem.IsVisible(fileItem);
}
-ContextMenuView CContextMenuManager::GetItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const
+bool CContextMenuManager::HasItems(const CFileItem& fileItem, const CContextMenuItem& root) const
+{
+ //! @todo implement group support
+ if (&root == &CContextMenuManager::MAIN)
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ return std::any_of(m_items.cbegin(), m_items.cend(),
+ [&fileItem](const std::shared_ptr<const IContextMenuItem>& menu) {
+ return menu->IsVisible(fileItem);
+ });
+ }
+ return false;
+}
+
+ContextMenuView CContextMenuManager::GetItems(const CFileItem& fileItem,
+ const CContextMenuItem& root) const
{
ContextMenuView result;
//! @todo implement group support
- if (&root == &MAIN)
+ if (&root == &CContextMenuManager::MAIN)
{
std::unique_lock<CCriticalSection> lock(m_criticalSection);
std::copy_if(m_items.begin(), m_items.end(), std::back_inserter(result),
@@ -213,7 +237,18 @@ ContextMenuView CContextMenuManager::GetItems(const CFileItem& fileItem, const C
return result;
}
-ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const
+bool CContextMenuManager::HasAddonItems(const CFileItem& fileItem,
+ const CContextMenuItem& root) const
+{
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ return std::any_of(m_addonItems.cbegin(), m_addonItems.cend(),
+ [this, root, &fileItem](const CContextMenuItem& menu) {
+ return IsVisible(menu, root, fileItem);
+ });
+}
+
+ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem,
+ const CContextMenuItem& root) const
{
ContextMenuView result;
{
@@ -223,7 +258,7 @@ ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem, co
result.emplace_back(new CContextMenuItem(menu));
}
- if (&root == &MANAGE)
+ if (&root == &CContextMenuManager::MANAGE)
{
std::sort(result.begin(), result.end(),
[&](const ContextMenuView::value_type& lhs, const ContextMenuView::value_type& rhs)
@@ -235,6 +270,20 @@ ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem, co
return result;
}
+bool CONTEXTMENU::HasAnyMenuItemsFor(const std::shared_ptr<CFileItem>& fileItem,
+ const CContextMenuItem& root)
+{
+ if (!fileItem)
+ return false;
+
+ if (fileItem->HasProperty("contextmenulabel(0)"))
+ return true;
+
+ const CContextMenuManager& contextMenuManager = CServiceBroker::GetContextMenuManager();
+ return (contextMenuManager.HasItems(*fileItem, root) ||
+ contextMenuManager.HasAddonItems(*fileItem, root));
+}
+
bool CONTEXTMENU::ShowFor(const std::shared_ptr<CFileItem>& fileItem, const CContextMenuItem& root)
{
if (!fileItem)
diff --git a/xbmc/ContextMenuManager.h b/xbmc/ContextMenuManager.h
index 13d92bdf20..c5f00d50b9 100644
--- a/xbmc/ContextMenuManager.h
+++ b/xbmc/ContextMenuManager.h
@@ -40,9 +40,35 @@ public:
void Init();
void Deinit();
- ContextMenuView GetItems(const CFileItem& item, const CContextMenuItem& root = MAIN) const;
+ /*! \brief Checks whether context menu items are available for a file item.
+ \param fileItem the file item
+ \param root the root context menu item
+ \return true if any items are present, false otherwise
+ */
+ bool HasItems(const CFileItem& fileItem, const CContextMenuItem& root) const;
+
+ /*! \brief Gets the context menu items available for a file item.
+ \param fileItem the file item
+ \param root the root context menu item
+ \return the items
+ \sa ContextMenuView
+ */
+ ContextMenuView GetItems(const CFileItem& fileItem, const CContextMenuItem& root) const;
- ContextMenuView GetAddonItems(const CFileItem& item, const CContextMenuItem& root = MAIN) const;
+ /*! \brief Checks whether addon context menu items are available for a file item.
+ \param fileItem the file item
+ \param root the root context menu item
+ \return true if any items are present, false otherwise
+ */
+ bool HasAddonItems(const CFileItem& fileItem, const CContextMenuItem& root) const;
+
+ /*! \brief Gets the addon context menu items available for a file item.
+ \param fileItem the file item
+ \param root the root context menu item
+ \return the items
+ \sa ContextMenuView
+ */
+ ContextMenuView GetAddonItems(const CFileItem& fileItem, const CContextMenuItem& root) const;
private:
CContextMenuManager(const CContextMenuManager&) = delete;
@@ -67,14 +93,24 @@ private:
namespace CONTEXTMENU
{
- /*!
- * Starts the context menu loop for a file item.
- * */
-bool ShowFor(const std::shared_ptr<CFileItem>& fileItem,
- const CContextMenuItem& root = CContextMenuManager::MAIN);
-
-/*!
- * Shortcut for continuing the context menu loop from an existing menu item.
- */
+/*! \brief Checks whether any context menu items are available for a file item.
+ \param fileItem the file item
+ \param root the root context menu item
+ \return true if any items are present, false otherwise
+ */
+bool HasAnyMenuItemsFor(const std::shared_ptr<CFileItem>& fileItem, const CContextMenuItem& root);
+
+/*! \brief Starts the context menu loop for a file item.
+ \param fileItem the file item
+ \param root the root context menu item
+ \return true on success, false otherwise
+ */
+bool ShowFor(const std::shared_ptr<CFileItem>& fileItem, const CContextMenuItem& root);
+
+/*! \brief Shortcut for continuing the context menu loop from an existing menu item.
+ \param menu the menu item
+ \param fileItem the file item
+ \return true on success, false otherwise
+ */
bool LoopFrom(const IContextMenuItem& menu, const std::shared_ptr<CFileItem>& fileItem);
}
diff --git a/xbmc/ContextMenus.cpp b/xbmc/ContextMenus.cpp
index 0f6534cfb6..c5e7c6fe62 100644
--- a/xbmc/ContextMenus.cpp
+++ b/xbmc/ContextMenus.cpp
@@ -78,10 +78,14 @@ std::string CAddRemoveFavourite::GetLabel(const CFileItem& item) const
bool CAddRemoveFavourite::IsVisible(const CFileItem& item) const
{
+ if (item.GetProperty("hide_add_remove_favourite").asBoolean())
+ return false;
+
return (!item.GetPath().empty() && !item.IsParentFolder() && !item.IsPath("add") &&
!item.IsPath("newplaylist://") && !URIUtils::IsProtocol(item.GetPath(), "favourites") &&
!URIUtils::IsProtocol(item.GetPath(), "newsmartplaylist") &&
!URIUtils::IsProtocol(item.GetPath(), "newtag") &&
+ !URIUtils::IsProtocol(item.GetPath(), "newvideoversion") &&
!URIUtils::IsProtocol(item.GetPath(), "musicsearch") &&
// Hide this item for all PVR EPG/timers/search except EPG/timer/timer rules/search root
// folders.
diff --git a/xbmc/DatabaseManager.cpp b/xbmc/DatabaseManager.cpp
index e4388648a7..38250b4da6 100644
--- a/xbmc/DatabaseManager.cpp
+++ b/xbmc/DatabaseManager.cpp
@@ -230,3 +230,17 @@ void CDatabaseManager::UpdateStatus(const std::string &name, DB_STATUS status)
std::unique_lock<CCriticalSection> lock(m_section);
m_dbStatus[name] = status;
}
+
+void CDatabaseManager::LocalizationChanged()
+{
+ std::unique_lock<CCriticalSection> lock(m_section);
+
+ // update video version type table after language changed
+ CVideoDatabase videodb;
+ if (videodb.Open())
+ {
+ videodb.UpdateVideoVersionTypeTable();
+ CLog::Log(LOGDEBUG, "{}, Video version type table updated for new language settings",
+ __FUNCTION__);
+ }
+}
diff --git a/xbmc/DatabaseManager.h b/xbmc/DatabaseManager.h
index 0fb10d2448..258355ffd9 100644
--- a/xbmc/DatabaseManager.h
+++ b/xbmc/DatabaseManager.h
@@ -51,6 +51,8 @@ public:
bool IsUpgrading() const { return m_bIsUpgrading; }
+ void LocalizationChanged();
+
private:
std::atomic<bool> m_bIsUpgrading;
diff --git a/xbmc/DbUrl.h b/xbmc/DbUrl.h
index d307d7f427..bb9be82608 100644
--- a/xbmc/DbUrl.h
+++ b/xbmc/DbUrl.h
@@ -30,9 +30,10 @@ public:
const std::string& GetType() const { return m_type; }
void AppendPath(const std::string &subPath);
- using CUrlOptions::HasOption;
+ using CUrlOptions::GetOption;
using CUrlOptions::GetOptions;
using CUrlOptions::GetOptionsString;
+ using CUrlOptions::HasOption;
void AddOption(const std::string &key, const char *value) override;
void AddOption(const std::string &key, const std::string &value) override;
diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp
index 5bb70b5779..171938dc95 100644
--- a/xbmc/FileItem.cpp
+++ b/xbmc/FileItem.cpp
@@ -31,6 +31,7 @@
#include "music/tags/MusicInfoTag.h"
#include "music/tags/MusicInfoTagLoaderFactory.h"
#include "pictures/PictureInfoTag.h"
+#include "playlists/PlayList.h"
#include "playlists/PlayListFactory.h"
#include "pvr/PVRManager.h"
#include "pvr/channels/PVRChannel.h"
@@ -38,6 +39,7 @@
#include "pvr/epg/EpgInfoTag.h"
#include "pvr/epg/EpgSearchFilter.h"
#include "pvr/guilib/PVRGUIActionsChannels.h"
+#include "pvr/guilib/PVRGUIActionsUtils.h"
#include "pvr/recordings/PVRRecording.h"
#include "pvr/timers/PVRTimerInfoTag.h"
#include "settings/AdvancedSettings.h"
@@ -61,6 +63,7 @@
#include <algorithm>
#include <cstdlib>
+#include <memory>
#include <mutex>
using namespace KODI;
@@ -120,19 +123,20 @@ CFileItem::CFileItem(const CVideoInfoTag& 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();
- }
+std::string GetEpgTagTitle(const std::shared_ptr<const 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 std::shared_ptr<CPVREpgInfoTag>& tag)
+void CFileItem::FillMusicInfoTag(const std::shared_ptr<const CPVREpgInfoTag>& tag)
{
CMusicInfoTag* musictag = GetMusicInfoTag(); // create (!) the music tag.
@@ -213,7 +217,7 @@ CFileItem::CFileItem(const std::shared_ptr<CPVRChannelGroupMember>& channelGroup
{
Initialize();
- const std::shared_ptr<CPVRChannel> channel = channelGroupMember->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = channelGroupMember->Channel();
m_pvrChannelGroupMemberInfoTag = channelGroupMember;
@@ -238,7 +242,7 @@ CFileItem::CFileItem(const std::shared_ptr<CPVRChannelGroupMember>& channelGroup
if (channel->IsRadio() && !HasMusicInfoTag())
{
- const std::shared_ptr<CPVREpgInfoTag> epgNow = channel->GetEPGNow();
+ const std::shared_ptr<const CPVREpgInfoTag> epgNow = channel->GetEPGNow();
FillMusicInfoTag(epgNow);
}
FillInMimeType(false);
@@ -261,7 +265,7 @@ CFileItem::CFileItem(const std::shared_ptr<CPVRRecording>& record)
SetArt("icon", record->IconPath());
else
{
- const std::shared_ptr<CPVRChannel> channel = record->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = record->Channel();
if (channel && !channel->IconPath().empty())
SetArt("icon", channel->IconPath());
else if (record->IsRadio())
@@ -1105,6 +1109,10 @@ bool CFileItem::IsFileFolder(EFileFolderType types) const
if(IsInternetStream())
always_type = EFILEFOLDER_TYPE_ONCLICK;
+ // strm files are not browsable
+ if (IsType(".strm") && (types & EFILEFOLDER_TYPE_ONBROWSE))
+ return false;
+
if(types & always_type)
{
if(IsSmartPlayList()
@@ -1176,6 +1184,12 @@ bool CFileItem::IsNFO() const
return URIUtils::HasExtension(m_strPath, ".nfo");
}
+bool CFileItem::IsVideoExtras() const
+{
+ return m_bIsFolder &&
+ StringUtils::EqualsNoCase(URIUtils::GetFileOrFolderName(m_strPath), "extras");
+}
+
bool CFileItem::IsDiscImage() const
{
return URIUtils::IsDiscImage(GetDynPath());
@@ -1627,6 +1641,16 @@ void CFileItem::FillInMimeType(bool lookup /*= true*/)
}
}
+void CFileItem::UpdateMimeType(bool lookup /*= true*/)
+{
+ //! @todo application/octet-stream might actually have been set by a web lookup. Currently we
+ //! cannot distinguish between set as fallback only (see FillInMimeType) or as an actual value.
+ if (m_mimetype == "application/octet-stream")
+ m_mimetype.clear();
+
+ FillInMimeType(lookup);
+}
+
void CFileItem::SetMimeTypeForInternetFile()
{
if (m_doContentLookup && IsInternetStream())
@@ -1738,6 +1762,26 @@ void CFileItem::UpdateInfo(const CFileItem &item, bool replaceLabels /*=true*/)
*GetGameInfoTag() = *item.GetGameInfoTag();
SetInvalid();
}
+ if (item.HasPVRChannelGroupMemberInfoTag())
+ {
+ m_pvrChannelGroupMemberInfoTag = item.GetPVRChannelGroupMemberInfoTag();
+ SetInvalid();
+ }
+ if (item.HasPVRTimerInfoTag())
+ {
+ m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
+ SetInvalid();
+ }
+ if (item.HasEPGInfoTag())
+ {
+ m_epgInfoTag = item.m_epgInfoTag;
+ SetInvalid();
+ }
+ if (item.HasEPGSearchFilter())
+ {
+ m_epgSearchFilter = item.m_epgSearchFilter;
+ SetInvalid();
+ }
SetDynPath(item.GetDynPath());
if (replaceLabels && !item.GetLabel().empty())
SetLabel(item.GetLabel());
@@ -1746,6 +1790,7 @@ void CFileItem::UpdateInfo(const CFileItem &item, bool replaceLabels /*=true*/)
if (!item.GetArt().empty())
SetArt(item.GetArt());
AppendProperties(item);
+ UpdateMimeType();
}
void CFileItem::MergeInfo(const CFileItem& item)
@@ -1781,6 +1826,26 @@ void CFileItem::MergeInfo(const CFileItem& item)
*GetGameInfoTag() = *item.GetGameInfoTag();
SetInvalid();
}
+ if (item.HasPVRChannelGroupMemberInfoTag())
+ {
+ m_pvrChannelGroupMemberInfoTag = item.GetPVRChannelGroupMemberInfoTag();
+ SetInvalid();
+ }
+ if (item.HasPVRTimerInfoTag())
+ {
+ m_pvrTimerInfoTag = item.m_pvrTimerInfoTag;
+ SetInvalid();
+ }
+ if (item.HasEPGInfoTag())
+ {
+ m_epgInfoTag = item.m_epgInfoTag;
+ SetInvalid();
+ }
+ if (item.HasEPGSearchFilter())
+ {
+ m_epgSearchFilter = item.m_epgSearchFilter;
+ SetInvalid();
+ }
SetDynPath(item.GetDynPath());
if (!item.GetLabel().empty())
SetLabel(item.GetLabel());
@@ -1794,6 +1859,7 @@ void CFileItem::MergeInfo(const CFileItem& item)
SetArt(item.GetArt());
}
AppendProperties(item);
+ UpdateMimeType();
}
void CFileItem::SetFromVideoInfoTag(const CVideoInfoTag &video)
@@ -2105,11 +2171,11 @@ bool CFileItem::LoadTracksFromCueDocument(CFileItemList& scannedItems)
if ( tag.Loaded() && oneFilePerTrack && ! ( tag.GetAlbum().empty() || tag.GetArtist().empty() || tag.GetTitle().empty() ) )
{
// If there are multiple files in a cue file, the tags from the files should be preferred if they exist.
- scannedItems.Add(CFileItemPtr(new CFileItem(song, tag)));
+ scannedItems.Add(std::make_shared<CFileItem>(song, tag));
}
else
{
- scannedItems.Add(CFileItemPtr(new CFileItem(song)));
+ scannedItems.Add(std::make_shared<CFileItem>(song));
}
++tracksFound;
}
@@ -2460,7 +2526,7 @@ void CFileItemList::Sort(SortDescription sortDescription)
SortItems sortItems((size_t)Size());
for (int index = 0; index < Size(); index++)
{
- sortItems[index] = std::shared_ptr<SortItem>(new SortItem);
+ sortItems[index] = std::make_shared<SortItem>();
m_items[index]->ToSortable(*sortItems[index], fields);
(*sortItems[index])[FieldId] = index;
}
@@ -2542,7 +2608,7 @@ void CFileItemList::Archive(CArchive& ar)
{
CFileItemPtr pItem=m_items[0];
if (pItem->IsParentFolder())
- pParent.reset(new CFileItem(*pItem));
+ pParent = std::make_shared<CFileItem>(*pItem);
}
SetIgnoreURLOptions(false);
@@ -3518,6 +3584,9 @@ std::string CFileItem::GetBaseMoviePath(bool bUseFolderNames) const
URIUtils::GetParentPath(name2, strMovieName);
}
+
+ // Remove trailing 'Disc n' path segment to get actual movie title
+ strMovieName = CUtil::RemoveTrailingDiscNumberSegmentFromPath(strMovieName);
}
return strMovieName;
@@ -3706,19 +3775,24 @@ bool CFileItem::LoadDetails()
CVideoDatabase db;
if (!db.Open())
+ {
+ CLog::LogF(LOGERROR, "Error opening video database");
return false;
+ }
VIDEODATABASEDIRECTORY::CQueryParams params;
VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(GetPath(), params);
+ bool ret{false};
+ auto tag{std::make_unique<CVideoInfoTag>()};
if (params.GetMovieId() >= 0)
- db.GetMovieInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetMovieId()));
+ ret = db.GetMovieInfo(GetPath(), *tag, static_cast<int>(params.GetMovieId()));
else if (params.GetMVideoId() >= 0)
- db.GetMusicVideoInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetMVideoId()));
+ ret = db.GetMusicVideoInfo(GetPath(), *tag, static_cast<int>(params.GetMVideoId()));
else if (params.GetEpisodeId() >= 0)
- db.GetEpisodeInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetEpisodeId()));
+ ret = db.GetEpisodeInfo(GetPath(), *tag, static_cast<int>(params.GetEpisodeId()));
else if (params.GetSetId() >= 0) // movie set
- db.GetSetInfo(static_cast<int>(params.GetSetId()), *GetVideoInfoTag(), this);
+ ret = db.GetSetInfo(static_cast<int>(params.GetSetId()), *tag, this);
else if (params.GetTvShowId() >= 0)
{
if (params.GetSeason() >= 0)
@@ -3726,43 +3800,145 @@ bool CFileItem::LoadDetails()
const int idSeason = db.GetSeasonId(static_cast<int>(params.GetTvShowId()),
static_cast<int>(params.GetSeason()));
if (idSeason >= 0)
- db.GetSeasonInfo(idSeason, *GetVideoInfoTag(), this);
+ ret = db.GetSeasonInfo(idSeason, *tag, this);
}
else
- db.GetTvShowInfo(GetPath(), *GetVideoInfoTag(), static_cast<int>(params.GetTvShowId()),
- this);
+ ret = db.GetTvShowInfo(GetPath(), *tag, static_cast<int>(params.GetTvShowId()), this);
}
- else
+
+ if (ret)
+ {
+ const CFileItem loadedItem{*tag};
+ UpdateInfo(loadedItem);
+ }
+ return ret;
+ }
+
+ if (IsPVR())
+ {
+ const std::shared_ptr<CFileItem> loadedItem{
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Utils>().LoadItem(*this)};
+ if (loadedItem)
+ {
+ UpdateInfo(*loadedItem);
+ return true;
+ }
+ CLog::LogF(LOGERROR, "Error filling PVR item details (path={})", GetPath());
+ return false;
+ }
+
+ if (!IsPlayList() && IsVideo())
+ {
+ if (HasVideoInfoTag())
+ return true;
+
+ CVideoDatabase db;
+ if (!db.Open())
{
- db.Close();
+ CLog::LogF(LOGERROR, "Error opening video database");
return false;
}
- db.Close();
- return true;
+
+ auto tag{std::make_unique<CVideoInfoTag>()};
+ if (db.LoadVideoInfo(GetDynPath(), *tag))
+ {
+ const CFileItem loadedItem{*tag};
+ UpdateInfo(loadedItem);
+ return true;
+ }
+
+ CLog::LogF(LOGERROR, "Error filling item details (path={})", GetPath());
+ return false;
}
- if (URIUtils::IsPVRRecordingFileOrFolder(GetPath()))
+ if (IsPlayList() && IsType(".strm"))
{
- if (HasProperty("watchedepisodes") || HasProperty("watched"))
+ const std::unique_ptr<PLAYLIST::CPlayList> playlist(PLAYLIST::CPlayListFactory::Create(*this));
+ if (playlist)
+ {
+ if (playlist->Load(GetPath()) && playlist->size() == 1)
+ {
+ const auto item{(*playlist)[0]};
+ if (item->IsVideo())
+ {
+ CVideoDatabase db;
+ if (!db.Open())
+ {
+ CLog::LogF(LOGERROR, "Error opening video database");
+ return false;
+ }
+
+ CVideoInfoTag tag;
+ if (db.LoadVideoInfo(GetDynPath(), tag))
+ {
+ UpdateInfo(*item);
+ *GetVideoInfoTag() = tag;
+ return true;
+ }
+ }
+ else if (item->IsAudio())
+ {
+ if (item->LoadMusicTag())
+ {
+ UpdateInfo(*item);
+ return true;
+ }
+ }
+ }
+ }
+ CLog::LogF(LOGERROR, "Error loading strm file details (path={})", GetPath());
+ return false;
+ }
+
+ if (IsAudio())
+ {
+ return LoadMusicTag();
+ }
+
+ if (IsMusicDb())
+ {
+ if (HasMusicInfoTag())
return true;
- const std::string parentPath = URIUtils::GetParentPath(GetPath());
+ CMusicDatabase db;
+ if (!db.Open())
+ {
+ CLog::LogF(LOGERROR, "Error opening music database");
+ return false;
+ }
+
+ MUSICDATABASEDIRECTORY::CQueryParams params;
+ MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(GetPath(), params);
- //! @todo optimize, find a way to set the details of the item without loading parent directory.
- CFileItemList items;
- if (CDirectory::GetDirectory(parentPath, items, "", XFILE::DIR_FLAG_DEFAULTS))
+ if (params.GetSongId() >= 0)
{
- const std::string path = GetPath();
- const auto it = std::find_if(items.cbegin(), items.cend(),
- [path](const auto& entry) { return entry->GetPath() == path; });
- if (it != items.cend())
+ CSong song;
+ if (db.GetSong(params.GetSongId(), song))
{
- *this = *(*it);
+ GetMusicInfoTag()->SetSong(song);
+ return true;
+ }
+ }
+ else if (params.GetAlbumId() >= 0)
+ {
+ m_bIsFolder = true;
+ CAlbum album;
+ if (db.GetAlbum(params.GetAlbumId(), album, false))
+ {
+ GetMusicInfoTag()->SetAlbum(album);
+ return true;
+ }
+ }
+ else if (params.GetArtistId() >= 0)
+ {
+ m_bIsFolder = true;
+ CArtist artist;
+ if (db.GetArtist(params.GetArtistId(), artist, false))
+ {
+ GetMusicInfoTag()->SetArtist(artist);
return true;
}
}
-
- CLog::LogF(LOGERROR, "Error filling item details (path={})", GetPath());
return false;
}
@@ -4067,3 +4243,12 @@ bool CFileItem::IsResumable() const
return HasVideoInfoTag() && GetVideoInfoTag()->GetResumePoint().IsPartWay();
}
}
+
+bool CFileItem::HasVideoVersions() const
+{
+ if (HasVideoInfoTag())
+ {
+ return GetVideoInfoTag()->m_hasVideoVersions;
+ }
+ return false;
+}
diff --git a/xbmc/FileItem.h b/xbmc/FileItem.h
index 464ce10a35..d3d47be104 100644
--- a/xbmc/FileItem.h
+++ b/xbmc/FileItem.h
@@ -214,6 +214,7 @@ public:
bool IsAddonsPath() const;
bool IsSourcesPath() const;
bool IsNFO() const;
+ bool IsVideoExtras() const;
bool IsDiscImage() const;
bool IsOpticalMediaFile() const;
bool IsDVDFile(bool bVobs = true, bool bIfos = true) const;
@@ -261,6 +262,7 @@ public:
bool IsRSS() const;
bool IsAndroidApp() const;
+ bool HasVideoVersions() const;
void RemoveExtension();
void CleanString();
void FillInDefaultIcon();
@@ -636,6 +638,12 @@ private:
*/
void Initialize();
+ /*! \brief Recalculate item's MIME type if it is not set or is set to "application/octet-stream".
+ Resolve the MIME type based on file extension or a web lookup.
+ \sa FillInMimeType
+ */
+ void UpdateMimeType(bool lookup = true);
+
/*!
\brief Return the current resume point for this item.
\return The resume point.
@@ -645,7 +653,7 @@ private:
/*!
\brief Fill item's music tag from given epg tag.
*/
- void FillMusicInfoTag(const std::shared_ptr<PVR::CPVREpgInfoTag>& tag);
+ void FillMusicInfoTag(const std::shared_ptr<const PVR::CPVREpgInfoTag>& tag);
std::string m_strPath; ///< complete path to item
std::string m_strDynPath;
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index 44704963cb..4b04f4a775 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -1741,6 +1741,20 @@ const infomap weather[] = {{ "isfetched", WEATHER_IS_FETCHED },
/// @return the current language.
/// <p>
/// }
+/// \table_row3{ <b>`System.Locale(type)`</b>,
+/// \anchor System_Locale
+/// _string_,
+/// @return Locale-specific information depending on the requested type.
+/// @param type - Can be one of the following:
+/// - <b>timezonecountry</b> The country name for the current time zone.
+/// - <b>timezone</b> The full timezone name with country and optional region.
+/// - <b>region</b> The currently selected region name within the selected language ( \link System_Language `System.Language` \endlink).
+/// - <b>iso</b> The country code of the currently selected region as specified in <a href="https://kodi.wiki/view/Language_support#What_is_langinfo.xml">langinfo.xml</a>.
+/// <p><hr>
+/// @skinning_v21 **[New Infolabel]** \link System_Locale
+/// `System.Locale(type)`\endlink
+/// <p>
+/// }
/// \table_row3{ <b>`System.ProfileName`</b>,
/// \anchor System_ProfileName
/// _string_,
@@ -3982,8 +3996,8 @@ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE },
/// _string_,
/// @return The video filter of the currently-playing game.
/// The following values are possible:
-/// - nearest (Nearest neighbor\, i.e. pixelate)
-/// - linear (Bilinear filtering\, i.e. smooth blur)
+/// - <b>`nearest`</b> (Nearest neighbor\, i.e. pixelate)
+/// - <b>`linear`</b> (Bilinear filtering\, i.e. smooth blur)
/// <p><hr>
/// @skinning_v18 **[New Infolabel]** \link RetroPlayer_VideoFilter `RetroPlayer.VideoFilter`\endlink
/// <p>
@@ -3993,10 +4007,10 @@ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE },
/// _string_,
/// @return The stretch mode of the currently-playing game.
/// The following values are possible:
-/// - normal (Show the game normally)
-/// - 4:3 (Stretch to a 4:3 aspect ratio)
-/// - fullscreen (Stretch to the full viewing area)
-/// - original (Shrink to the original resolution)
+/// - <b>`normal`</b> (Show the game normally)
+/// - <b>`4:3`</b> (Stretch to a 4:3 aspect ratio)
+/// - <b>`fullscreen`</b> (Stretch to the full viewing area)
+/// - <b>`original`</b> (Shrink to the original resolution)
/// <p><hr>
/// @skinning_v18 **[New Infolabel]** \link RetroPlayer_StretchMode `RetroPlayer.StretchMode`\endlink
/// <p>
@@ -4007,10 +4021,10 @@ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE },
/// @return The video rotation of the currently-playing game
/// in degrees counter-clockwise.
/// The following values are possible:
-/// - 0
-/// - 90 (Shown in the GUI as 270 degrees)
-/// - 180
-/// - 270 (Shown in the GUI as 90 degrees)
+/// - <b>`0`</b>
+/// - <b>`90`</b> (Shown in the GUI as 270 degrees)
+/// - <b>`180`</b>
+/// - <b>`270`</b> (Shown in the GUI as 90 degrees)
/// <p><hr>
/// @skinning_v18 **[New Infolabel]** \link RetroPlayer_VideoRotation `RetroPlayer.VideoRotation`\endlink
/// <p>
@@ -6886,6 +6900,20 @@ const infomap container_str[] = {{ "property", CONTAINER_PROPERTY },
/// <p><hr>
/// @skinning_v21 **[New Infolabel]** \link ListItem_VideoHeight `ListItem.VideoHeight`\endlink
/// }
+/// \table_row3{ <b>`ListItem.HasVideoVersions`</b>,
+/// \anchor ListItem_HasVideoVersions
+/// _boolean_,
+/// @return **True** when the selected item has multiple video versions.
+/// <p><hr>
+/// @skinning_v21 **[New Infolabel]** \link ListItem_HasVideoVersions `ListItem.HasVideoVersions`\endlink
+/// }
+/// \table_row3{ <b>`ListItem.IsVideoExtras`</b>,
+/// \anchor ListItem_IsVideoExtras
+/// _boolean_,
+/// @return **True** when the selected item is video extras.
+/// <p><hr>
+/// @skinning_v21 **[New Infolabel]** \link ListItem_IsVideoExtras `ListItem.IsVideoExtras`\endlink
+/// }
/// \table_end
///
/// -----------------------------------------------------------------------------
@@ -7103,6 +7131,8 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB },
{ "isautoupdateable", LISTITEM_ISAUTOUPDATEABLE },
{ "hdrtype", LISTITEM_VIDEO_HDR_TYPE },
{ "songvideourl", LISTITEM_SONG_VIDEO_URL },
+ { "hasvideoversions", LISTITEM_HASVIDEOVERSIONS },
+ { "isvideoextras", LISTITEM_ISVIDEOEXTRAS },
};
// clang-format on
@@ -9894,7 +9924,7 @@ void CGUIInfoManager::SplitInfoString(const std::string &infoString, std::vector
if (!property.empty()) // add our property and parameters
{
StringUtils::ToLower(property);
- info.emplace_back(Property(property, param));
+ info.emplace_back(property, param);
}
property.clear();
param.clear();
@@ -9912,7 +9942,7 @@ void CGUIInfoManager::SplitInfoString(const std::string &infoString, std::vector
if (!property.empty())
{
StringUtils::ToLower(property);
- info.emplace_back(Property(property, param));
+ info.emplace_back(property, param);
}
}
@@ -10138,6 +10168,25 @@ int CGUIInfoManager::TranslateSingleString(const std::string &strCondition, bool
}
else if (prop.name == "idletime")
return AddMultiInfo(CGUIInfo(SYSTEM_IDLE_TIME, atoi(param.c_str())));
+ else if (prop.name == "locale")
+ {
+ if (param == "timezonecountry")
+ {
+ return SYSTEM_LOCALE_TIMEZONECOUNTRY;
+ }
+ else if (param == "timezone")
+ {
+ return SYSTEM_LOCALE_TIMEZONE;
+ }
+ else if (param == "region")
+ {
+ return SYSTEM_LOCALE_REGION;
+ }
+ else if (param == "iso")
+ {
+ return SYSTEM_LOCALE;
+ }
+ }
}
if (prop.name == "alarmlessorequal" && prop.num_params() == 2)
return AddMultiInfo(CGUIInfo(SYSTEM_ALARM_LESS_OR_EQUAL, prop.param(0), atoi(prop.param(1).c_str())));
diff --git a/xbmc/LangInfo.cpp b/xbmc/LangInfo.cpp
index d5ebf014ae..bc6a286145 100644
--- a/xbmc/LangInfo.cpp
+++ b/xbmc/LangInfo.cpp
@@ -8,6 +8,7 @@
#include "LangInfo.h"
+#include "DatabaseManager.h"
#include "ServiceBroker.h"
#include "XBDateTime.h"
#include "addons/AddonInstaller.h"
@@ -790,6 +791,7 @@ bool CLangInfo::SetLanguage(std::string language /* = "" */, bool reloadServices
// also tell our weather and skin to reload as these are localized
CServiceBroker::GetWeatherManager().Refresh();
CServiceBroker::GetPVRManager().LocalizationChanged();
+ CServiceBroker::GetDatabaseManager().LocalizationChanged();
CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
"ReloadSkin");
}
diff --git a/xbmc/ServiceManager.cpp b/xbmc/ServiceManager.cpp
index ba94b8eec8..68ca4ca0c1 100644
--- a/xbmc/ServiceManager.cpp
+++ b/xbmc/ServiceManager.cpp
@@ -48,6 +48,8 @@
#include "utils/log.h"
#include "weather/WeatherManager.h"
+#include <memory>
+
using namespace KODI;
CServiceManager::CServiceManager() = default;
@@ -66,18 +68,18 @@ bool CServiceManager::InitForTesting()
{
m_network = CNetworkBase::GetNetwork();
- m_databaseManager.reset(new CDatabaseManager);
+ m_databaseManager = std::make_unique<CDatabaseManager>();
- m_binaryAddonManager.reset(new ADDON::CBinaryAddonManager());
- m_addonMgr.reset(new ADDON::CAddonMgr());
+ m_binaryAddonManager = std::make_unique<ADDON::CBinaryAddonManager>();
+ m_addonMgr = std::make_unique<ADDON::CAddonMgr>();
if (!m_addonMgr->Init())
{
CLog::Log(LOGFATAL, "CServiceManager::{}: Unable to start CAddonMgr", __FUNCTION__);
return false;
}
- m_extsMimeSupportList.reset(new ADDONS::CExtsMimeSupportList(*m_addonMgr));
- m_fileExtensionProvider.reset(new CFileExtensionProvider(*m_addonMgr));
+ m_extsMimeSupportList = std::make_unique<ADDONS::CExtsMimeSupportList>(*m_addonMgr);
+ m_fileExtensionProvider = std::make_unique<CFileExtensionProvider>(*m_addonMgr);
init_level = 1;
return true;
@@ -101,12 +103,12 @@ bool CServiceManager::InitStageOne()
return false;
#ifdef HAS_PYTHON
- m_XBPython.reset(new XBPython());
+ m_XBPython = std::make_unique<XBPython>();
CScriptInvocationManager::GetInstance().RegisterLanguageInvocationHandler(m_XBPython.get(),
".py");
#endif
- m_playlistPlayer.reset(new PLAYLIST::CPlayListPlayer());
+ m_playlistPlayer = std::make_unique<PLAYLIST::CPlayListPlayer>();
m_network = CNetworkBase::GetNetwork();
@@ -117,55 +119,56 @@ bool CServiceManager::InitStageOne()
bool CServiceManager::InitStageTwo(const std::string& profilesUserDataFolder)
{
// Initialize the addon database (must be before the addon manager is init'd)
- m_databaseManager.reset(new CDatabaseManager);
+ m_databaseManager = std::make_unique<CDatabaseManager>();
- m_binaryAddonManager.reset(
- new ADDON::
- CBinaryAddonManager()); /* Need to constructed before, GetRunningInstance() of binary CAddonDll need to call them */
- m_addonMgr.reset(new ADDON::CAddonMgr());
+ m_binaryAddonManager = std::make_unique<
+ ADDON::
+ CBinaryAddonManager>(); /* Need to constructed before, GetRunningInstance() of binary CAddonDll need to call them */
+ m_addonMgr = std::make_unique<ADDON::CAddonMgr>();
if (!m_addonMgr->Init())
{
CLog::Log(LOGFATAL, "CServiceManager::{}: Unable to start CAddonMgr", __FUNCTION__);
return false;
}
- m_repositoryUpdater.reset(new ADDON::CRepositoryUpdater(*m_addonMgr));
+ m_repositoryUpdater = std::make_unique<ADDON::CRepositoryUpdater>(*m_addonMgr);
- m_extsMimeSupportList.reset(new ADDONS::CExtsMimeSupportList(*m_addonMgr));
+ m_extsMimeSupportList = std::make_unique<ADDONS::CExtsMimeSupportList>(*m_addonMgr);
- m_vfsAddonCache.reset(new ADDON::CVFSAddonCache());
+ m_vfsAddonCache = std::make_unique<ADDON::CVFSAddonCache>();
m_vfsAddonCache->Init();
- m_PVRManager.reset(new PVR::CPVRManager());
+ m_PVRManager = std::make_unique<PVR::CPVRManager>();
- m_dataCacheCore.reset(new CDataCacheCore());
+ m_dataCacheCore = std::make_unique<CDataCacheCore>();
- m_binaryAddonCache.reset(new ADDON::CBinaryAddonCache());
+ m_binaryAddonCache = std::make_unique<ADDON::CBinaryAddonCache>();
m_binaryAddonCache->Init();
- m_favouritesService.reset(new CFavouritesService(profilesUserDataFolder));
+ m_favouritesService = std::make_unique<CFavouritesService>(profilesUserDataFolder);
- m_serviceAddons.reset(new ADDON::CServiceAddonManager(*m_addonMgr));
+ m_serviceAddons = std::make_unique<ADDON::CServiceAddonManager>(*m_addonMgr);
- m_contextMenuManager.reset(new CContextMenuManager(*m_addonMgr));
+ m_contextMenuManager = std::make_unique<CContextMenuManager>(*m_addonMgr);
m_gameControllerManager = std::make_unique<GAME::CControllerManager>(*m_addonMgr);
- m_inputManager.reset(new CInputManager());
+ m_inputManager = std::make_unique<CInputManager>();
m_inputManager->InitializeInputs();
- m_peripherals.reset(new PERIPHERALS::CPeripherals(*m_inputManager, *m_gameControllerManager));
+ m_peripherals =
+ std::make_unique<PERIPHERALS::CPeripherals>(*m_inputManager, *m_gameControllerManager);
- m_gameRenderManager.reset(new RETRO::CGUIGameRenderManager);
+ m_gameRenderManager = std::make_unique<RETRO::CGUIGameRenderManager>();
- m_fileExtensionProvider.reset(new CFileExtensionProvider(*m_addonMgr));
+ m_fileExtensionProvider = std::make_unique<CFileExtensionProvider>(*m_addonMgr);
- m_powerManager.reset(new CPowerManager());
+ m_powerManager = std::make_unique<CPowerManager>();
m_powerManager->Initialize();
m_powerManager->SetDefaults();
- m_weatherManager.reset(new CWeatherManager());
+ m_weatherManager = std::make_unique<CWeatherManager>();
- m_mediaManager.reset(new CMediaManager());
+ m_mediaManager = std::make_unique<CMediaManager>();
m_mediaManager->Initialize();
#if !defined(TARGET_WINDOWS) && defined(HAS_OPTICAL_DRIVE)
@@ -205,7 +208,7 @@ bool CServiceManager::InitStageThree(const std::shared_ptr<CProfileManager>& pro
if (!profileManager->UsingLoginScreen())
m_PVRManager->Init();
- m_playerCoreFactory.reset(new CPlayerCoreFactory(*profileManager));
+ m_playerCoreFactory = std::make_unique<CPlayerCoreFactory>(*profileManager);
if (!m_Platform->InitStageThree())
return false;
@@ -400,22 +403,6 @@ CPowerManager& CServiceManager::GetPowerManager()
return *m_powerManager;
}
-// deleters for unique_ptr
-void CServiceManager::delete_dataCacheCore::operator()(CDataCacheCore* p) const
-{
- delete p;
-}
-
-void CServiceManager::delete_contextMenuManager::operator()(CContextMenuManager* p) const
-{
- delete p;
-}
-
-void CServiceManager::delete_favouritesService::operator()(CFavouritesService* p) const
-{
- delete p;
-}
-
CNetworkBase& CServiceManager::GetNetwork()
{
return *m_network;
diff --git a/xbmc/ServiceManager.h b/xbmc/ServiceManager.h
index 4555c8ea88..085589498f 100644
--- a/xbmc/ServiceManager.h
+++ b/xbmc/ServiceManager.h
@@ -148,21 +148,6 @@ public:
#endif
protected:
- struct delete_dataCacheCore
- {
- void operator()(CDataCacheCore* p) const;
- };
-
- struct delete_contextMenuManager
- {
- void operator()(CContextMenuManager* p) const;
- };
-
- struct delete_favouritesService
- {
- void operator()(CFavouritesService* p) const;
- };
-
std::unique_ptr<ADDON::CAddonMgr> m_addonMgr;
std::unique_ptr<ADDON::CBinaryAddonManager> m_binaryAddonManager;
std::unique_ptr<ADDON::CBinaryAddonCache> m_binaryAddonCache;
@@ -177,15 +162,15 @@ protected:
std::unique_ptr<XBPython> m_XBPython;
#endif
std::unique_ptr<PVR::CPVRManager> m_PVRManager;
- std::unique_ptr<CContextMenuManager, delete_contextMenuManager> m_contextMenuManager;
- std::unique_ptr<CDataCacheCore, delete_dataCacheCore> m_dataCacheCore;
+ std::unique_ptr<CContextMenuManager> m_contextMenuManager;
+ std::unique_ptr<CDataCacheCore> m_dataCacheCore;
std::unique_ptr<CPlatform> m_Platform;
std::unique_ptr<PLAYLIST::CPlayListPlayer> m_playlistPlayer;
std::unique_ptr<KODI::GAME::CControllerManager> m_gameControllerManager;
std::unique_ptr<KODI::GAME::CGameServices> m_gameServices;
std::unique_ptr<KODI::RETRO::CGUIGameRenderManager> m_gameRenderManager;
std::unique_ptr<PERIPHERALS::CPeripherals> m_peripherals;
- std::unique_ptr<CFavouritesService, delete_favouritesService> m_favouritesService;
+ std::unique_ptr<CFavouritesService> m_favouritesService;
std::unique_ptr<CInputManager> m_inputManager;
std::unique_ptr<CFileExtensionProvider> m_fileExtensionProvider;
std::unique_ptr<CNetworkBase> m_network;
diff --git a/xbmc/TextureCache.cpp b/xbmc/TextureCache.cpp
index ab5fff51e7..0479bf1e5b 100644
--- a/xbmc/TextureCache.cpp
+++ b/xbmc/TextureCache.cpp
@@ -87,6 +87,9 @@ std::string CTextureCache::GetCachedImage(const std::string &image, CTextureDeta
// lookup the item in the database
if (GetCachedTexture(url, details))
{
+ if (details.file.empty())
+ return {};
+
if (trackUsage)
IncrementUseCount(details);
return GetCachedPath(details.file);
@@ -296,7 +299,7 @@ void CTextureCache::OnCachingComplete(bool success, CTextureCacheJob *job)
{
if (success)
{
- if (job->m_details.id != -1 && job->m_oldHash == job->m_details.hash)
+ if (job->m_details.hashRevalidated)
SetCachedTextureValid(job->m_url, job->m_details.updateable);
else
AddCachedTexture(job->m_url, job->m_details);
diff --git a/xbmc/TextureCacheJob.cpp b/xbmc/TextureCacheJob.cpp
index 66edb39202..2850046e03 100644
--- a/xbmc/TextureCacheJob.cpp
+++ b/xbmc/TextureCacheJob.cpp
@@ -118,7 +118,10 @@ bool CTextureCacheJob::CacheTexture(std::unique_ptr<CTexture>* out_texture)
return false;
if (m_details.hash == m_oldHash)
+ {
+ m_details.hashRevalidated = true;
return true;
+ }
}
std::unique_ptr<CTexture> texture = LoadImage(image, width, height, additional_info, true);
diff --git a/xbmc/TextureCacheJob.h b/xbmc/TextureCacheJob.h
index 8cbe65ef54..8d7b764cd8 100644
--- a/xbmc/TextureCacheJob.h
+++ b/xbmc/TextureCacheJob.h
@@ -26,24 +26,20 @@ class CTexture;
class CTextureDetails
{
public:
- CTextureDetails()
- {
- id = -1;
- width = height = 0;
- updateable = false;
- };
bool operator==(const CTextureDetails &right) const
{
return (id == right.id &&
file == right.file &&
width == right.width );
};
- int id;
- std::string file;
- std::string hash;
- unsigned int width;
- unsigned int height;
- bool updateable;
+
+ int id{-1};
+ std::string file;
+ std::string hash;
+ unsigned int width{0};
+ unsigned int height{0};
+ bool updateable{false};
+ bool hashRevalidated{false};
};
/*!
diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp
index 62548f0a08..8a6bae30da 100644
--- a/xbmc/Util.cpp
+++ b/xbmc/Util.cpp
@@ -374,6 +374,107 @@ std::string CUtil::GetTitleFromPath(const CURL& url, bool bIsFolder /* = false *
return strFilename;
}
+namespace
+{
+void GetTrailingDiscNumberSegmentInfoFromPath(const std::string& pathIn,
+ size_t& pos,
+ std::string& number)
+{
+ std::string path{pathIn};
+ URIUtils::RemoveSlashAtEnd(path);
+
+ pos = std::string::npos;
+ number.clear();
+
+ // Handle Disc, Disk and locale specific spellings
+ std::string discStr{StringUtils::Format("/{} ", g_localizeStrings.Get(427))};
+ size_t discPos = path.rfind(discStr);
+
+ if (discPos == std::string::npos)
+ {
+ discStr = "/Disc ";
+ discPos = path.rfind(discStr);
+ }
+
+ if (discPos == std::string::npos)
+ {
+ discStr = "/Disk ";
+ discPos = path.rfind(discStr);
+ }
+
+ if (discPos != std::string::npos)
+ {
+ // Check remainder of path is numeric (eg. Disc 1)
+ const std::string discNum{path.substr(discPos + discStr.size())};
+ if (discNum.find_first_not_of("0123456789") == std::string::npos)
+ {
+ pos = discPos;
+ number = discNum;
+ }
+ }
+}
+} // unnamed namespace
+
+std::string CUtil::RemoveTrailingDiscNumberSegmentFromPath(std::string path)
+{
+ size_t discPos{std::string::npos};
+ std::string discNum;
+ GetTrailingDiscNumberSegmentInfoFromPath(path, discPos, discNum);
+
+ if (discPos != std::string::npos)
+ path.erase(discPos);
+
+ return path;
+}
+
+std::string CUtil::GetDiscNumberFromPath(const std::string& path)
+{
+ size_t discPos{std::string::npos};
+ std::string discNum;
+ GetTrailingDiscNumberSegmentInfoFromPath(path, discPos, discNum);
+ return discNum;
+}
+
+bool CUtil::GetFilenameIdentifier(const std::string& fileName,
+ std::string& identifierType,
+ std::string& identifier)
+{
+ std::string match;
+ return GetFilenameIdentifier(fileName, identifierType, identifier, match);
+}
+
+bool CUtil::GetFilenameIdentifier(const std::string& fileName,
+ std::string& identifierType,
+ std::string& identifier,
+ std::string& match)
+{
+ CRegExp reIdentifier(true, CRegExp::autoUtf8);
+
+ const std::shared_ptr<CAdvancedSettings> advancedSettings =
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
+ if (!reIdentifier.RegComp(advancedSettings->m_videoFilenameIdentifierRegExp))
+ {
+ CLog::LogF(LOGERROR, "Invalid filename identifier RegExp:'{}'",
+ advancedSettings->m_videoFilenameIdentifierRegExp);
+ return false;
+ }
+ else
+ {
+ if (reIdentifier.RegComp(advancedSettings->m_videoFilenameIdentifierRegExp))
+ {
+ if (reIdentifier.RegFind(fileName) >= 0)
+ {
+ match = reIdentifier.GetMatch(0);
+ identifierType = reIdentifier.GetMatch(1);
+ identifier = reIdentifier.GetMatch(2);
+ StringUtils::ToLower(identifierType);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void CUtil::CleanString(const std::string& strFileName,
std::string& strTitle,
std::string& strTitleAndYear,
@@ -386,6 +487,12 @@ void CUtil::CleanString(const std::string& strFileName,
if (strFileName == "..")
return;
+ std::string identifier;
+ std::string identifierType;
+ std::string identifierMatch;
+ if (GetFilenameIdentifier(strFileName, identifierType, identifier, identifierMatch))
+ StringUtils::Replace(strTitleAndYear, identifierMatch, "");
+
const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
const std::vector<std::string> &regexps = advancedSettings->m_videoCleanStringRegExps;
diff --git a/xbmc/Util.h b/xbmc/Util.h
index 88e58c4820..b93df4c3c8 100644
--- a/xbmc/Util.h
+++ b/xbmc/Util.h
@@ -43,8 +43,28 @@ public:
std::string& strYear,
bool bRemoveExtension = false,
bool bCleanChars = true);
+ static bool GetFilenameIdentifier(const std::string& fileName,
+ std::string& identifierType,
+ std::string& identifier);
+ static bool GetFilenameIdentifier(const std::string& fileName,
+ std::string& identifierType,
+ std::string& identifier,
+ std::string& match);
static std::string GetTitleFromPath(const CURL& url, bool bIsFolder = false);
static std::string GetTitleFromPath(const std::string& strFileNameAndPath, bool bIsFolder = false);
+
+ /*! \brief Return the disc number in case the last segment of given path ends with 'Disc n'.
+ Will look for 'Disc', 'Disk' and the locale specific spelling.
+ \return the disc number as string if found, empty string otherwise.
+ */
+ static std::string GetDiscNumberFromPath(const std::string& path);
+
+ /*! \brief Remove last segment of the given path if it matches 'Disc n'.
+ Will look for 'Disc', 'Disk' and the locale specific spelling.
+ \return the given path with last segment removed if it matches 'Disc n', unchanged path otherwise.
+ */
+ static std::string RemoveTrailingDiscNumberSegmentFromPath(std::string path);
+
static void GetQualifiedFilename(const std::string &strBasePath, std::string &strFilename);
static void RunShortcut(const char* szPath);
static std::string GetHomePath(
diff --git a/xbmc/addons/AddonInstaller.cpp b/xbmc/addons/AddonInstaller.cpp
index 0a0d1694dd..3343bf1170 100644
--- a/xbmc/addons/AddonInstaller.cpp
+++ b/xbmc/addons/AddonInstaller.cpp
@@ -46,6 +46,7 @@
#include "utils/log.h"
#include <functional>
+#include <memory>
#include <mutex>
using namespace XFILE;
@@ -553,7 +554,7 @@ void CAddonInstaller::PrunePackageCache()
{
it->second->Sort(SortByLabel, SortOrderDescending);
for (int j = 2; j < it->second->Size(); j++)
- items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j))));
+ items.Add(std::make_shared<CFileItem>(*it->second->Get(j)));
}
items.Sort(SortBySize, SortOrderDescending);
@@ -572,7 +573,7 @@ void CAddonInstaller::PrunePackageCache()
for (auto it = packs.begin(); it != packs.end(); ++it)
{
if (it->second->Size() > 1)
- items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1))));
+ items.Add(std::make_shared<CFileItem>(*it->second->Get(1)));
}
items.Sort(SortByDate, SortOrderAscending);
@@ -626,7 +627,7 @@ int64_t CAddonInstaller::EnumeratePackageFolder(
CAddonVersion::SplitFileName(pack, dummy, items[i]->GetLabel());
if (result.find(pack) == result.end())
result[pack] = std::make_unique<CFileItemList>();
- result[pack]->Add(CFileItemPtr(new CFileItem(*items[i])));
+ result[pack]->Add(std::make_shared<CFileItem>(*items[i]));
}
return size;
@@ -979,7 +980,7 @@ bool CAddonInstallJob::DownloadPackage(const std::string &path, const std::strin
// need to download/copy the package first
CFileItemList list;
- list.Add(CFileItemPtr(new CFileItem(path, false)));
+ list.Add(std::make_shared<CFileItem>(path, false));
list[0]->Select(true);
return DoFileOperation(CFileOperationJob::ActionReplace, list, dest, true);
diff --git a/xbmc/addons/AddonUpdateRules.cpp b/xbmc/addons/AddonUpdateRules.cpp
index 7d9d43a7c6..6c620f01d7 100644
--- a/xbmc/addons/AddonUpdateRules.cpp
+++ b/xbmc/addons/AddonUpdateRules.cpp
@@ -12,6 +12,7 @@
#include "addons/addoninfo/AddonInfo.h"
#include "utils/log.h"
+#include <algorithm>
#include <mutex>
using namespace ADDON;
diff --git a/xbmc/addons/Scraper.cpp b/xbmc/addons/Scraper.cpp
index ad7b251052..66cedcb969 100644
--- a/xbmc/addons/Scraper.cpp
+++ b/xbmc/addons/Scraper.cpp
@@ -30,6 +30,7 @@
#include "settings/SettingsComponent.h"
#include "settings/SettingsValueFlatJsonSerializer.h"
#include "utils/CharsetConverter.h"
+#include "utils/JSONVariantWriter.h"
#include "utils/ScraperParser.h"
#include "utils/ScraperUrl.h"
#include "utils/StringUtils.h"
@@ -731,10 +732,10 @@ static std::string ParseFanart(const CFileItem &item, int nFanart, const std::st
}
template<class T>
-static void DetailsFromFileItem(const CFileItem &, T &);
+static bool DetailsFromFileItem(const CFileItem&, T&);
template<>
-void DetailsFromFileItem<CAlbum>(const CFileItem &item, CAlbum &album)
+bool DetailsFromFileItem<CAlbum>(const CFileItem& item, CAlbum& album)
{
album.strAlbum = item.GetLabel();
album.strMusicBrainzAlbumID = FromString(item, "album.musicbrainzid");
@@ -777,10 +778,11 @@ void DetailsFromFileItem<CAlbum>(const CFileItem &item, CAlbum &album)
int nThumbs = item.GetProperty("album.thumbs").asInteger32();
ParseThumbs(album.thumbURL, item, nThumbs, "album.thumb");
+ return true;
}
template<>
-void DetailsFromFileItem<CArtist>(const CFileItem &item, CArtist &artist)
+bool DetailsFromFileItem<CArtist>(const CFileItem& item, CArtist& artist)
{
artist.strArtist = item.GetLabel();
artist.strMusicBrainzArtistID = FromString(item, "artist.musicbrainzid");
@@ -846,34 +848,58 @@ void DetailsFromFileItem<CArtist>(const CFileItem &item, CArtist &artist)
for (unsigned int i = 0; i < fanart.GetNumFanarts(); i++)
artist.thumbURL.AddParsedUrl(fanart.GetImageURL(i), "fanart", fanart.GetPreviewURL(i));
}
+ return true;
}
template<>
-void DetailsFromFileItem<CVideoInfoTag>(const CFileItem &item, CVideoInfoTag &tag)
+bool DetailsFromFileItem<CVideoInfoTag>(const CFileItem& item, CVideoInfoTag& tag)
{
if (item.HasVideoInfoTag())
+ {
tag = *item.GetVideoInfoTag();
+ return true;
+ }
+ return false;
}
template<class T>
-static bool PythonDetails(const std::string &ID,
- const std::string &key,
- const std::string &url,
- const std::string &action,
- const std::string &pathSettings,
- T &result)
+static bool PythonDetails(const std::string& ID,
+ const std::string& key,
+ const std::string& url,
+ const std::string& action,
+ const std::string& pathSettings,
+ const std::unordered_map<std::string, std::string>& uniqueIDs,
+ T& result)
{
+ CVariant ids;
+ for (const auto& [identifierType, identifier] : uniqueIDs)
+ ids[identifierType] = identifier;
+ std::string uids;
+ CJSONVariantWriter::Write(ids, uids, true);
std::stringstream str;
str << "plugin://" << ID << "?action=" << action << "&" << key << "=" << CURL::Encode(url);
str << "&pathSettings=" << CURL::Encode(pathSettings);
+ if (!uniqueIDs.empty())
+ str << "&uniqueIDs=" << CURL::Encode(uids);
CFileItem item(url, false);
if (!XFILE::CPluginDirectory::GetPluginResult(str.str(), item, false))
return false;
- DetailsFromFileItem(item, result);
- return true;
+ return DetailsFromFileItem(item, result);
+}
+
+template<class T>
+static bool PythonDetails(const std::string& ID,
+ const std::string& key,
+ const std::string& url,
+ const std::string& action,
+ const std::string& pathSettings,
+ T& result)
+{
+ const std::unordered_map<std::string, std::string> ids;
+ return PythonDetails(ID, key, url, action, pathSettings, ids, result);
}
// fetch list of matching movies sorted by relevance (may be empty);
@@ -1324,10 +1350,11 @@ EPISODELIST CScraper::GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl
}
// takes URL; returns true and populates video details on success, false otherwise
-bool CScraper::GetVideoDetails(XFILE::CCurlFile &fcurl,
- const CScraperUrl &scurl,
+bool CScraper::GetVideoDetails(XFILE::CCurlFile& fcurl,
+ const std::unordered_map<std::string, std::string>& uniqueIDs,
+ const CScraperUrl& scurl,
bool fMovie /*else episode*/,
- CVideoInfoTag &video)
+ CVideoInfoTag& video)
{
CLog::Log(LOGDEBUG,
"{}: Reading {} '{}' using {} scraper "
@@ -1339,7 +1366,8 @@ bool CScraper::GetVideoDetails(XFILE::CCurlFile &fcurl,
if (m_isPython)
return PythonDetails(ID(), "url", scurl.GetFirstThumbUrl(),
- fMovie ? "getdetails" : "getepisodedetails", GetPathSettingsAsJSON(), video);
+ fMovie ? "getdetails" : "getepisodedetails", GetPathSettingsAsJSON(),
+ uniqueIDs, video);
std::string sFunc = fMovie ? "GetDetails" : "GetEpisodeDetails";
std::vector<std::string> vcsIn;
diff --git a/xbmc/addons/Scraper.h b/xbmc/addons/Scraper.h
index 62b0d38139..93d34be3d8 100644
--- a/xbmc/addons/Scraper.h
+++ b/xbmc/addons/Scraper.h
@@ -131,8 +131,11 @@ public:
XFILE::CCurlFile &fcurl, const std::string &sArtist);
VIDEO::EPISODELIST GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl);
- bool GetVideoDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl,
- bool fMovie/*else episode*/, CVideoInfoTag &video);
+ bool GetVideoDetails(XFILE::CCurlFile& fcurl,
+ const std::unordered_map<std::string, std::string>& uniqueIDs,
+ const CScraperUrl& scurl,
+ bool fMovie /*else episode*/,
+ CVideoInfoTag& video);
bool GetAlbumDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl,
CAlbum &album);
bool GetArtistDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl,
diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp
index 9e63a3f047..a6192d902b 100644
--- a/xbmc/addons/Skin.cpp
+++ b/xbmc/addons/Skin.cpp
@@ -34,6 +34,7 @@
#include "utils/log.h"
#include <charconv>
+#include <memory>
#define XML_SETTINGS "settings"
#define XML_SETTING "setting"
@@ -157,7 +158,7 @@ CSkinInfo::CSkinInfo(const AddonInfoPtr& addonInfo,
m_effectsSlowDown(1.f),
m_debugging(false)
{
- m_settingsUpdateHandler.reset(new CSkinSettingUpdateHandler(*this));
+ m_settingsUpdateHandler = std::make_unique<CSkinSettingUpdateHandler>(*this);
}
CSkinInfo::CSkinInfo(const AddonInfoPtr& addonInfo) : CAddon(addonInfo, AddonType::SKIN)
@@ -193,7 +194,7 @@ CSkinInfo::CSkinInfo(const AddonInfoPtr& addonInfo) : CAddon(addonInfo, AddonTyp
m_debugging = Type(AddonType::SKIN)->GetValue("@debugging").asBoolean();
- m_settingsUpdateHandler.reset(new CSkinSettingUpdateHandler(*this));
+ m_settingsUpdateHandler = std::make_unique<CSkinSettingUpdateHandler>(*this);
LoadStartupWindows(addonInfo);
}
diff --git a/xbmc/addons/addoninfo/AddonExtensions.cpp b/xbmc/addons/addoninfo/AddonExtensions.cpp
index b0bc3c5b25..496373ff88 100644
--- a/xbmc/addons/addoninfo/AddonExtensions.cpp
+++ b/xbmc/addons/addoninfo/AddonExtensions.cpp
@@ -55,7 +55,7 @@ const EXT_ELEMENTS CAddonExtensions::GetElements(const std::string& id) const
for (const auto& child : m_children)
{
if (child.first == id)
- children.push_back(std::make_pair(child.first, child.second));
+ children.emplace_back(child.first, child.second);
}
return children;
}
@@ -63,6 +63,6 @@ const EXT_ELEMENTS CAddonExtensions::GetElements(const std::string& id) const
void CAddonExtensions::Insert(const std::string& id, const std::string& value)
{
EXT_VALUE extension;
- extension.push_back(std::make_pair(id, SExtValue(value)));
- m_values.push_back(std::make_pair(id, extension));
+ extension.emplace_back(id, SExtValue(value));
+ m_values.emplace_back(id, extension);
}
diff --git a/xbmc/addons/binary-addons/AddonDll.cpp b/xbmc/addons/binary-addons/AddonDll.cpp
index 7f867dbf83..2c03541110 100644
--- a/xbmc/addons/binary-addons/AddonDll.cpp
+++ b/xbmc/addons/binary-addons/AddonDll.cpp
@@ -27,6 +27,7 @@
#include "utils/Variant.h"
#include "utils/log.h"
+#include <algorithm>
#include <utility>
using namespace KODI::MESSAGING;
diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.cpp b/xbmc/addons/gui/GUIDialogAddonInfo.cpp
index b1a24a9687..671e55dcf4 100644
--- a/xbmc/addons/gui/GUIDialogAddonInfo.cpp
+++ b/xbmc/addons/gui/GUIDialogAddonInfo.cpp
@@ -45,6 +45,7 @@
#include "utils/log.h"
#include <functional>
+#include <memory>
#include <sstream>
#include <utility>
@@ -65,7 +66,8 @@ using namespace XFILE;
using namespace KODI::MESSAGING;
CGUIDialogAddonInfo::CGUIDialogAddonInfo(void)
- : CGUIDialog(WINDOW_DIALOG_ADDON_INFO, "DialogAddonInfo.xml"), m_item(CFileItemPtr(new CFileItem))
+ : CGUIDialog(WINDOW_DIALOG_ADDON_INFO, "DialogAddonInfo.xml"),
+ m_item(std::make_shared<CFileItem>())
{
m_loadType = KEEP_IN_MEMORY;
}
diff --git a/xbmc/addons/kodi-dev-kit/doxygen/Doxyfile b/xbmc/addons/kodi-dev-kit/doxygen/Doxyfile
index eea8a58b78..eaad4c2de8 100644
--- a/xbmc/addons/kodi-dev-kit/doxygen/Doxyfile
+++ b/xbmc/addons/kodi-dev-kit/doxygen/Doxyfile
@@ -806,6 +806,8 @@ INPUT = main.txt \
Skin/skin.dox \
../../../../cmake/scripts/common/AddonHelpers.dox \
../../../cores/RetroPlayer/guicontrols/GUIGameControl.dox \
+ ../../../games/controllers/guicontrols/GUIGameController.dox \
+ ../../../games/controllers/guicontrols/GUIGameControllerList.dox \
../../../pvr/guilib/GUIEPGGridContainer.dox \
../../../guilib/GUIButtonControl.dox \
../../../guilib/GUIColorButtonControl.dox \
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h
index b02b2b78df..4b95cb5a4e 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h
@@ -52,21 +52,21 @@ public:
{
provides_input = layout.provides_input;
for (unsigned int i = 0; i < layout.digital_button_count; ++i)
- digital_buttons.push_back(layout.digital_buttons[i]);
+ digital_buttons.emplace_back(layout.digital_buttons[i]);
for (unsigned int i = 0; i < layout.analog_button_count; ++i)
- analog_buttons.push_back(layout.analog_buttons[i]);
+ analog_buttons.emplace_back(layout.analog_buttons[i]);
for (unsigned int i = 0; i < layout.analog_stick_count; ++i)
- analog_sticks.push_back(layout.analog_sticks[i]);
+ analog_sticks.emplace_back(layout.analog_sticks[i]);
for (unsigned int i = 0; i < layout.accelerometer_count; ++i)
- accelerometers.push_back(layout.accelerometers[i]);
+ accelerometers.emplace_back(layout.accelerometers[i]);
for (unsigned int i = 0; i < layout.key_count; ++i)
- keys.push_back(layout.keys[i]);
+ keys.emplace_back(layout.keys[i]);
for (unsigned int i = 0; i < layout.rel_pointer_count; ++i)
- rel_pointers.push_back(layout.rel_pointers[i]);
+ rel_pointers.emplace_back(layout.rel_pointers[i]);
for (unsigned int i = 0; i < layout.abs_pointer_count; ++i)
- abs_pointers.push_back(layout.abs_pointers[i]);
+ abs_pointers.emplace_back(layout.abs_pointers[i]);
for (unsigned int i = 0; i < layout.motor_count; ++i)
- motors.push_back(layout.motors[i]);
+ motors.emplace_back(layout.motors[i]);
}
/*! @endcond */
@@ -201,7 +201,7 @@ public:
for (unsigned int i = 0; i < m_instanceData->props->proxy_dll_count; ++i)
{
if (m_instanceData->props->proxy_dll_paths[i] != nullptr)
- paths.push_back(m_instanceData->props->proxy_dll_paths[i]);
+ paths.emplace_back(m_instanceData->props->proxy_dll_paths[i]);
}
return !paths.empty();
}
@@ -223,7 +223,7 @@ public:
for (unsigned int i = 0; i < m_instanceData->props->resource_directory_count; ++i)
{
if (m_instanceData->props->resource_directories[i] != nullptr)
- dirs.push_back(m_instanceData->props->resource_directories[i]);
+ dirs.emplace_back(m_instanceData->props->resource_directories[i]);
}
return !dirs.empty();
}
@@ -269,7 +269,7 @@ public:
for (unsigned int i = 0; i < m_instanceData->props->extension_count; ++i)
{
if (m_instanceData->props->extensions[i] != nullptr)
- extensions.push_back(m_instanceData->props->extensions[i]);
+ extensions.emplace_back(m_instanceData->props->extensions[i]);
}
return !extensions.empty();
}
@@ -1079,7 +1079,7 @@ private:
for (size_t i = 0; i < urlCount; ++i)
{
if (urls[i] != nullptr)
- urlList.push_back(urls[i]);
+ urlList.emplace_back(urls[i]);
}
return static_cast<CInstanceGame*>(instance->toAddon->addonInstance)
@@ -1165,7 +1165,7 @@ private:
std::vector<GameControllerLayout> controllerList;
for (unsigned int i = 0; i < controller_count; ++i)
- controllerList.push_back(controllers[i]);
+ controllerList.emplace_back(controllers[i]);
static_cast<CInstanceGame*>(instance->toAddon->addonInstance)
->SetControllerLayouts(controllerList);
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h
index c435d1a24c..9c8238a225 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h
@@ -64,7 +64,7 @@ extern "C"
*/
struct PVR_HANDLE_STRUCT
{
- void* callerAddress; /*!< address of the caller */
+ const void* callerAddress; /*!< address of the caller */
void* dataAddress; /*!< address to store data in */
int dataIdentifier; /*!< parameter to pass back when calling the callback */
};
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h
index 16d43e37b4..761c5cc511 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h
@@ -95,7 +95,6 @@
#else
#if HAS_GLES == 3
#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
#else
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/versions.h b/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
index 1f887a4ebe..2ff397d9bb 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
@@ -130,7 +130,7 @@
#define ADDON_INSTANCE_VERSION_PERIPHERAL_DEPENDS "addon-instance/Peripheral.h" \
"addon-instance/PeripheralUtils.h"
-#define ADDON_INSTANCE_VERSION_PVR "8.2.0"
+#define ADDON_INSTANCE_VERSION_PVR "8.3.0"
#define ADDON_INSTANCE_VERSION_PVR_MIN "8.2.0"
#define ADDON_INSTANCE_VERSION_PVR_XML_ID "kodi.binary.instance.pvr"
#define ADDON_INSTANCE_VERSION_PVR_DEPENDS "c-api/addon-instance/pvr.h" \
diff --git a/xbmc/addons/settings/AddonSettings.cpp b/xbmc/addons/settings/AddonSettings.cpp
index cdee1b2df8..2fc9f26fae 100644
--- a/xbmc/addons/settings/AddonSettings.cpp
+++ b/xbmc/addons/settings/AddonSettings.cpp
@@ -39,6 +39,7 @@
#include <algorithm>
#include <cassert>
+#include <memory>
#include <mutex>
#include <vector>
@@ -154,11 +155,14 @@ namespace ADDON
CAddonSettings::CAddonSettings(const std::shared_ptr<IAddon>& addon, AddonInstanceId instanceId)
: CSettingsBase(),
+ m_addonId(addon->ID()),
+ m_addonPath(addon->Path()),
+ m_addonProfile(addon->Profile()),
m_instanceId(instanceId),
m_addon{addon},
m_unknownSettingLabelId(UnknownSettingLabelIdStart),
m_logger(CServiceBroker::GetLogging().GetLogger(
- StringUtils::Format("CAddonSettings[{}@{}]", m_instanceId, addon->ID())))
+ StringUtils::Format("CAddonSettings[{}@{}]", m_instanceId, m_addonId)))
{
}
@@ -173,11 +177,6 @@ std::shared_ptr<CSetting> CAddonSettings::CreateSetting(
return CSettingCreator::CreateSetting(settingType, settingId, settingsManager);
}
-std::string CAddonSettings::GetAddonId() const
-{
- return m_addon->ID();
-}
-
void CAddonSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
{
std::string actionData;
@@ -191,9 +190,9 @@ void CAddonSettings::OnSettingAction(const std::shared_ptr<const CSetting>& sett
{
actionData = settingAction->GetData();
// replace $CWD with the url of the add-on
- StringUtils::Replace(actionData, "$CWD", m_addon->Path());
+ StringUtils::Replace(actionData, "$CWD", m_addonPath);
// replace $ID with the id of the add-on
- StringUtils::Replace(actionData, "$ID", m_addon->ID());
+ StringUtils::Replace(actionData, "$ID", m_addonId);
}
}
@@ -229,7 +228,7 @@ bool CAddonSettings::AddInstanceSettings()
CLog::Log(
LOGDEBUG,
"CAddonSettings::{} - Add-on {} using instance setting values byself, Kodi's add ignored",
- __func__, m_addon->ID());
+ __func__, m_addonId);
return true;
}
@@ -351,7 +350,7 @@ bool CAddonSettings::Load(const CXBMCTinyXML& doc)
settingValue = setting->FirstChild()->ValueStr();
// add the setting to the map
- settingValues.emplace(std::make_pair(settingId, settingValue));
+ settingValues.emplace(settingId, settingValue);
};
// check if there were any setting values without a definition
@@ -438,8 +437,12 @@ bool CAddonSettings::HasSettings() const
bool CAddonSettings::Save()
{
- assert(m_addon);
- return m_addon->SaveSettings();
+ std::shared_ptr<IAddon> addon = m_addon.lock();
+ assert(addon);
+ if (addon)
+ return addon->SaveSettings();
+ else
+ return false;
}
std::string CAddonSettings::GetSettingLabel(int label) const
@@ -588,7 +591,7 @@ std::shared_ptr<CSettingGroup> CAddonSettings::ParseOldSettingElement(
category->AddGroup(group);
// and create a new one
- group.reset(new CSettingGroup(std::to_string(groupId), GetSettingsManager()));
+ group = std::make_shared<CSettingGroup>(std::to_string(groupId), GetSettingsManager());
groupId += 1;
}
@@ -812,7 +815,7 @@ bool CAddonSettings::InitializeFromOldSettingDefinitions(const CXBMCTinyXML& doc
return false;
std::shared_ptr<CSettingSection> section =
- std::make_shared<CSettingSection>(m_addon->ID(), GetSettingsManager());
+ std::make_shared<CSettingSection>(m_addonId, GetSettingsManager());
std::shared_ptr<CSettingCategory> category;
uint32_t categoryId = 0;
@@ -845,9 +848,9 @@ SettingPtr CAddonSettings::InitializeFromOldSettingAction(const std::string& set
// parse the action attribute
std::string action = XMLUtils::GetAttribute(settingElement, "action");
// replace $CWD with the url of the add-on
- StringUtils::Replace(action, "$CWD", m_addon->Path());
+ StringUtils::Replace(action, "$CWD", m_addonPath);
// replace $ID with the id of the add-on
- StringUtils::Replace(action, "$ID", m_addon->ID());
+ StringUtils::Replace(action, "$ID", m_addonId);
// prepare the setting's control
auto control = std::make_shared<CSettingControlButton>();
@@ -1103,7 +1106,7 @@ SettingPtr CAddonSettings::InitializeFromOldSettingSelect(
StringSettingOptions options;
for (const auto& value : values)
- options.push_back(StringSettingOption(value, value));
+ options.emplace_back(value, value);
settingString->SetOptions(options);
setting = settingString;
@@ -1116,8 +1119,7 @@ SettingPtr CAddonSettings::InitializeFromOldSettingSelect(
TranslatableIntegerSettingOptions options;
for (uint32_t i = 0; i < values.size(); ++i)
- options.push_back(TranslatableIntegerSettingOption(
- static_cast<int>(strtol(values[i].c_str(), nullptr, 0)), i));
+ options.emplace_back(static_cast<int>(strtol(values[i].c_str(), nullptr, 0)), i);
settingInt->SetTranslatableOptions(options);
setting = settingInt;
@@ -1260,7 +1262,7 @@ SettingPtr CAddonSettings::InitializeFromOldSettingEnums(
if (settingEntries.size() > i)
value = static_cast<int>(strtol(settingEntries[i].c_str(), nullptr, 0));
- options.push_back(IntegerSettingOption(label, value));
+ options.emplace_back(label, value);
}
settingInt->SetOptions(options);
@@ -1275,7 +1277,7 @@ SettingPtr CAddonSettings::InitializeFromOldSettingEnums(
if (settingEntries.size() > i)
value = static_cast<int>(strtol(settingEntries[i].c_str(), nullptr, 0));
- options.push_back(TranslatableIntegerSettingOption(label, value));
+ options.emplace_back(label, value);
}
settingInt->SetTranslatableOptions(options);
@@ -1303,7 +1305,7 @@ SettingPtr CAddonSettings::InitializeFromOldSettingEnums(
if (settingEntries.size() > i)
value = settingEntries[i];
- options.push_back(StringSettingOption(value, value));
+ options.emplace_back(value, value);
}
settingString->SetOptions(options);
@@ -1314,11 +1316,11 @@ SettingPtr CAddonSettings::InitializeFromOldSettingEnums(
for (uint32_t i = 0; i < values.size(); ++i)
{
int label = static_cast<int>(strtol(values[i].c_str(), nullptr, 0));
- std::string value = g_localizeStrings.GetAddonString(m_addon->ID(), label);
+ std::string value = g_localizeStrings.GetAddonString(m_addonId, label);
if (settingEntries.size() > i)
value = settingEntries[i];
- options.push_back(std::make_pair(label, value));
+ options.emplace_back(label, value);
}
settingString->SetTranslatableOptions(options);
@@ -1464,9 +1466,9 @@ SettingPtr CAddonSettings::InitializeFromOldSettingFileWithSource(
setting->SetDefault(defaultValue);
if (source.find("$PROFILE") != std::string::npos)
- StringUtils::Replace(source, "$PROFILE", m_addon->Profile());
+ StringUtils::Replace(source, "$PROFILE", m_addonProfile);
else
- source = URIUtils::AddFileToFolder(m_addon->Path(), source);
+ source = URIUtils::AddFileToFolder(m_addonPath, source);
setting->SetSources({source});
diff --git a/xbmc/addons/settings/AddonSettings.h b/xbmc/addons/settings/AddonSettings.h
index 1e23174e48..189f8a7f0e 100644
--- a/xbmc/addons/settings/AddonSettings.h
+++ b/xbmc/addons/settings/AddonSettings.h
@@ -35,9 +35,9 @@ namespace ADDON
class IAddon;
class IAddonInstanceHandler;
-class CAddonSettings : public CSettingsBase,
+class CAddonSettings : public CSettingControlCreator,
public CSettingCreator,
- public CSettingControlCreator,
+ public CSettingsBase,
public ISettingCallback
{
public:
@@ -60,7 +60,7 @@ public:
// implementation of ISettingCallback
void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
- std::string GetAddonId() const;
+ const std::string& GetAddonId() const { return m_addonId; }
bool Initialize(const CXBMCTinyXML& doc, bool allowEmpty = false);
bool Load(const CXBMCTinyXML& doc);
@@ -187,8 +187,12 @@ private:
std::string& current,
void* data);
+ // store these values so that we don't always have to access the weak pointer
+ const std::string m_addonId;
+ const std::string m_addonPath;
+ const std::string m_addonProfile;
const AddonInstanceId m_instanceId{ADDON_SETTINGS_ID};
- std::shared_ptr<IAddon> m_addon;
+ std::weak_ptr<IAddon> m_addon;
uint32_t m_unidentifiedSettingId = 0;
int m_unknownSettingLabelId;
diff --git a/xbmc/application/AppParams.h b/xbmc/application/AppParams.h
index c96b95f5b7..6999249719 100644
--- a/xbmc/application/AppParams.h
+++ b/xbmc/application/AppParams.h
@@ -53,6 +53,9 @@ public:
std::string_view GetAudioBackend() const { return m_audioBackend; }
void SetAudioBackend(std::string_view audioBackend) { m_audioBackend = audioBackend; }
+ std::string_view GetGlInterface() const { return m_glInterface; }
+ void SetGlInterface(const std::string& glInterface) { m_glInterface = glInterface; }
+
CFileItemList& GetPlaylist() const { return *m_playlist; }
/*!
@@ -86,6 +89,7 @@ private:
std::string m_windowing;
std::string m_logTarget;
std::string m_audioBackend;
+ std::string m_glInterface;
std::unique_ptr<CFileItemList> m_playlist;
diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp
index 54c2c8ad7f..0c19106f8b 100644
--- a/xbmc/application/Application.cpp
+++ b/xbmc/application/Application.cpp
@@ -10,20 +10,32 @@
#include "Autorun.h"
#include "CompileInfo.h"
+#include "DatabaseManager.h"
+#include "FileItem.h"
#include "GUIInfoManager.h"
+#include "GUILargeTextureManager.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
#include "HDRStatus.h"
#include "LangInfo.h"
+#include "PartyModeManager.h"
#include "PlayListPlayer.h"
+#include "SectionLoader.h"
+#include "SeekHandler.h"
+#include "ServiceBroker.h"
#include "ServiceManager.h"
+#include "TextureCache.h"
#include "URL.h"
#include "Util.h"
#include "addons/AddonManager.h"
+#include "addons/AddonSystemSettings.h"
#include "addons/RepositoryUpdater.h"
#include "addons/Service.h"
#include "addons/Skin.h"
#include "addons/VFSEntry.h"
#include "addons/addoninfo/AddonInfo.h"
#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIDialogAddonSettings.h"
#include "application/AppInboundProtocol.h"
#include "application/AppParams.h"
#include "application/ApplicationActionListeners.h"
@@ -33,170 +45,141 @@
#include "application/ApplicationStackHelper.h"
#include "application/ApplicationVolumeHandling.h"
#include "cores/AudioEngine/Engines/ActiveAE/ActiveAE.h"
+#include "cores/FFmpeg.h"
#include "cores/IPlayer.h"
#include "cores/playercorefactory/PlayerCoreFactory.h"
#include "dialogs/GUIDialogBusy.h"
#include "dialogs/GUIDialogCache.h"
#include "dialogs/GUIDialogKaiToast.h"
+#include "dialogs/GUIDialogSimpleMenu.h"
#include "events/EventLog.h"
#include "events/NotificationEvent.h"
+#include "filesystem/Directory.h"
+#include "filesystem/DirectoryCache.h"
#include "filesystem/DirectoryFactory.h"
+#include "filesystem/DllLibCurl.h"
#include "filesystem/File.h"
+#ifdef HAS_FILESYSTEM_NFS
+#include "filesystem/NFSFile.h"
+#endif
+#include "filesystem/PluginDirectory.h"
+#include "filesystem/SpecialProtocol.h"
+#ifdef HAS_UPNP
+#include "filesystem/UPnPDirectory.h"
+#endif
+#include "guilib/GUIAudioManager.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIControlProfiler.h"
#include "guilib/GUIFontManager.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
#include "guilib/StereoscopicsManager.h"
#include "guilib/TextureManager.h"
+#include "input/InertialScrollingHandler.h"
+#include "input/InputManager.h"
+#include "input/KeyboardLayoutManager.h"
+#include "input/actions/ActionTranslator.h"
+#include "interfaces/AnnouncementManager.h"
#include "interfaces/builtins/Builtins.h"
#include "interfaces/generic/ScriptInvocationManager.h"
-#include "music/MusicLibraryQueue.h"
-#include "music/tags/MusicInfoTag.h"
-#include "network/EventServer.h"
-#include "network/Network.h"
-#include "platform/Environment.h"
-#include "playlists/PlayListFactory.h"
-#include "threads/SystemClock.h"
-#include "utils/ContentUtils.h"
-#include "utils/JobManager.h"
-#include "utils/LangCodeExpander.h"
-#include "utils/Screenshot.h"
-#include "utils/Variant.h"
-#include "video/Bookmark.h"
-#include "video/VideoLibraryQueue.h"
-
+#include "interfaces/json-rpc/JSONRPC.h"
#ifdef HAS_PYTHON
#include "interfaces/python/XBPython.h"
#endif
-#include "GUILargeTextureManager.h"
-#include "GUIPassword.h"
-#include "GUIUserMessages.h"
-#include "SectionLoader.h"
-#include "SeekHandler.h"
-#include "ServiceBroker.h"
-#include "TextureCache.h"
-#include "filesystem/Directory.h"
-#include "filesystem/DirectoryCache.h"
-#include "filesystem/DllLibCurl.h"
-#include "filesystem/PluginDirectory.h"
-#include "filesystem/SpecialProtocol.h"
-#include "guilib/GUIAudioManager.h"
-#include "guilib/LocalizeStrings.h"
-#include "input/InertialScrollingHandler.h"
-#include "input/KeyboardLayoutManager.h"
-#include "input/actions/ActionTranslator.h"
#include "messaging/ApplicationMessenger.h"
#include "messaging/ThreadMessage.h"
#include "messaging/helpers/DialogHelper.h"
#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicLibraryQueue.h"
+#include "music/MusicThumbLoader.h"
+#include "music/MusicUtils.h"
+#include "music/infoscanner/MusicInfoScanner.h"
+#include "music/tags/MusicInfoTag.h"
+#include "network/EventServer.h"
+#include "network/Network.h"
+#include "network/ZeroconfBrowser.h"
+#ifdef HAS_UPNP
+#include "network/upnp/UPnP.h"
+#endif
+#include "peripherals/Peripherals.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "platform/Environment.h"
#include "playlists/PlayList.h"
+#include "playlists/PlayListFactory.h"
#include "playlists/SmartPlayList.h"
#include "powermanagement/PowerManager.h"
#include "profiles/ProfileManager.h"
+#include "pvr/PVRManager.h"
+#include "pvr/guilib/PVRGUIActionsPlayback.h"
+#include "pvr/guilib/PVRGUIActionsPowerManagement.h"
#include "settings/AdvancedSettings.h"
#include "settings/DisplaySettings.h"
#include "settings/MediaSettings.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "speech/ISpeechRecognition.h"
+#include "storage/MediaManager.h"
#include "threads/SingleLock.h"
+#include "threads/SystemClock.h"
+#include "utils/AlarmClock.h"
#include "utils/CPUInfo.h"
+#include "utils/CharsetConverter.h"
+#include "utils/ContentUtils.h"
#include "utils/FileExtensionProvider.h"
+#include "utils/JobManager.h"
+#include "utils/LangCodeExpander.h"
#include "utils/RegExp.h"
+#include "utils/Screenshot.h"
+#include "utils/StringUtils.h"
#include "utils/SystemInfo.h"
#include "utils/TimeUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
#include "utils/XTimeUtils.h"
#include "utils/log.h"
-#include "windowing/WinSystem.h"
-#include "windowing/WindowSystemFactory.h"
-
-#include <cmath>
-
-#ifdef HAS_UPNP
-#include "network/upnp/UPnP.h"
-#include "filesystem/UPnPDirectory.h"
-#endif
-#if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
-#include "platform/posix/filesystem/SMBFile.h"
-#endif
-#ifdef HAS_FILESYSTEM_NFS
-#include "filesystem/NFSFile.h"
-#endif
-#include "PartyModeManager.h"
-#include "network/ZeroconfBrowser.h"
-#ifndef TARGET_POSIX
-#include "platform/win32/threads/Win32Exception.h"
-#endif
-#include "interfaces/json-rpc/JSONRPC.h"
-#include "interfaces/AnnouncementManager.h"
-#include "peripherals/Peripherals.h"
-#include "music/infoscanner/MusicInfoScanner.h"
-#include "music/MusicUtils.h"
-#include "music/MusicThumbLoader.h"
-
-// Windows includes
-#include "guilib/GUIWindowManager.h"
+#include "video/Bookmark.h"
#include "video/PlayerController.h"
-
-// Dialog includes
-#include "addons/gui/GUIDialogAddonSettings.h"
-#include "dialogs/GUIDialogKaiToast.h"
-#include "dialogs/GUIDialogSimpleMenu.h"
+#include "video/VideoLibraryQueue.h"
#include "video/dialogs/GUIDialogVideoBookmarks.h"
-
-// PVR related include Files
-#include "pvr/PVRManager.h"
-#include "pvr/guilib/PVRGUIActionsPlayback.h"
-#include "pvr/guilib/PVRGUIActionsPowerManagement.h"
-
#ifdef TARGET_WINDOWS
#include "win32util.h"
#endif
+#include "windowing/WinSystem.h"
+#include "windowing/WindowSystemFactory.h"
-#ifdef TARGET_DARWIN_OSX
-#ifdef HAS_XBMCHELPER
-#include "platform/darwin/osx/XBMCHelper.h"
-#endif
+#if defined(TARGET_ANDROID)
+#include "platform/android/activity/XBMCApp.h"
#endif
#ifdef TARGET_DARWIN
#include "platform/darwin/DarwinUtils.h"
#endif
-
-#ifdef HAS_OPTICAL_DRIVE
-#include <cdio/logging.h>
+#ifdef TARGET_DARWIN_OSX
+#ifdef HAS_XBMCHELPER
+#include "platform/darwin/osx/XBMCHelper.h"
+#endif
#endif
-
-#include "DatabaseManager.h"
-#include "input/InputManager.h"
-#include "storage/MediaManager.h"
-#include "utils/AlarmClock.h"
-#include "utils/StringUtils.h"
-#include "utils/URIUtils.h"
-
#ifdef TARGET_POSIX
-#include "platform/posix/XHandle.h"
#include "platform/posix/PlatformPosix.h"
+#include "platform/posix/XHandle.h"
#endif
-
-#if defined(TARGET_ANDROID)
-#include "platform/android/activity/XBMCApp.h"
+#if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
+#include "platform/posix/filesystem/SMBFile.h"
#endif
-
-#ifdef TARGET_WINDOWS
-#include "platform/Environment.h"
+#ifndef TARGET_POSIX
+#include "platform/win32/threads/Win32Exception.h"
#endif
+#include <cmath>
+#include <memory>
+#include <mutex>
+
//TODO: XInitThreads
#ifdef HAVE_X11
#include <X11/Xlib.h>
#endif
-
-#include "FileItem.h"
-#include "addons/AddonSystemSettings.h"
-#include "cores/FFmpeg.h"
-#include "pictures/GUIWindowSlideShow.h"
-#include "utils/CharsetConverter.h"
-
-#include <mutex>
+#ifdef HAS_OPTICAL_DRIVE
+#include <cdio/logging.h>
+#endif
using namespace ADDON;
using namespace XFILE;
@@ -351,7 +334,7 @@ bool CApplication::Create()
const auto keyboardLayoutManager = std::make_shared<CKeyboardLayoutManager>();
CServiceBroker::RegisterKeyboardLayoutManager(keyboardLayoutManager);
- m_ServiceManager.reset(new CServiceManager());
+ m_ServiceManager = std::make_unique<CServiceManager>();
if (!m_ServiceManager->InitStageOne())
{
@@ -396,9 +379,19 @@ bool CApplication::Create()
if (!settingsComponent->Load())
return false;
+ // Log Cache GUI settings (replacement of cache in advancedsettings.xml)
+ const auto settings = settingsComponent->GetSettings();
+ CLog::Log(LOGINFO,
+ "New Cache GUI Settings (replacement of cache in advancedsettings.xml) are:\n - Buffer "
+ "Mode: {}\n - Memory Size: {} MB\n - Read "
+ "Factor: {:.2f} x\n - Chunk Size : {} bytes",
+ settings->GetInt(CSettings::SETTING_FILECACHE_BUFFERMODE),
+ settings->GetInt(CSettings::SETTING_FILECACHE_MEMORYSIZE),
+ settings->GetInt(CSettings::SETTING_FILECACHE_READFACTOR) / 100.0f,
+ settings->GetInt(CSettings::SETTING_FILECACHE_CHUNKSIZE));
+
CLog::Log(LOGINFO, "creating subdirectories");
const std::shared_ptr<CProfileManager> profileManager = settingsComponent->GetProfileManager();
- const std::shared_ptr<CSettings> settings = settingsComponent->GetSettings();
CLog::Log(LOGINFO, "userdata folder: {}",
CURL::GetRedacted(profileManager->GetProfileUserDataFolder()));
CLog::Log(LOGINFO, "recording folder: {}",
@@ -421,7 +414,7 @@ bool CApplication::Create()
return false;
}
- m_pActiveAE.reset(new ActiveAE::CActiveAE());
+ m_pActiveAE = std::make_unique<ActiveAE::CActiveAE>();
CServiceBroker::RegisterAE(m_pActiveAE.get());
// initialize m_replayGainSettings
@@ -562,7 +555,7 @@ bool CApplication::CreateGUI()
if (sav_res)
CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP, true);
- m_pGUI.reset(new CGUIComponent());
+ m_pGUI = std::make_unique<CGUIComponent>();
m_pGUI->Init();
// Splash requires gui component!!
@@ -2400,7 +2393,9 @@ bool CApplication::PlayFile(CFileItem item, const std::string& player, bool bRes
std::string videoInfoTagPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
if (videoInfoTagPath.find("removable://") == 0 || item.IsVideoDb())
path = videoInfoTagPath;
- dbs.LoadVideoInfo(path, *item.GetVideoInfoTag());
+
+ if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_type != MediaTypeVideoVersion)
+ dbs.LoadVideoInfo(path, *item.GetVideoInfoTag());
if (item.HasProperty("savedplayerstate"))
{
@@ -2467,9 +2462,17 @@ bool CApplication::PlayFile(CFileItem item, const std::string& player, bool bRes
if (!(options.startpercent > 0.0 || options.starttime > 0.0) &&
(item.IsBDFile() || item.IsDiscImage()))
{
- //check if we must show the simplified bd menu
- if (!CGUIDialogSimpleMenu::ShowPlaySelection(item))
- return true;
+ // No video selection when using an external player, because it needs to handle that on its own.
+ const std::string defaulPlayer{
+ player.empty() ? m_ServiceManager->GetPlayerCoreFactory().GetDefaultPlayer(item) : player};
+ const bool isExternalPlayer{
+ m_ServiceManager->GetPlayerCoreFactory().IsExternalPlayer(defaulPlayer)};
+ if (!isExternalPlayer)
+ {
+ // Check if we must show the simplified bd menu.
+ if (!CGUIDialogSimpleMenu::ShowPlaySelection(item))
+ return true;
+ }
}
// this really aught to be inside !bRestart, but since PlayStack
@@ -2698,9 +2701,6 @@ bool CApplication::OnMessage(CGUIMessage& message)
m_incompatibleAddons.clear();
}
- // show info dialog about moved configuration files if needed
- ShowAppMigrationMessage();
-
// offer enabling addons at kodi startup that are disabled due to
// e.g. os package manager installation on linux
ConfigureAndEnableAddons();
@@ -2747,7 +2747,7 @@ bool CApplication::OnMessage(CGUIMessage& message)
CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_CHANGED, 0, 0, CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist(), param, item);
CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
CServiceBroker::GetPlaylistPlayer().SetCurrentSong(m_nextPlaylistItem);
- m_itemCurrentFile.reset(new CFileItem(*item));
+ m_itemCurrentFile = std::make_shared<CFileItem>(*item);
}
CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
g_partyModeManager.OnSongChange(true);
@@ -3000,26 +3000,6 @@ bool CApplication::ExecuteXBMCAction(std::string actionStr, const CGUIListItemPt
return true;
}
-// inform the user that the configuration data has moved from old XBMC location
-// to new Kodi location - if applicable
-void CApplication::ShowAppMigrationMessage()
-{
- // .kodi_migration_complete will be created from the installer/packaging
- // once an old XBMC configuration was moved to the new Kodi location
- // if this is the case show the migration info to the user once which
- // tells him to have a look into the wiki where the move of configuration
- // is further explained.
- if (CFile::Exists("special://home/.kodi_data_was_migrated") &&
- !CFile::Exists("special://home/.kodi_migration_info_shown"))
- {
- HELPERS::ShowOKDialogText(CVariant{24128}, CVariant{24129});
- CFile tmpFile;
- // create the file which will prevent this dialog from appearing in the future
- tmpFile.OpenForWrite("special://home/.kodi_migration_info_shown");
- tmpFile.Close();
- }
-}
-
void CApplication::ConfigureAndEnableAddons()
{
std::vector<std::shared_ptr<IAddon>>
@@ -3127,16 +3107,6 @@ void CApplication::ProcessSlow()
CServiceBroker::GetPowerManager().ProcessEvents();
-#if defined(TARGET_DARWIN_OSX) && defined(SDL_FOUND)
- // There is an issue on OS X that several system services ask the cursor to become visible
- // during their startup routines. Given that we can't control this, we hack it in by
- // forcing the
- if (CServiceBroker::GetWinSystem()->IsFullScreen())
- { // SDL thinks it's hidden
- Cocoa_HideMouse();
- }
-#endif
-
// Temporarily pause pausable jobs when viewing video/picture
int currentWindow = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
if (CurrentFileItem().IsVideo() ||
diff --git a/xbmc/application/ApplicationPlayerCallback.cpp b/xbmc/application/ApplicationPlayerCallback.cpp
index 0dbb4b9fe5..262a106e01 100644
--- a/xbmc/application/ApplicationPlayerCallback.cpp
+++ b/xbmc/application/ApplicationPlayerCallback.cpp
@@ -35,6 +35,8 @@
#include "video/VideoDatabase.h"
#include "video/VideoInfoTag.h"
+#include <memory>
+
CApplicationPlayerCallback::CApplicationPlayerCallback()
: m_itemCurrentFile(new CFileItem), m_playerEvent(true, true)
{
@@ -77,9 +79,9 @@ void CApplicationPlayerCallback::OnPlayBackStarted(const CFileItem& file)
auto& components = CServiceBroker::GetAppComponents();
const auto stackHelper = components.GetComponent<CApplicationStackHelper>();
if (stackHelper->IsPlayingISOStack() || stackHelper->IsPlayingRegularStack())
- m_itemCurrentFile.reset(new CFileItem(*stackHelper->GetRegisteredStack(file)));
+ m_itemCurrentFile = std::make_shared<CFileItem>(*stackHelper->GetRegisteredStack(file));
else
- m_itemCurrentFile.reset(new CFileItem(file));
+ m_itemCurrentFile = std::make_shared<CFileItem>(file);
/* When playing video pause any low priority jobs, they will be unpaused when playback stops.
* This should speed up player startup for files on internet filesystems (eg. webdav) and
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
index 8bd9e45a76..908b087b98 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
@@ -8,10 +8,6 @@
#include "ActiveAE.h"
-#include <mutex>
-
-using namespace AE;
-using namespace ActiveAE;
#include "ActiveAESettings.h"
#include "ActiveAESound.h"
#include "ActiveAEStream.h"
@@ -27,6 +23,12 @@ using namespace ActiveAE;
#include "utils/log.h"
#include "windowing/WinSystem.h"
+#include <memory>
+#include <mutex>
+
+using namespace AE;
+using namespace ActiveAE;
+
using namespace std::chrono_literals;
namespace
@@ -288,7 +290,7 @@ CActiveAE::CActiveAE() :
m_stats.Reset(44100, true);
m_streamIdGen = 0;
- m_settingsHandler.reset(new CActiveAESettings(*this));
+ m_settingsHandler = std::make_unique<CActiveAESettings>(*this);
}
CActiveAE::~CActiveAE()
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
index 3ef7c5d52d..8e4d957cea 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
@@ -13,6 +13,8 @@
#include "cores/AudioEngine/AEResampleFactory.h"
#include "cores/AudioEngine/Utils/AEUtil.h"
+#include <memory>
+
using namespace ActiveAE;
CSoundPacket::CSoundPacket(const SampleConfig& conf, int samples) : config(conf)
@@ -467,7 +469,7 @@ bool CActiveAEBufferPoolAtempo::Create(unsigned int totaltime)
{
CActiveAEBufferPool::Create(totaltime);
- m_pTempoFilter.reset(new CActiveAEFilter());
+ m_pTempoFilter = std::make_unique<CActiveAEFilter>();
m_pTempoFilter->Init(CAEUtil::GetAVSampleFormat(m_format.m_dataFormat), m_format.m_sampleRate, CAEUtil::GetAVChannelLayout(m_format.m_channelLayout));
return true;
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
index 54d46e3fd8..96d8def6b0 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
@@ -872,7 +872,7 @@ void CActiveAESink::EnumerateOutputDevices(AEDeviceList &devices, bool passthrou
if (!devInfo.m_displayNameExtra.empty())
ss << ", " << devInfo.m_displayNameExtra;
- devices.push_back(AEDevice(ss.str(), device));
+ devices.emplace_back(ss.str(), device);
}
}
}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp
index 587406f79f..c6763bae13 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPULSE.cpp
@@ -16,6 +16,7 @@
#include "utils/log.h"
#include <array>
+#include <memory>
#include <mutex>
//-----------------------------------------------------------------------------
@@ -706,7 +707,7 @@ bool CAESinkPULSE::Register()
pa_simple_free(s);
}
- m_pMonitor.reset(new CDriverMonitor());
+ m_pMonitor = std::make_unique<CDriverMonitor>();
m_pMonitor->Start();
AE::AESinkRegEntry entry;
diff --git a/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp b/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp
index 75952610f3..aabc0eabe9 100644
--- a/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp
@@ -220,11 +220,11 @@ CADeviceList AEDeviceEnumerationOSX::GetDeviceInfoList() const
deviceInfo.m_deviceName = getDeviceNameForStream(streamIdx) + ":source" + sourceIdxStr.str();
deviceInfo.m_displayNameExtra = m_caDevice.GetDataSourceName(sourceList[sourceIdx]);
devInstance.sourceId = sourceList[sourceIdx];
- list.push_back(std::make_pair(devInstance, deviceInfo));
+ list.emplace_back(devInstance, deviceInfo);
}
}
else
- list.push_back(std::make_pair(devInstance, deviceInfo));
+ list.emplace_back(devInstance, deviceInfo);
}
return list;
}
diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.cpp b/xbmc/cores/RetroPlayer/RetroPlayer.cpp
index def0e7d945..e80987a701 100644
--- a/xbmc/cores/RetroPlayer/RetroPlayer.cpp
+++ b/xbmc/cores/RetroPlayer/RetroPlayer.cpp
@@ -50,6 +50,7 @@
#include "utils/log.h"
#include "windowing/WinSystem.h"
+#include <memory>
#include <mutex>
using namespace KODI;
@@ -100,7 +101,7 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options
m_processInfo->ResetInfo();
m_guiMessenger = std::make_unique<CGUIGameMessenger>(*m_processInfo);
- m_renderManager.reset(new CRPRenderManager(*m_processInfo));
+ m_renderManager = std::make_unique<CRPRenderManager>(*m_processInfo);
std::unique_lock<CCriticalSection> lock(m_mutex);
@@ -128,7 +129,7 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options
m_gameClient = std::static_pointer_cast<CGameClient>(addon);
if (m_gameClient->Initialize())
{
- m_streamManager.reset(new CRPStreamManager(*m_renderManager, *m_processInfo));
+ m_streamManager = std::make_unique<CRPStreamManager>(*m_renderManager, *m_processInfo);
// Initialize input
m_input = std::make_unique<CRetroPlayerInput>(CServiceBroker::GetPeripherals(),
@@ -196,11 +197,11 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options
// Initialize gameplay
CreatePlayback(savestatePath);
RegisterWindowCallbacks();
- m_playbackControl.reset(new CGUIPlaybackControl(*this));
+ m_playbackControl = std::make_unique<CGUIPlaybackControl>(*this);
m_callback.OnPlayBackStarted(fileCopy);
m_callback.OnAVStarted(fileCopy);
if (!bStandalone)
- m_autoSave.reset(new CRetroPlayerAutoSave(*this, m_gameServices.GameSettings()));
+ m_autoSave = std::make_unique<CRetroPlayerAutoSave>(*this, m_gameServices.GameSettings());
// Set video framerate
m_processInfo->SetVideoFps(static_cast<float>(m_gameClient->GetFrameRate()));
@@ -648,7 +649,7 @@ void CRetroPlayer::ResetPlayback()
if (m_playback)
m_playback->Deinitialize();
- m_playback.reset(new CRealtimePlayback);
+ m_playback = std::make_unique<CRealtimePlayback>();
}
void CRetroPlayer::OpenOSD()
diff --git a/xbmc/cores/RetroPlayer/guicontrols/GUIGameControl.dox b/xbmc/cores/RetroPlayer/guicontrols/GUIGameControl.dox
index 6350110d30..740a0b911b 100644
--- a/xbmc/cores/RetroPlayer/guicontrols/GUIGameControl.dox
+++ b/xbmc/cores/RetroPlayer/guicontrols/GUIGameControl.dox
@@ -2,7 +2,7 @@
\page Game_Control Game Control
\brief **Used to display the currently playing game, with optional effects,
-whilst in the GUI.**
+while playing a game.**
\tableofcontents
@@ -22,7 +22,9 @@ game is being played.
<posy>60</posy>
<width>250</width>
<height>200</height>
- <visible>true</visible>
+ <videofilter>nearest</videofilter>
+ <stretchmode>normal</stretchmode>
+ <rotation>0</rotation>
</control>
~~~~~~~~~~~~~
@@ -30,12 +32,52 @@ game is being played.
--------------------------------------------------------------------------------
\section Game_Control_sect2 Available tags
-The [default control](http://kodi.wiki/view/Default_Control_Tags) tags are
-applicable to this control.
+In addition to the [Default Control Tags](http://kodi.wiki/view/Default_Control_Tags)
+the following tags are available. Note that each tag is lower case only. This is
+important, as xml tags are case-sensitive.
+
+| Tag | Description |
+|--------------:|:--------------------------------------------------------------|
+| videofilter | The filter to use, see \ref RetroPlayer_VideoFilter "RetroPlayer.VideoFilter" for supported values. If empty, the value of the \ref RetroPlayer_VideoFilter "RetroPlayer.VideoFilter" info label is used.
+| stretchmode | The stretch mode, see \ref RetroPlayer_StretchMode "RetroPlayer.StretchMode" for supported values. If empty, the value of the \ref RetroPlayer_StretchMode "RetroPlayer.StretchMode" info label is used.
+| rotation | The rotation, in degrees counter-clockwise, see \ref RetroPlayer_VideoRotation "RetroPlayer.VideoRotation" for supported values. If empty, the value of the \ref RetroPlayer_VideoRotation "RetroPlayer.VideoRotation" info label is used.
+| pixels | A path to a v20 savestate, whose pixels will be rendered instead of the active game. A value of <b>`-`</b> will disable rendering of any pixels.
+
+@skinning_v20 <b>pixels</b> New tag added for rendering savestate pixels. Requires savestate v3 schema, introduced with the Savestate Manager in v20.
+
+
+--------------------------------------------------------------------------------
+\section Game_Control_sect3 List item info
+
+List item info can be used for all tag values. For example, if the control defintion looks like:
+
+~~~~~~~~~~~~~
+<itemlayout width="444" height="250">
+ <control type="gamewindow">
+ <videofilter>$INFO[ListItem.Property(game.videofilter)]</videofilter>
+ <stretchmode>$INFO[ListItem.Property(game.stretchmode)]</stretchmode>
+ <rotation>$INFO[ListItem.Property(game.videorotation)]</rotation>
+ </control>
+</itemlayout>
+~~~~~~~~~~~~~
+
+Static list items can be provided. Each gamewindow will inherit the properties:
+
+~~~~~~~~~~~~~
+<content>
+ <item>
+ <property name="game.videofilter">nearest</property>
+ <property name="game.stretchmode">normal</property>
+ <property name="game.videorotation">0</property>
+ </item>
+</content>
+~~~~~~~~~~~~~
+
+The in-game dialogs with a gamewindow control (<b>`GameVideoFilter`</b>, <b>`GameStretchMode`</b>, <b>`GameVideoRotation`</b> and <b>`InGameSaves`</b>) use a similar strategy with list items populated by core.
--------------------------------------------------------------------------------
-\section Game_Control_sect3 See also
+\section Game_Control_sect4 See also
#### Development:
diff --git a/xbmc/cores/RetroPlayer/playback/ReversiblePlayback.cpp b/xbmc/cores/RetroPlayer/playback/ReversiblePlayback.cpp
index 8148dfd58a..16a0a9369b 100644
--- a/xbmc/cores/RetroPlayer/playback/ReversiblePlayback.cpp
+++ b/xbmc/cores/RetroPlayer/playback/ReversiblePlayback.cpp
@@ -26,6 +26,7 @@
#include "utils/log.h"
#include <algorithm>
+#include <memory>
#include <mutex>
using namespace KODI;
@@ -414,7 +415,7 @@ void CReversiblePlayback::UpdateMemoryStream()
if (!m_memoryStream)
{
- m_memoryStream.reset(new CDeltaPairMemoryStream);
+ m_memoryStream = std::make_unique<CDeltaPairMemoryStream>();
m_memoryStream->Init(m_gameClient->SerializeSize(), frameCount);
}
diff --git a/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp b/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp
index c1fee544d7..7864a15261 100644
--- a/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp
+++ b/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp
@@ -20,6 +20,8 @@
#include "utils/URIUtils.h"
#include "utils/log.h"
+#include <memory>
+
namespace
{
constexpr auto SAVESTATE_EXTENSION = ".sav";
@@ -35,7 +37,7 @@ std::unique_ptr<ISavestate> CSavestateDatabase::AllocateSavestate()
{
std::unique_ptr<ISavestate> savestate;
- savestate.reset(new CSavestateFlatBuffer);
+ savestate = std::make_unique<CSavestateFlatBuffer>();
return savestate;
}
diff --git a/xbmc/cores/RetroPlayer/savestates/SavestateFlatBuffer.cpp b/xbmc/cores/RetroPlayer/savestates/SavestateFlatBuffer.cpp
index 9b98bde9ce..e9f19ca30a 100644
--- a/xbmc/cores/RetroPlayer/savestates/SavestateFlatBuffer.cpp
+++ b/xbmc/cores/RetroPlayer/savestates/SavestateFlatBuffer.cpp
@@ -13,6 +13,8 @@
#include "utils/log.h"
#include "video_generated.h"
+#include <memory>
+
using namespace KODI;
using namespace RETRO;
@@ -192,7 +194,7 @@ CSavestateFlatBuffer::~CSavestateFlatBuffer() = default;
void CSavestateFlatBuffer::Reset()
{
- m_builder.reset(new flatbuffers::FlatBufferBuilder(INITIAL_FLATBUFFER_SIZE));
+ m_builder = std::make_unique<flatbuffers::FlatBufferBuilder>(INITIAL_FLATBUFFER_SIZE);
m_data.clear();
m_savestate = nullptr;
}
@@ -252,7 +254,7 @@ std::string CSavestateFlatBuffer::Label() const
void CSavestateFlatBuffer::SetLabel(const std::string& label)
{
- m_labelOffset.reset(new StringOffset{m_builder->CreateString(label)});
+ m_labelOffset = std::make_unique<StringOffset>(m_builder->CreateString(label));
}
std::string CSavestateFlatBuffer::Caption() const
@@ -298,7 +300,7 @@ std::string CSavestateFlatBuffer::GameFileName() const
void CSavestateFlatBuffer::SetGameFileName(const std::string& gameFileName)
{
- m_gameFileNameOffset.reset(new StringOffset{m_builder->CreateString(gameFileName)});
+ m_gameFileNameOffset = std::make_unique<StringOffset>(m_builder->CreateString(gameFileName));
}
uint64_t CSavestateFlatBuffer::TimestampFrames() const
@@ -336,7 +338,7 @@ std::string CSavestateFlatBuffer::GameClientID() const
void CSavestateFlatBuffer::SetGameClientID(const std::string& gameClientId)
{
- m_emulatorAddonIdOffset.reset(new StringOffset{m_builder->CreateString(gameClientId)});
+ m_emulatorAddonIdOffset = std::make_unique<StringOffset>(m_builder->CreateString(gameClientId));
}
std::string CSavestateFlatBuffer::GameClientVersion() const
@@ -351,7 +353,8 @@ std::string CSavestateFlatBuffer::GameClientVersion() const
void CSavestateFlatBuffer::SetGameClientVersion(const std::string& gameClientVersion)
{
- m_emulatorVersionOffset.reset(new StringOffset{m_builder->CreateString(gameClientVersion)});
+ m_emulatorVersionOffset =
+ std::make_unique<StringOffset>(m_builder->CreateString(gameClientVersion));
}
AVPixelFormat CSavestateFlatBuffer::GetPixelFormat() const
@@ -517,8 +520,8 @@ uint8_t* CSavestateFlatBuffer::GetMemoryBuffer(size_t size)
{
uint8_t* memoryBuffer = nullptr;
- m_memoryDataOffset.reset(
- new VectorOffset{m_builder->CreateUninitializedVector(size, &memoryBuffer)});
+ m_memoryDataOffset =
+ std::make_unique<VectorOffset>(m_builder->CreateUninitializedVector(size, &memoryBuffer));
return memoryBuffer;
}
diff --git a/xbmc/cores/VideoPlayer/DVDClock.cpp b/xbmc/cores/VideoPlayer/DVDClock.cpp
index cd2d024dd7..d27c956ef3 100644
--- a/xbmc/cores/VideoPlayer/DVDClock.cpp
+++ b/xbmc/cores/VideoPlayer/DVDClock.cpp
@@ -16,6 +16,7 @@
#include <inttypes.h>
#include <math.h>
+#include <memory>
#include <mutex>
CDVDClock::CDVDClock()
@@ -34,7 +35,7 @@ CDVDClock::CDVDClock()
m_vSyncAdjust = 0;
m_frameTime = DVD_TIME_BASE / 60.0;
- m_videoRefClock.reset(new CVideoReferenceClock());
+ m_videoRefClock = std::make_unique<CVideoReferenceClock>();
m_lastSystemTime = m_videoRefClock->GetTime();
m_systemOffset = m_videoRefClock->GetTime();
m_systemFrequency = CurrentHostFrequency();
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
index 24659d7582..3c55a7b06c 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h
@@ -53,7 +53,8 @@ public:
AVColorSpace color_space;
unsigned int color_range : 1; //< 1 indicate if we have a full range of color
AVChromaLocation chroma_position;
- AVColorPrimaries color_primaries;
+ AVColorPrimaries color_primaries; // heuristics applied when original is AVCOL_PRI_UNSPECIFIED
+ AVColorPrimaries m_originalColorPrimaries;
AVColorTransferCharacteristic color_transfer;
unsigned int colorBits = 8;
std::string stereoMode;
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
index 68203861fc..4dd2a9d71f 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
@@ -68,24 +68,6 @@ enum MEDIACODEC_STATES
MEDIACODEC_STATE_STOPPED
};
-static bool IsSupportedColorFormat(int color_format)
-{
- static const int supported_colorformats[] = {
- CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar,
- CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar,
- CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar,
- CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar,
- CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace,
- -1
- };
- for (const int *ptr = supported_colorformats; *ptr != -1; ptr++)
- {
- if (color_format == *ptr)
- return true;
- }
- return false;
-}
-
/*****************************************************************************/
/*****************************************************************************/
class CDVDMediaCodecOnFrameAvailable : public CEvent,
@@ -516,6 +498,10 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
m_mime = "video/hevc";
m_formatname = "amc-hevc";
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const bool convertDovi =
+ (settings) ? settings->GetBool(CSettings::SETTING_VIDEOPLAYER_CONVERTDOVI) : false;
+
bool isDvhe = (m_hints.codec_tag == MKTAG('d', 'v', 'h', 'e'));
bool isDvh1 = (m_hints.codec_tag == MKTAG('d', 'v', 'h', '1'));
@@ -548,6 +534,46 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
m_mime = "video/dolby-vision";
m_formatname = isDvhe ? "amc-dvhe" : "amc-dvh1";
profile = 0; // not an HEVC profile
+
+ if (CJNIBase::GetSDKVersion() >= 24)
+ {
+ switch (m_hints.dovi.dv_profile)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 6:
+ // obsolete profiles that are not supported in current applications.
+ // 0 is ignored in case the AVDOVIDecoderConfigurationRecord hint is unset.
+ break;
+ case 4:
+ profile = CJNIMediaCodecInfoCodecProfileLevel::DolbyVisionProfileDvheDtr;
+ break;
+ case 5:
+ profile = CJNIMediaCodecInfoCodecProfileLevel::DolbyVisionProfileDvheStn;
+ break;
+ case 7:
+ // set profile 8 when converting
+ if (convertDovi && CJNIBase::GetSDKVersion() >= 27)
+ profile = CJNIMediaCodecInfoCodecProfileLevel::DolbyVisionProfileDvheSt;
+
+ // Profile 7 is not commonly supported. Not setting the profile here
+ // allows to pick the first available Dolby Vision codec.
+ // profile = CJNIMediaCodecInfoCodecProfileLevel::DolbyVisionProfileDvheDtb;
+ break;
+ case 8:
+ if (CJNIBase::GetSDKVersion() >= 27)
+ profile = CJNIMediaCodecInfoCodecProfileLevel::DolbyVisionProfileDvheSt;
+ break;
+ case 9:
+ if (CJNIBase::GetSDKVersion() >= 27)
+ profile = CJNIMediaCodecInfoCodecProfileLevel::DolbyVisionProfileDvavSe;
+ break;
+ default:
+ break;
+ }
+ }
}
}
@@ -564,9 +590,6 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
// Only set for profile 7, container hint allows to skip parsing unnecessarily
if (m_bitstream && m_hints.dovi.dv_profile == 7)
{
- bool convertDovi = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
- CSettings::SETTING_VIDEOPLAYER_CONVERTDOVI);
-
CLog::Log(LOGDEBUG,
"CDVDVideoCodecAndroidMediaCodec::Open Dolby Vision compatibility mode "
"enabled: {}",
@@ -713,7 +736,6 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
}
m_codec = nullptr;
- m_colorFormat = -1;
codecInfos = CJNIMediaCodecList(CJNIMediaCodecList::REGULAR_CODECS).getCodecInfos();
for (const CJNIMediaCodecInfo& codec_info : codecInfos)
@@ -751,8 +773,6 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
continue;
}
- std::vector<int> color_formats = codec_caps.colorFormats();
-
if (profile)
{
std::vector<CJNIMediaCodecInfoCodecProfileLevel> profileLevels = codec_caps.profileLevels();
@@ -786,16 +806,6 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open cannot create codec");
continue;
}
-
- for (size_t k = 0; k < color_formats.size(); ++k)
- {
- CLog::Log(LOGDEBUG,
- "CDVDVideoCodecAndroidMediaCodec::Open "
- "m_codecname({}), colorFormat({})",
- m_codecname, color_formats[k]);
- if (IsSupportedColorFormat(color_formats[k]))
- m_colorFormat = color_formats[k]; // Save color format for initial output configuration
- }
break;
}
}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h
index 4192459cc4..a672b8c9e0 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h
@@ -151,7 +151,6 @@ protected:
CDVDStreamInfo m_hints;
std::string m_mime;
std::string m_codecname;
- int m_colorFormat;
std::string m_formatname;
bool m_opened = false;
bool m_needSecureDecoder = false;
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
index 5d1b7162f9..d974171346 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
@@ -1025,6 +1025,7 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(VideoPicture* pVideoPicture)
pVideoPicture->chroma_position = m_pCodecContext->chroma_sample_location;
pVideoPicture->color_primaries = m_pCodecContext->color_primaries == AVCOL_PRI_UNSPECIFIED ? m_hints.colorPrimaries : m_pCodecContext->color_primaries;
+ pVideoPicture->m_originalColorPrimaries = pVideoPicture->color_primaries;
pVideoPicture->color_transfer = m_pCodecContext->color_trc == AVCOL_TRC_UNSPECIFIED ? m_hints.colorTransferCharacteristic : m_pCodecContext->color_trc;
pVideoPicture->color_space = m_pCodecContext->colorspace == AVCOL_SPC_UNSPECIFIED ? m_hints.colorSpace : m_pCodecContext->colorspace;
pVideoPicture->colorBits = 8;
@@ -1148,6 +1149,14 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(VideoPicture* pVideoPicture)
m_requestSkipDeint = false;
pVideoPicture->iFlags |= m_codecControlFlags;
+ if (pVideoPicture->color_primaries == AVCOL_PRI_UNSPECIFIED)
+ {
+ if (pVideoPicture->iDisplayWidth > 1024 || pVideoPicture->iDisplayHeight >= 600)
+ pVideoPicture->color_primaries = AVCOL_PRI_BT709;
+ else
+ pVideoPicture->color_primaries = AVCOL_PRI_BT470BG;
+ }
+
return true;
}
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.cpp
index c393fd6b6d..f919443451 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.cpp
@@ -28,6 +28,7 @@
#include <memory>
#include <vector>
+#include <appswitching-control-block/AcbAPI.h>
#include <player-factory/custompipeline.hpp>
#include <player-factory/customplayer.hpp>
@@ -47,6 +48,20 @@ constexpr unsigned int MAX_SRC_BUFFER_LEVEL = 8 * 1024 * 1024; // 8 MB
CDVDVideoCodecStarfish::CDVDVideoCodecStarfish(CProcessInfo& processInfo)
: CDVDVideoCodec(processInfo), m_starfishMediaAPI(std::make_unique<StarfishMediaAPIs>())
{
+ using namespace KODI::WINDOWING::WAYLAND;
+ auto winSystem = static_cast<CWinSystemWaylandWebOS*>(CServiceBroker::GetWinSystem());
+ if (!winSystem->SupportsExportedWindow())
+ {
+ m_acbId = AcbAPI_create();
+ if (m_acbId)
+ {
+ if (!AcbAPI_initialize(m_acbId, PLAYER_TYPE_MSE, getenv("APPID"), &AcbCallback))
+ {
+ AcbAPI_destroy(m_acbId);
+ m_acbId = 0;
+ }
+ }
+ }
}
CDVDVideoCodecStarfish::~CDVDVideoCodecStarfish()
@@ -200,10 +215,12 @@ bool CDVDVideoCodecStarfish::OpenInternal(CDVDStreamInfo& hints, CDVDCodecOption
using namespace KODI::WINDOWING::WAYLAND;
auto winSystem = static_cast<CWinSystemWaylandWebOS*>(CServiceBroker::GetWinSystem());
- std::string exportedWindowName = winSystem->GetExportedWindowName();
-
payloadArg["mediaTransportType"] = "BUFFERSTREAM";
- payloadArg["option"]["windowId"] = exportedWindowName;
+ if (winSystem->SupportsExportedWindow())
+ {
+ std::string exportedWindowName = winSystem->GetExportedWindowName();
+ payloadArg["option"]["windowId"] = exportedWindowName;
+ }
payloadArg["option"]["appId"] = CCompileInfo::GetPackage();
payloadArg["option"]["externalStreamingInfo"]["contents"]["codec"]["video"] = m_codecname;
payloadArg["option"]["externalStreamingInfo"]["contents"]["esInfo"]["pauseAtDecodeTime"] = true;
@@ -257,7 +274,9 @@ bool CDVDVideoCodecStarfish::OpenInternal(CDVDStreamInfo& hints, CDVDCodecOption
m_videobuffer.iDisplayWidth = m_hints.width;
m_videobuffer.iDisplayHeight = m_hints.height;
m_videobuffer.stereoMode = m_hints.stereo_mode;
- m_videobuffer.videoBuffer = new CStarfishVideoBuffer(0);
+ CStarfishVideoBuffer* starfishVideoBuffer = new CStarfishVideoBuffer(0);
+ m_videobuffer.videoBuffer = starfishVideoBuffer;
+ starfishVideoBuffer->m_acbId = m_acbId;
m_opened = true;
@@ -280,6 +299,12 @@ void CDVDVideoCodecStarfish::Dispose()
m_starfishMediaAPI->Unload();
+ if (m_acbId)
+ {
+ AcbAPI_finalize(m_acbId);
+ AcbAPI_destroy(m_acbId);
+ }
+
ms_instanceGuard.exchange(false);
}
@@ -533,17 +558,43 @@ void CDVDVideoCodecStarfish::PlayerCallback(const int32_t type,
case PF_EVENT_TYPE_STR_RESOURCE_INFO:
m_newFrame = true;
break;
+ case PF_EVENT_TYPE_STR_VIDEO_INFO:
+ if (m_acbId)
+ AcbAPI_setMediaVideoData(m_acbId, logstr.c_str());
+ break;
case PF_EVENT_TYPE_STR_STATE_UPDATE__LOADCOMPLETED:
+ if (m_acbId)
+ {
+ AcbAPI_setSinkType(m_acbId, SINK_TYPE_MAIN);
+ AcbAPI_setMediaId(m_acbId, m_starfishMediaAPI->getMediaID());
+ AcbAPI_setState(m_acbId, APPSTATE_FOREGROUND, PLAYSTATE_LOADED, nullptr);
+ }
m_starfishMediaAPI->Play();
m_state = StarfishState::FLUSHED;
break;
+ case PF_EVENT_TYPE_STR_STATE_UPDATE__PLAYING:
+ if (m_acbId)
+ AcbAPI_setState(m_acbId, APPSTATE_FOREGROUND, PLAYSTATE_PLAYING, nullptr);
+ break;
+ case PF_EVENT_TYPE_STR_STATE_UPDATE__PAUSED:
+ if (m_acbId)
+ AcbAPI_setState(m_acbId, APPSTATE_FOREGROUND, PLAYSTATE_PAUSED, nullptr);
+ break;
case PF_EVENT_TYPE_STR_STATE_UPDATE__ENDOFSTREAM:
m_state = StarfishState::EOS;
break;
+ case PF_EVENT_TYPE_STR_STATE_UPDATE__UNLOADCOMPLETED:
+ if (m_acbId)
+ AcbAPI_setState(m_acbId, APPSTATE_FOREGROUND, PLAYSTATE_UNLOADED, nullptr);
+ break;
case PF_EVENT_TYPE_INT_ERROR:
+ CLog::LogF(LOGERROR, "CDVDVideoCodecStarfish: Pipeline INT_ERROR numValue: {}, strValue: {}",
+ numValue, logstr);
+ m_state = StarfishState::ERROR;
+ break;
case PF_EVENT_TYPE_STR_ERROR:
- CLog::LogF(LOGERROR, "CDVDVideoCodecStarfish: Pipeline error numValue: {}, strValue: {}",
- type, numValue, logstr);
+ CLog::LogF(LOGERROR, "CDVDVideoCodecStarfish: Pipeline STR_ERROR numValue: {}, strValue: {}",
+ numValue, logstr);
m_state = StarfishState::ERROR;
break;
}
@@ -556,3 +607,11 @@ void CDVDVideoCodecStarfish::PlayerCallback(const int32_t type,
{
static_cast<CDVDVideoCodecStarfish*>(data)->PlayerCallback(type, numValue, strValue);
}
+
+void CDVDVideoCodecStarfish::AcbCallback(
+ long acbId, long taskId, long eventType, long appState, long playState, const char* reply)
+{
+ CLog::LogF(LOGDEBUG,
+ "ACB callback: acbId={}, taskId={}, eventType={}, appState={}, playState={}, reply={}",
+ acbId, taskId, eventType, appState, playState, reply);
+} \ No newline at end of file
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.h
index ce24b1fce9..1ee1fb94b1 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.h
@@ -25,6 +25,7 @@ class CStarfishVideoBuffer : public CVideoBuffer
public:
explicit CStarfishVideoBuffer(int id) : CVideoBuffer(id) {}
AVPixelFormat GetFormat() override { return AV_PIX_FMT_NONE; }
+ long m_acbId{0};
};
enum class StarfishState
@@ -95,7 +96,10 @@ private:
const int64_t numValue,
const char* strValue,
void* data);
+ static void AcbCallback(
+ long acbId, long taskId, long eventType, long appState, long playState, const char* reply);
std::unique_ptr<StarfishMediaAPIs> m_starfishMediaAPI;
+ long m_acbId{0};
CDVDStreamInfo m_hints;
std::string m_codecname;
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
index f4fbc6be30..7236b549ff 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
@@ -1114,15 +1114,6 @@ bool CVideoBufferPool::HasFree()
return !m_freeViews.empty();
}
-bool CVideoBufferPool::HasRefs()
-{
- std::unique_lock<CCriticalSection> lock(m_section);
- // out buffers hold views
- const size_t buffRefs = m_out.size() - m_freeOut.size();
- // ffmpeg refs = total - free - out refs
- return m_freeViews.size() != m_views.size() - buffRefs;
-}
-
//-----------------------------------------------------------------------------
// DXVA::CDecoder
//-----------------------------------------------------------------------------
@@ -1166,15 +1157,6 @@ CDecoder::~CDecoder()
av_freep(&m_avD3D11Context);
}
-long CDecoder::Release()
-{
- // if ffmpeg holds any references, flush buffers
- if (m_bufferPool && m_bufferPool->HasRefs())
- avcodec_flush_buffers(m_avCtx);
-
- return IHardwareDecoder::Release();
-}
-
void CDecoder::Close()
{
std::unique_lock<CCriticalSection> lock(m_section);
@@ -1443,8 +1425,6 @@ bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, enum AVPixel
mainctx->hwaccel_context = m_avD3D11Context;
mainctx->slice_flags = SLICE_FLAG_ALLOW_FIELD | SLICE_FLAG_CODED_ORDER;
- m_avCtx = mainctx;
-
if (m_format.Guid == DXVADDI_Intel_ModeH264_E)
{
#ifdef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO
@@ -1693,7 +1673,7 @@ void CDecoder::ReleaseBuffer(uint8_t* data)
CLog::LogF(LOGWARNING, "return of invalid surface.");
}
- IHardwareDecoder::Release();
+ Release();
}
int CDecoder::FFGetBuffer(AVCodecContext* avctx, AVFrame* pic, int flags)
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.h
index f633eead60..ec099831e7 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.h
@@ -177,7 +177,6 @@ public:
bool IsValid(ID3D11View* view);
size_t Size();
bool HasFree();
- bool HasRefs();
protected:
void Reset();
@@ -218,9 +217,6 @@ public:
unsigned GetAllowedReferences() override;
void Reset() override;
- // IDVDResourceCounted overrides
- long Release() override;
-
bool OpenDecoder();
int GetBuffer(AVCodecContext* avctx, AVFrame* pic);
void ReleaseBuffer(uint8_t* data);
@@ -270,7 +266,6 @@ protected:
CContext::shared_ptr m_dxvaContext;
CVideoBuffer* m_videoBuffer = nullptr;
struct AVD3D11VAContext* m_avD3D11Context = nullptr;
- struct AVCodecContext* m_avCtx = nullptr;
int m_refs = 0;
unsigned int m_shared = 0;
unsigned int m_surface_alignment = 0;
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
index 4335105e80..fb7606e0d0 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
@@ -518,7 +518,6 @@ CDecoder::CDecoder(CProcessInfo& processInfo) :
m_vaapiConfig.context = 0;
m_vaapiConfig.configId = VA_INVALID_ID;
m_vaapiConfig.processInfo = &m_processInfo;
- m_avctx = nullptr;
m_getBufferError = 0;
}
@@ -715,7 +714,7 @@ bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum A
else if (avctx->codec_id == AV_CODEC_ID_VP9)
m_vaapiConfig.maxReferences = 8;
else if (avctx->codec_id == AV_CODEC_ID_AV1)
- m_vaapiConfig.maxReferences = 18;
+ m_vaapiConfig.maxReferences = 21;
else
m_vaapiConfig.maxReferences = 2;
@@ -753,7 +752,6 @@ bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum A
avctx->get_buffer2 = CDecoder::FFGetBuffer;
avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
- m_avctx = mainctx;
return true;
}
@@ -774,12 +772,6 @@ void CDecoder::Close()
long CDecoder::Release()
{
- // if ffmpeg holds any references, flush buffers
- if (m_avctx && m_videoSurfaces.HasRefs())
- {
- avcodec_flush_buffers(m_avctx);
- }
-
if (m_presentPicture)
{
m_presentPicture->Release();
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.h
index 46ddf6407b..fe16256e4a 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.h
@@ -497,7 +497,6 @@ protected:
bool m_vaapiConfigured;
CVaapiConfig m_vaapiConfig;
CVideoSurfaces m_videoSurfaces;
- AVCodecContext* m_avctx;
int m_getBufferError;
COutput m_vaapiOutput;
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp
index f524d09acf..beb3f994a6 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp
@@ -471,17 +471,6 @@ int CVideoSurfaces::Size()
return m_state.size();
}
-bool CVideoSurfaces::HasRefs()
-{
- std::unique_lock<CCriticalSection> lock(m_section);
- for (const auto &i : m_state)
- {
- if (i.second & SURFACE_USED_FOR_REFERENCE)
- return true;
- }
- return false;
-}
-
//-----------------------------------------------------------------------------
// CVDPAU
//-----------------------------------------------------------------------------
@@ -626,7 +615,6 @@ bool CDecoder::Open(AVCodecContext* avctx, AVCodecContext* mainctx, const enum A
avctx->hwaccel_context = &m_hwContext;
CServiceBroker::GetWinSystem()->Register(this);
- m_avctx = mainctx;
return true;
}
}
@@ -656,12 +644,6 @@ void CDecoder::Close()
long CDecoder::Release()
{
- // if ffmpeg holds any references, flush buffers
- if (m_avctx && m_videoSurfaces.HasRefs())
- {
- avcodec_flush_buffers(m_avctx);
- }
-
// check if we should do some pre-cleanup here
// a second decoder might need resources
if (m_vdpauConfigured == true)
@@ -793,7 +775,6 @@ CDVDVideoCodec::VCReturn CDecoder::Check(AVCodecContext* avctx)
{
std::unique_lock<CCriticalSection> lock(m_DecoderSection);
- avcodec_flush_buffers(avctx);
FiniVDPAUOutput();
if (m_vdpauConfig.context)
m_vdpauConfig.context->Release();
diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.h
index d92416e63b..c690fabac3 100644
--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.h
+++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.h
@@ -526,7 +526,6 @@ public:
VdpVideoSurface RemoveNext(bool skiprender = false);
void Reset();
int Size();
- bool HasRefs();
protected:
std::map<VdpVideoSurface, int> m_state;
std::list<VdpVideoSurface> m_freeSurfaces;
@@ -645,7 +644,6 @@ protected:
CVdpauConfig m_vdpauConfig;
CVideoSurfaces m_videoSurfaces;
AVVDPAUContext m_hwContext;
- AVCodecContext* m_avctx = nullptr;
COutput m_vdpauOutput;
CVdpauBufferStats m_bufferStats;
diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
index 21ded53a1b..0cdf8c3864 100644
--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
@@ -432,7 +432,7 @@ void CDVDDemuxClient::SetStreamProps(CDemuxStream *stream, std::map<int, std::sh
streamAudio = std::dynamic_pointer_cast<CDemuxStreamClientInternalTpl<CDemuxStreamAudio>>(currentStream);
if (forceInit || !streamAudio || streamAudio->codec != source->codec)
{
- streamAudio.reset(new CDemuxStreamClientInternalTpl<CDemuxStreamAudio>());
+ streamAudio = std::make_shared<CDemuxStreamClientInternalTpl<CDemuxStreamAudio>>();
streamAudio->m_parser = av_parser_init(source->codec);
if (streamAudio->m_parser)
streamAudio->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
@@ -469,7 +469,7 @@ void CDVDDemuxClient::SetStreamProps(CDemuxStream *stream, std::map<int, std::sh
streamVideo = std::dynamic_pointer_cast<CDemuxStreamClientInternalTpl<CDemuxStreamVideo>>(currentStream);
if (forceInit || !streamVideo || streamVideo->codec != source->codec)
{
- streamVideo.reset(new CDemuxStreamClientInternalTpl<CDemuxStreamVideo>());
+ streamVideo = std::make_shared<CDemuxStreamClientInternalTpl<CDemuxStreamVideo>>();
streamVideo->m_parser = av_parser_init(source->codec);
if (streamVideo->m_parser)
streamVideo->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
@@ -513,7 +513,7 @@ void CDVDDemuxClient::SetStreamProps(CDemuxStream *stream, std::map<int, std::sh
streamSubtitle = std::dynamic_pointer_cast<CDemuxStreamClientInternalTpl<CDemuxStreamSubtitle>>(currentStream);
if (!streamSubtitle || streamSubtitle->codec != source->codec)
{
- streamSubtitle.reset(new CDemuxStreamClientInternalTpl<CDemuxStreamSubtitle>());
+ streamSubtitle = std::make_shared<CDemuxStreamClientInternalTpl<CDemuxStreamSubtitle>>();
streamSubtitle->m_parser = av_parser_init(source->codec);
if (streamSubtitle->m_parser)
streamSubtitle->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
@@ -543,7 +543,7 @@ void CDVDDemuxClient::SetStreamProps(CDemuxStream *stream, std::map<int, std::sh
streamTeletext = std::dynamic_pointer_cast<CDemuxStreamClientInternalTpl<CDemuxStreamTeletext>>(currentStream);
if (!streamTeletext || streamTeletext->codec != source->codec)
{
- streamTeletext.reset(new CDemuxStreamClientInternalTpl<CDemuxStreamTeletext>());
+ streamTeletext = std::make_shared<CDemuxStreamClientInternalTpl<CDemuxStreamTeletext>>();
}
map[stream->uniqueId] = streamTeletext;
@@ -566,7 +566,7 @@ void CDVDDemuxClient::SetStreamProps(CDemuxStream *stream, std::map<int, std::sh
streamRDS = std::dynamic_pointer_cast<CDemuxStreamClientInternalTpl<CDemuxStreamRadioRDS>>(currentStream);
if (!streamRDS || streamRDS->codec != source->codec)
{
- streamRDS.reset(new CDemuxStreamClientInternalTpl<CDemuxStreamRadioRDS>());
+ streamRDS = std::make_shared<CDemuxStreamClientInternalTpl<CDemuxStreamRadioRDS>>();
}
map[stream->uniqueId] = streamRDS;
diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
index 24fbc18518..9499a383fb 100644
--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
@@ -10,6 +10,9 @@
#include "DVDDemuxUtils.h"
#include "DVDInputStreams/DVDInputStream.h"
+#ifdef HAVE_LIBBLURAY
+#include "DVDInputStreams/DVDInputStreamBluray.h"
+#endif
#include "DVDInputStreams/DVDInputStreamFFmpeg.h"
#include "ServiceBroker.h"
#include "URL.h"
@@ -32,21 +35,12 @@
#include "utils/XTimeUtils.h"
#include "utils/log.h"
+#include <memory>
#include <mutex>
#include <sstream>
#include <tuple>
#include <utility>
-extern "C"
-{
-#include "libavutil/channel_layout.h"
-#include "libavutil/display.h"
-#include "libavutil/pixdesc.h"
-}
-
-#ifdef HAVE_LIBBLURAY
-#include "DVDInputStreams/DVDInputStreamBluray.h"
-#endif
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif
@@ -59,9 +53,12 @@ extern "C"
extern "C"
{
+#include <libavutil/channel_layout.h>
#include <libavutil/dict.h>
+#include <libavutil/display.h>
#include <libavutil/dovi_meta.h>
#include <libavutil/opt.h>
+#include <libavutil/pixdesc.h>
}
using namespace std::chrono_literals;
@@ -2292,8 +2289,7 @@ void CDVDDemuxFFmpeg::ParsePacket(AVPacket* pkt)
auto parser = m_parsers.find(st->index);
if (parser == m_parsers.end())
{
- m_parsers.insert(std::make_pair(st->index,
- std::unique_ptr<CDemuxParserFFmpeg>(new CDemuxParserFFmpeg())));
+ m_parsers.insert(std::make_pair(st->index, std::make_unique<CDemuxParserFFmpeg>()));
parser = m_parsers.find(st->index);
parser->second->m_parserCtx = av_parser_init(st->codecpar->codec_id);
diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxVobsub.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxVobsub.cpp
index ba02a306e5..0e8bb11dbc 100644
--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxVobsub.cpp
+++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxVobsub.cpp
@@ -18,6 +18,8 @@
#include "cores/VideoPlayer/Interface/TimingConstants.h"
#include "utils/StringUtils.h"
+#include <memory>
+
CDVDDemuxVobsub::CDVDDemuxVobsub() = default;
CDVDDemuxVobsub::~CDVDDemuxVobsub()
@@ -64,7 +66,7 @@ bool CDVDDemuxVobsub::Open(const std::string& filename, int source, const std::s
if (!m_Input || !m_Input->Open())
return false;
- m_Demuxer.reset(new CDVDDemuxFFmpeg());
+ m_Demuxer = std::make_unique<CDVDDemuxFFmpeg>();
if (!m_Demuxer->Open(m_Input, false))
return false;
diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DemuxMultiSource.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DemuxMultiSource.cpp
index 9f49630352..784bfd71cb 100644
--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DemuxMultiSource.cpp
+++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DemuxMultiSource.cpp
@@ -138,7 +138,7 @@ bool CDemuxMultiSource::Open(const std::shared_ptr<CDVDInputStream>& pInput)
m_demuxerMap[demuxer->GetDemuxerId()] = demuxer;
m_DemuxerToInputStreamMap[demuxer] = *iter;
- m_demuxerQueue.push(std::make_pair(-1.0, demuxer));
+ m_demuxerQueue.emplace(-1.0, demuxer);
++iter;
}
}
@@ -175,7 +175,7 @@ DemuxPacket* CDemuxMultiSource::Read()
readTime = packet->dts;
else
readTime = packet->pts;
- m_demuxerQueue.push(std::make_pair(readTime, currentDemuxer));
+ m_demuxerQueue.emplace(readTime, currentDemuxer);
}
else
{
@@ -188,7 +188,7 @@ DemuxPacket* CDemuxMultiSource::Read()
__FUNCTION__, CURL::GetRedacted(currentDemuxer->GetFileName()));
}
else //maybe add an error counter?
- m_demuxerQueue.push(std::make_pair(-1.0, currentDemuxer));
+ m_demuxerQueue.emplace(-1.0, currentDemuxer);
}
}
@@ -203,7 +203,7 @@ bool CDemuxMultiSource::SeekTime(double time, bool backwards, double* startpts)
{
if (iter.second->SeekTime(time, false, startpts))
{
- demuxerQueue.push(std::make_pair(*startpts, iter.second));
+ demuxerQueue.emplace(*startpts, iter.second);
CLog::Log(LOGDEBUG, "{} - starting demuxer from: {:f}", __FUNCTION__, time);
ret = true;
}
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDFactoryInputStream.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDFactoryInputStream.cpp
index caacf7f86a..e25e35dcaf 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDFactoryInputStream.cpp
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDFactoryInputStream.cpp
@@ -33,6 +33,7 @@
#include "utils/FileUtils.h"
#include "utils/URIUtils.h"
+#include <memory>
std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVideoPlayer* pPlayer, const CFileItem &fileitem, bool scanforextaudio)
{
@@ -68,7 +69,7 @@ std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVide
if (fileitem.GetProperty(STREAM_PROPERTY_INPUTSTREAM).asString() ==
STREAM_PROPERTY_VALUE_INPUTSTREAMFFMPEG)
- return std::shared_ptr<CDVDInputStreamFFmpeg>(new CDVDInputStreamFFmpeg(fileitem));
+ return std::make_shared<CDVDInputStreamFFmpeg>(fileitem);
if (fileitem.IsDiscImage())
{
@@ -77,14 +78,14 @@ std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVide
url.SetHostName(file);
url.SetFileName("BDMV/index.bdmv");
if (CFileUtils::Exists(url.Get()))
- return std::shared_ptr<CDVDInputStreamBluray>(new CDVDInputStreamBluray(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamBluray>(pPlayer, fileitem);
url.SetHostName(file);
url.SetFileName("BDMV/INDEX.BDM");
if (CFileUtils::Exists(url.Get()))
- return std::shared_ptr<CDVDInputStreamBluray>(new CDVDInputStreamBluray(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamBluray>(pPlayer, fileitem);
#endif
- return std::shared_ptr<CDVDInputStreamNavigator>(new CDVDInputStreamNavigator(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamNavigator>(pPlayer, fileitem);
}
#ifdef HAS_OPTICAL_DRIVE
@@ -93,24 +94,24 @@ std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVide
#ifdef HAVE_LIBBLURAY
if (CFileUtils::Exists(URIUtils::AddFileToFolder(file, "BDMV", "index.bdmv")) ||
CFileUtils::Exists(URIUtils::AddFileToFolder(file, "BDMV", "INDEX.BDM")))
- return std::shared_ptr<CDVDInputStreamBluray>(new CDVDInputStreamBluray(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamBluray>(pPlayer, fileitem);
#endif
- return std::shared_ptr<CDVDInputStreamNavigator>(new CDVDInputStreamNavigator(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamNavigator>(pPlayer, fileitem);
}
#endif
if (fileitem.IsDVDFile(false, true))
- return std::shared_ptr<CDVDInputStreamNavigator>(new CDVDInputStreamNavigator(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamNavigator>(pPlayer, fileitem);
else if (URIUtils::IsPVRChannel(file))
- return std::shared_ptr<CInputStreamPVRChannel>(new CInputStreamPVRChannel(pPlayer, fileitem));
+ return std::make_shared<CInputStreamPVRChannel>(pPlayer, fileitem);
else if (URIUtils::IsPVRRecording(file))
- return std::shared_ptr<CInputStreamPVRRecording>(new CInputStreamPVRRecording(pPlayer, fileitem));
+ return std::make_shared<CInputStreamPVRRecording>(pPlayer, fileitem);
#ifdef HAVE_LIBBLURAY
else if (fileitem.IsType(".bdmv") || fileitem.IsType(".mpls")
|| fileitem.IsType(".bdm") || fileitem.IsType(".mpl")
|| StringUtils::StartsWithNoCase(file, "bluray:"))
- return std::shared_ptr<CDVDInputStreamBluray>(new CDVDInputStreamBluray(pPlayer, fileitem));
+ return std::make_shared<CDVDInputStreamBluray>(pPlayer, fileitem);
#endif
else if (StringUtils::StartsWithNoCase(file, "rtp://") ||
StringUtils::StartsWithNoCase(file, "rtsp://") ||
@@ -128,10 +129,10 @@ std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVide
StringUtils::StartsWithNoCase(file, "rtmpte://") ||
StringUtils::StartsWithNoCase(file, "rtmps://"))
{
- return std::shared_ptr<CDVDInputStreamFFmpeg>(new CDVDInputStreamFFmpeg(fileitem));
+ return std::make_shared<CDVDInputStreamFFmpeg>(fileitem);
}
else if(StringUtils::StartsWithNoCase(file, "stack://"))
- return std::shared_ptr<CDVDInputStreamStack>(new CDVDInputStreamStack(fileitem));
+ return std::make_shared<CDVDInputStreamStack>(fileitem);
CFileItem finalFileitem(fileitem);
@@ -165,25 +166,23 @@ std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVide
}
if (finalFileitem.IsType(".m3u8"))
- return std::shared_ptr<CDVDInputStreamFFmpeg>(new CDVDInputStreamFFmpeg(finalFileitem));
+ return std::make_shared<CDVDInputStreamFFmpeg>(finalFileitem);
// mime type for m3u8/hls streams
if (finalFileitem.GetMimeType() == "application/vnd.apple.mpegurl" ||
finalFileitem.GetMimeType() == "application/x-mpegURL")
- return std::shared_ptr<CDVDInputStreamFFmpeg>(new CDVDInputStreamFFmpeg(finalFileitem));
+ return std::make_shared<CDVDInputStreamFFmpeg>(finalFileitem);
if (URIUtils::IsProtocol(finalFileitem.GetPath(), "udp"))
- return std::shared_ptr<CDVDInputStreamFFmpeg>(new CDVDInputStreamFFmpeg(finalFileitem));
+ return std::make_shared<CDVDInputStreamFFmpeg>(finalFileitem);
}
// our file interface handles all these types of streams
- return std::shared_ptr<CDVDInputStreamFile>(new CDVDInputStreamFile(finalFileitem,
- XFILE::READ_TRUNCATED |
- XFILE::READ_BITRATE |
- XFILE::READ_CHUNKED));
+ return std::make_shared<CDVDInputStreamFile>(
+ finalFileitem, XFILE::READ_TRUNCATED | XFILE::READ_BITRATE | XFILE::READ_CHUNKED);
}
std::shared_ptr<CDVDInputStream> CDVDFactoryInputStream::CreateInputStream(IVideoPlayer* pPlayer, const CFileItem &fileitem, const std::vector<std::string>& filenames)
{
- return std::shared_ptr<CInputStreamMultiSource>(new CInputStreamMultiSource(pPlayer, fileitem, filenames));
+ return std::make_shared<CInputStreamMultiSource>(pPlayer, fileitem, filenames);
}
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.cpp
index 35b030ee22..a87888c7be 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.cpp
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.cpp
@@ -30,6 +30,7 @@
#include <functional>
#include <limits>
+#include <memory>
#include <libbluray/bluray.h>
#include <libbluray/log_control.h>
@@ -331,7 +332,7 @@ bool CDVDInputStreamBluray::Open()
m_navmode = false;
m_titleInfo = GetTitleLongest();
}
- else if (resumable && m_item.GetStartOffset() == STARTOFFSET_RESUME)
+ else if (resumable && m_item.GetStartOffset() == STARTOFFSET_RESUME && m_item.IsResumable())
{
// resuming a bluray for which we have a saved state - the playlist will be open later on SetState
m_navmode = false;
@@ -1241,7 +1242,8 @@ void CDVDInputStreamBluray::SetupPlayerSettings()
bool CDVDInputStreamBluray::OpenStream(CFileItem &item)
{
- m_pstream.reset(new CDVDInputStreamFile(item, 0));
+ m_pstream = std::make_unique<CDVDInputStreamFile>(item, READ_TRUNCATED | READ_BITRATE |
+ READ_CHUNKED | READ_NO_CACHE);
if (!m_pstream->Open())
{
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.h b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.h
index 05af77ecef..cf7cd1be79 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.h
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamBluray.h
@@ -184,7 +184,7 @@ protected:
bool OpenStream(CFileItem &item);
void SetupPlayerSettings();
void FreeTitleInfo();
- std::unique_ptr<CDVDInputStreamFile> m_pstream = nullptr;
+ std::unique_ptr<CDVDInputStreamFile> m_pstream;
std::string m_rootPath;
/*! Bluray state serializer handler */
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
index 0fa5121dc0..178ed401dc 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
@@ -7,25 +7,31 @@
*/
#include "DVDInputStreamNavigator.h"
-#include "filesystem/IFileTypes.h"
-#include "utils/LangCodeExpander.h"
+
#include "../DVDDemuxSPU.h"
-#include "settings/Settings.h"
-#include "settings/SettingsComponent.h"
#include "LangInfo.h"
#include "ServiceBroker.h"
+#include "filesystem/IFileTypes.h"
+#if defined(TARGET_WINDOWS_STORE)
+#include "filesystem/SpecialProtocol.h"
+#endif
+#include "guilib/LocalizeStrings.h"
+#if defined(TARGET_WINDOWS_STORE)
+#include "platform/Environment.h"
+#endif
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
#include "utils/Geometry.h"
-#include "utils/log.h"
-#include "utils/URIUtils.h"
+#include "utils/LangCodeExpander.h"
#include "utils/StringUtils.h"
-#include "guilib/LocalizeStrings.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+
#if defined(TARGET_DARWIN_OSX)
#include "platform/darwin/osx/CocoaInterface.h"
#endif
-#if defined(TARGET_WINDOWS_STORE)
-#include "filesystem/SpecialProtocol.h"
-#include "platform/Environment.h"
-#endif
+
+#include <memory>
namespace
{
@@ -144,7 +150,8 @@ bool CDVDInputStreamNavigator::Open()
if (m_item.IsDiscImage())
{
// if dvd image file (ISO or alike) open using libdvdnav stream callback functions
- m_pstream.reset(new CDVDInputStreamFile(m_item, XFILE::READ_TRUNCATED | XFILE::READ_BITRATE | XFILE::READ_CHUNKED));
+ m_pstream = std::make_unique<CDVDInputStreamFile>(
+ m_item, XFILE::READ_TRUNCATED | XFILE::READ_BITRATE | XFILE::READ_CHUNKED);
#if DVDNAV_VERSION >= 60100
if (!m_pstream->Open() || m_dll.dvdnav_open_stream2(&m_dvdnav, m_pstream.get(), &loggerCallback,
&m_dvdnav_stream_cb) != DVDNAV_STATUS_OK)
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
index be8f46d8ab..66173a9d36 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
@@ -24,6 +24,8 @@
#include "utils/log.h"
#include "windowing/Resolution.h"
+#include <memory>
+
CInputStreamProvider::CInputStreamProvider(const ADDON::AddonInfoPtr& addonInfo,
KODI_HANDLE parentInstance)
: m_addonInfo(addonInfo), m_parentInstance(parentInstance)
@@ -191,8 +193,8 @@ bool CInputStreamAddon::Open()
m_caps = {};
m_ifc.inputstream->toAddon->get_capabilities(m_ifc.inputstream, &m_caps);
- m_subAddonProvider = std::shared_ptr<CInputStreamProvider>(
- new CInputStreamProvider(GetAddonInfo(), m_ifc.inputstream->toAddon->addonInstance));
+ m_subAddonProvider = std::make_shared<CInputStreamProvider>(
+ GetAddonInfo(), m_ifc.inputstream->toAddon->addonInstance);
}
return ret;
}
@@ -444,8 +446,7 @@ KODI_HANDLE CInputStreamAddon::cb_get_stream_transfer(KODI_HANDLE handle,
if (stream->m_masteringMetadata)
{
- videoStream->masteringMetaData =
- std::shared_ptr<AVMasteringDisplayMetadata>(new AVMasteringDisplayMetadata);
+ videoStream->masteringMetaData = std::make_shared<AVMasteringDisplayMetadata>();
videoStream->masteringMetaData->display_primaries[0][0] =
av_d2q(stream->m_masteringMetadata->primary_r_chromaticity_x, INT_MAX);
videoStream->masteringMetaData->display_primaries[0][1] =
@@ -472,8 +473,7 @@ KODI_HANDLE CInputStreamAddon::cb_get_stream_transfer(KODI_HANDLE handle,
if (stream->m_contentLightMetadata)
{
- videoStream->contentLightMetaData =
- std::shared_ptr<AVContentLightMetadata>(new AVContentLightMetadata);
+ videoStream->contentLightMetaData = std::make_shared<AVContentLightMetadata>();
videoStream->contentLightMetaData->MaxCLL =
static_cast<unsigned>(stream->m_contentLightMetadata->max_cll);
videoStream->contentLightMetaData->MaxFALL =
@@ -543,9 +543,9 @@ KODI_HANDLE CInputStreamAddon::cb_get_stream_transfer(KODI_HANDLE handle,
CRYPTO_SESSION_SYSTEM_PLAYREADY, CRYPTO_SESSION_SYSTEM_WISEPLAY,
CRYPTO_SESSION_SYSTEM_CLEARKEY,
};
- demuxStream->cryptoSession = std::shared_ptr<DemuxCryptoSession>(
- new DemuxCryptoSession(map[stream->m_cryptoSession.keySystem],
- stream->m_cryptoSession.sessionId, stream->m_cryptoSession.flags));
+ demuxStream->cryptoSession = std::make_shared<DemuxCryptoSession>(
+ map[stream->m_cryptoSession.keySystem], stream->m_cryptoSession.sessionId,
+ stream->m_cryptoSession.flags);
if ((stream->m_features & INPUTSTREAM_FEATURE_DECODE) != 0)
demuxStream->externalInterfaces = thisClass->m_subAddonProvider;
diff --git a/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitleParser.h b/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitleParser.h
index 4a822ad856..1e7164944b 100644
--- a/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitleParser.h
+++ b/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitleParser.h
@@ -76,7 +76,7 @@ protected:
return true;
}
else
- m_pStream.reset(new CDVDSubtitleStream());
+ m_pStream = std::make_unique<CDVDSubtitleStream>();
return m_pStream->Open(m_filename);
}
diff --git a/xbmc/cores/VideoPlayer/Edl.cpp b/xbmc/cores/VideoPlayer/Edl.cpp
index a3bbea05ad..01c7ed09b1 100644
--- a/xbmc/cores/VideoPlayer/Edl.cpp
+++ b/xbmc/cores/VideoPlayer/Edl.cpp
@@ -17,7 +17,7 @@
#include "settings/SettingsComponent.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
-#include "utils/XBMCTinyXML.h"
+#include "utils/XBMCTinyXML2.h"
#include "utils/log.h"
#include "PlatformDefs.h"
@@ -482,36 +482,36 @@ bool CEdl::ReadBeyondTV(const std::string& strMovie)
if (!CFile::Exists(beyondTVFilename))
return false;
- CXBMCTinyXML xmlDoc;
+ CXBMCTinyXML2 xmlDoc;
if (!xmlDoc.LoadFile(beyondTVFilename))
{
CLog::Log(LOGERROR, "{} - Could not load Beyond TV file: {}. {}", __FUNCTION__,
- CURL::GetRedacted(beyondTVFilename), xmlDoc.ErrorDesc());
+ CURL::GetRedacted(beyondTVFilename), xmlDoc.ErrorStr());
return false;
}
if (xmlDoc.Error())
{
CLog::Log(LOGERROR, "{} - Could not parse Beyond TV file: {}. {}", __FUNCTION__,
- CURL::GetRedacted(beyondTVFilename), xmlDoc.ErrorDesc());
+ CURL::GetRedacted(beyondTVFilename), xmlDoc.ErrorStr());
return false;
}
- TiXmlElement *pRoot = xmlDoc.RootElement();
- if (!pRoot || strcmp(pRoot->Value(), "cutlist"))
+ const tinyxml2::XMLElement* root = xmlDoc.RootElement();
+ if (!root || strcmp(root->Value(), "cutlist"))
{
CLog::Log(LOGERROR, "{} - Invalid Beyond TV file: {}. Expected root node to be <cutlist>",
__FUNCTION__, CURL::GetRedacted(beyondTVFilename));
return false;
}
- bool bValid = true;
- TiXmlElement *pRegion = pRoot->FirstChildElement("Region");
- while (bValid && pRegion)
+ bool valid = true;
+ const tinyxml2::XMLElement* region = root->FirstChildElement("Region");
+ while (valid && region)
{
- TiXmlElement *pStart = pRegion->FirstChildElement("start");
- TiXmlElement *pEnd = pRegion->FirstChildElement("end");
- if (pStart && pEnd && pStart->FirstChild() && pEnd->FirstChild())
+ const tinyxml2::XMLElement* start = region->FirstChildElement("start");
+ const tinyxml2::XMLElement* end = region->FirstChildElement("end");
+ if (start && end && start->FirstChild() && end->FirstChild())
{
/*
* Need to divide the start and end times by a factor of 10,000 to get msec.
@@ -526,17 +526,17 @@ bool CEdl::ReadBeyondTV(const std::string& strMovie)
* atof() returns 0 if there were any problems and will subsequently be rejected in AddEdit().
*/
Edit edit;
- edit.start = std::lround((std::atof(pStart->FirstChild()->Value()) / 10000));
- edit.end = std::lround((std::atof(pEnd->FirstChild()->Value()) / 10000));
+ edit.start = std::lround((std::atof(start->FirstChild()->Value()) / 10000));
+ edit.end = std::lround((std::atof(end->FirstChild()->Value()) / 10000));
edit.action = Action::COMM_BREAK;
- bValid = AddEdit(edit);
+ valid = AddEdit(edit);
}
else
- bValid = false;
+ valid = false;
- pRegion = pRegion->NextSiblingElement("Region");
+ region = region->NextSiblingElement("Region");
}
- if (!bValid)
+ if (!valid)
{
CLog::Log(LOGERROR,
"{} - Invalid Beyond TV file: {}. Clearing any valid commercial breaks found.",
diff --git a/xbmc/cores/VideoPlayer/Process/ProcessInfo.cpp b/xbmc/cores/VideoPlayer/Process/ProcessInfo.cpp
index 00c58cb10c..ce244f96f9 100644
--- a/xbmc/cores/VideoPlayer/Process/ProcessInfo.cpp
+++ b/xbmc/cores/VideoPlayer/Process/ProcessInfo.cpp
@@ -13,6 +13,7 @@
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
+#include <memory>
#include <mutex>
CCriticalSection createSection;
@@ -42,7 +43,8 @@ CProcessInfo* CProcessInfo::CreateInstance()
CProcessInfo::CProcessInfo()
{
- m_videoSettingsLocked.reset(new CVideoSettingsLocked(m_videoSettings, m_settingsSection));
+ m_videoSettingsLocked =
+ std::make_unique<CVideoSettingsLocked>(m_videoSettings, m_settingsSection);
}
void CProcessInfo::SetDataCache(CDataCacheCore *cache)
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
index c4590feca7..0974c2d985 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
@@ -17,16 +17,12 @@
#include "DVDDemuxers/DVDFactoryDemuxer.h"
#include "DVDInputStreams/DVDFactoryInputStream.h"
#include "DVDInputStreams/DVDInputStream.h"
-#include "DVDMessage.h"
-#include "VideoPlayerVideo.h"
-#include "application/Application.h"
-
-#include <mutex>
#if defined(HAVE_LIBBLURAY)
#include "DVDInputStreams/DVDInputStreamBluray.h"
#endif
#include "DVDInputStreams/DVDInputStreamNavigator.h"
#include "DVDInputStreams/InputStreamPVRBase.h"
+#include "DVDMessage.h"
#include "FileItem.h"
#include "GUIUserMessages.h"
#include "LangInfo.h"
@@ -35,6 +31,8 @@
#include "Util.h"
#include "VideoPlayerAudio.h"
#include "VideoPlayerRadioRDS.h"
+#include "VideoPlayerVideo.h"
+#include "application/Application.h"
#include "cores/DataCacheCore.h"
#include "cores/EdlEdit.h"
#include "cores/FFmpeg.h"
@@ -51,7 +49,6 @@
#include "settings/AdvancedSettings.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
-#include "storage/MediaManager.h"
#include "threads/SingleLock.h"
#include "utils/FontUtils.h"
#include "utils/JobManager.h"
@@ -67,6 +64,8 @@
#include "windowing/WinSystem.h"
#include <iterator>
+#include <memory>
+#include <mutex>
#include <utility>
using namespace std::chrono_literals;
@@ -616,7 +615,7 @@ CVideoPlayer::CVideoPlayer(IPlayerCallback& callback)
m_messenger("player"),
m_renderManager(m_clock, this)
{
- m_outboundEvents.reset(new CJobQueue(false, 1, CJob::PRIORITY_NORMAL));
+ m_outboundEvents = std::make_unique<CJobQueue>(false, 1, CJob::PRIORITY_NORMAL);
m_players_created = false;
m_pDemuxer = nullptr;
m_pSubtitleDemuxer = nullptr;
@@ -1805,6 +1804,7 @@ CacheInfo CVideoPlayer::GetCachingTimes()
if (!m_pInputStream->GetCacheStatus(&status))
return info;
+ const uint64_t& maxforward = status.maxforward;
const uint64_t& cached = status.forward;
const uint32_t& currate = status.currate;
const uint32_t& maxrate = status.maxrate;
@@ -1820,7 +1820,6 @@ CacheInfo CVideoPlayer::GetCachingTimes()
double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
double queued = 1000.0 * queueTime / play_sbp;
- info.delay = 0.0;
info.level = 0.0;
info.offset = (cached + queued) / length;
info.time = 0.0;
@@ -1829,16 +1828,13 @@ CacheInfo CVideoPlayer::GetCachingTimes()
if (currate == 0)
return info;
- double cache_sbp = 1.1 * (double)DVD_TIME_BASE / currate; /* underestimate by 10 % */
- double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */
- double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */
- double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */
-
// estimated playback time of current cached bytes
- double cache_time = (static_cast<double>(cached) / currate) + (queueTime / 1000.0);
+ const double cacheTime = (static_cast<double>(cached) / currate) + (queueTime / 1000.0);
- info.delay = cache_left - play_left;
- info.time = cache_time;
+ // cache level as current forward bytes / max forward bytes [0.0 - 1.0]
+ const double cacheLevel = (maxforward > 0) ? static_cast<double>(cached) / maxforward : 0.0;
+
+ info.time = cacheTime;
if (lowrate > 0)
{
@@ -1847,7 +1843,7 @@ CacheInfo CVideoPlayer::GetCachingTimes()
info.level = -1.0;
}
else
- info.level = (cached + queued) / (cache_need + queued);
+ info.level = cacheLevel;
return info;
}
@@ -3182,7 +3178,7 @@ void CVideoPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
{
if (!bPlus)
{
- SeekChapter(GetChapter() - 1);
+ SeekChapter(GetPreviousChapter());
return;
}
else if (GetChapter() < GetChapterCount())
@@ -3291,15 +3287,10 @@ void CVideoPlayer::GetGeneralInfo(std::string& strGeneralInfo)
std::unique_lock<CCriticalSection> lock(m_StateSection);
if (m_State.cache_bytes >= 0)
{
- strBuf += StringUtils::Format("forward: {}", StringUtils::SizeToString(m_State.cache_bytes));
-
- if (m_State.cache_time > 0)
- strBuf += StringUtils::Format(" {:6.3f}s", m_State.cache_time);
- else
- strBuf += StringUtils::Format(" {:2.0f}%", m_State.cache_level * 100);
-
- if (m_playSpeed == 0 || m_caching == CACHESTATE_FULL)
- strBuf += StringUtils::Format(" {} msec", DVD_TIME_TO_MSEC(m_State.cache_delay));
+ strBuf += StringUtils::Format("forward: {} / {:2.0f}% / {:6.3f}s / {:.3f}%",
+ StringUtils::SizeToString(m_State.cache_bytes),
+ m_State.cache_level * 100.0, m_State.cache_time,
+ m_State.cache_offset * 100.0);
}
strGeneralInfo = StringUtils::Format("Player: a/v:{: 6.3f}, {}", dDiff, strBuf);
@@ -4387,7 +4378,7 @@ bool CVideoPlayer::OnAction(const CAction &action)
THREAD_ACTION(action);
CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() > 0)
- m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() - 1));
+ m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
else
pMenus->OnPrevious();
@@ -4514,7 +4505,7 @@ bool CVideoPlayer::OnAction(const CAction &action)
case ACTION_PREV_ITEM:
if (GetChapter() > 0)
{
- m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() - 1));
+ m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
m_processInfo->SeekFinished(0);
return true;
}
@@ -4632,6 +4623,18 @@ int64_t CVideoPlayer::GetChapterPos(int chapterIdx) const
return -1;
}
+int CVideoPlayer::GetPreviousChapter()
+{
+ // 5-second grace period from chapter start to skip backwards to previous chapter
+ // Afterwards skip to start of current chapter.
+ const int chapter = GetChapter();
+
+ if (chapter > 0 && (GetTime() < (GetChapterPos(chapter) + 5) * 1000))
+ return chapter - 1;
+ else
+ return chapter;
+}
+
void CVideoPlayer::AddSubtitle(const std::string& strSubPath)
{
m_messenger.Put(
@@ -4821,7 +4824,7 @@ void CVideoPlayer::UpdatePlayState(double timeout)
{
std::string name;
pChapter->GetChapterName(name, i + 1);
- state.chapters.push_back(make_pair(name, pChapter->GetChapterPos(i + 1)));
+ state.chapters.emplace_back(name, pChapter->GetChapterPos(i + 1));
}
}
CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
@@ -4916,14 +4919,12 @@ void CVideoPlayer::UpdatePlayState(double timeout)
if (cache.valid)
{
- state.cache_delay = std::max(0.0, cache.delay);
state.cache_level = std::max(0.0, std::min(1.0, cache.level));
state.cache_offset = cache.offset;
state.cache_time = cache.time;
}
else
{
- state.cache_delay = 0.0;
state.cache_level = std::min(1.0, queueTime / 8000.0);
state.cache_offset = queueTime / state.timeMax;
state.cache_time = queueTime / 1000.0;
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.h b/xbmc/cores/VideoPlayer/VideoPlayer.h
index daa0f2ad41..c243c5a4d3 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.h
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.h
@@ -55,7 +55,6 @@ struct SPlayerState
caching = false;
cache_bytes = 0;
cache_level = 0.0;
- cache_delay = 0.0;
cache_offset = 0.0;
lastSeek = 0;
streamsReady = false;
@@ -84,10 +83,9 @@ struct SPlayerState
bool cantempo;
bool caching;
- int64_t cache_bytes; // number of bytes current's cached
- double cache_level; // current estimated required cache level
- double cache_delay; // time until cache is expected to reach estimated level
- double cache_offset; // percentage of file ahead of current position
+ int64_t cache_bytes; // number of bytes current's cached
+ double cache_level; // current cache level
+ double cache_offset; // percentage of file ahead of current position
double cache_time; // estimated playback time of current cached bytes
};
@@ -240,8 +238,7 @@ protected:
struct CacheInfo
{
- double level; // current estimated required cache level
- double delay; // time until cache is expected to reach estimated level
+ double level; // current cache level
double offset; // percentage of file ahead of current position
double time; // estimated playback time of current cached bytes
bool valid;
@@ -469,6 +466,7 @@ protected:
void UpdateContentState();
void UpdateFileItemStreamDetails(CFileItem& item);
+ int GetPreviousChapter();
bool m_players_created;
diff --git a/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp b/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp
index 2c246da620..9d4e16700b 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp
@@ -61,7 +61,8 @@ CVideoPlayerAudio::CVideoPlayerAudio(CDVDClock* pClock, CDVDMessageQueue& parent
m_prevskipped = false;
m_maxspeedadjust = 0.0;
- m_messageQueue.SetMaxDataSize(6 * 1024 * 1024);
+ // 18 MB allows max bitrate of 18 Mbit/s (TrueHD max peak) during 8 seconds
+ m_messageQueue.SetMaxDataSize(18 * 1024 * 1024);
m_messageQueue.SetMaxTimeSize(8.0);
m_disconAdjustTimeMs = processInfo.GetMaxPassthroughOffSyncDuration();
}
diff --git a/xbmc/cores/VideoPlayer/VideoPlayerTeletext.cpp b/xbmc/cores/VideoPlayer/VideoPlayerTeletext.cpp
index 600e433303..4e82c2d8c2 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayerTeletext.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayerTeletext.cpp
@@ -361,8 +361,12 @@ void CDVDTeletextData::Process()
b1 = dehamming[vtxt_row[9]];
if (b1 != 0xFF)
{
- pageinfo_thread->nationalvalid = 1;
- pageinfo_thread->national = rev_lut[b1] & 0x07;
+ int countryCode = rev_lut[b1] & 0x07;
+ if (countryCode != NAT_DEFAULT)
+ {
+ pageinfo_thread->nationalvalid = 1;
+ pageinfo_thread->national = countryCode;
+ }
}
if (dehamming[vtxt_row[7]] & 0x08)// subtitle page
@@ -583,10 +587,11 @@ void CDVDTeletextData::Process()
{
int t1 = CDVDTeletextTools::deh24(&vtxt_row[7-4]);
pageinfo_thread->function = t1 & 0x0f;
- if (!pageinfo_thread->nationalvalid)
+ int countryCode = (t1 >> 4) & 0x07;
+ if (!pageinfo_thread->nationalvalid && countryCode != NAT_DEFAULT)
{
pageinfo_thread->nationalvalid = 1;
- pageinfo_thread->national = (t1>>4) & 0x07;
+ pageinfo_thread->national = countryCode;
}
}
diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
index ccfca71b79..e4f0e993de 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
@@ -66,7 +66,9 @@ CVideoPlayerVideo::CVideoPlayerVideo(CDVDClock* pClock
m_iLateFrames = 0;
m_iDroppedRequest = 0;
m_fForcedAspectRatio = 0;
- m_messageQueue.SetMaxDataSize(40 * 1024 * 1024);
+
+ // 128 MB allows max bitrate of 128 Mbit/s (e.g. UHD Blu-Ray) during 8 seconds
+ m_messageQueue.SetMaxDataSize(128 * 1024 * 1024);
m_messageQueue.SetMaxTimeSize(8.0);
m_iDroppedFrames = 0;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp
index b8649daaa8..a451bc4be7 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.cpp
@@ -218,7 +218,8 @@ void CBaseRenderer::CalcNormalRenderRect(float offsetX,
return;
// clip as needed
- if (!(CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() || CServiceBroker::GetWinSystem()->GetGfxContext().IsCalibrating()))
+ if (m_alwaysClip || !(CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() ||
+ CServiceBroker::GetWinSystem()->GetGfxContext().IsCalibrating()))
{
CRect original(m_destRect);
m_destRect.Intersect(CRect(offsetX, offsetY, offsetX + width, offsetY + height));
@@ -501,6 +502,11 @@ void CBaseRenderer::MarkDirty()
CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(m_destRect);
}
+void CBaseRenderer::EnableAlwaysClip()
+{
+ m_alwaysClip = true;
+}
+
void CBaseRenderer::SetVideoSettings(const CVideoSettings &settings)
{
m_videoSettings = settings;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h
index 67871f9b9d..3fdac4149a 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/BaseRenderer.h
@@ -114,6 +114,7 @@ protected:
virtual void ReorderDrawPoints();
virtual EShaderFormat GetShaderFormat();
void MarkDirty();
+ void EnableAlwaysClip();
//@todo drop those
void saveRotatedCoords();//saves the current state of m_rotatedDestCoords
@@ -141,4 +142,7 @@ protected:
AVPixelFormat m_format = AV_PIX_FMT_NONE;
CVideoSettings m_videoSettings;
+
+private:
+ bool m_alwaysClip = false;
};
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp
index ee540c9d2c..3bf8f05f9f 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp
@@ -10,6 +10,8 @@
#include "utils/log.h"
+#include <memory>
+
using namespace DRMPRIME;
namespace
@@ -55,7 +57,7 @@ CDRMPRIMETexture::~CDRMPRIMETexture()
void CDRMPRIMETexture::Init(EGLDisplay eglDisplay)
{
- m_eglImage.reset(new CEGLImage(eglDisplay));
+ m_eglImage = std::make_unique<CEGLImage>(eglDisplay);
}
bool CDRMPRIMETexture::Map(CVideoBufferDRMPRIME* buffer)
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp
index cf2d73b750..8147222f65 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp
@@ -660,3 +660,18 @@ bool CProcessorHD::SetConversion(const ProcessorConversion& conversion)
return true;
}
+
+bool CProcessorHD::Supports(ERENDERFEATURE feature) const
+{
+ switch (feature)
+ {
+ case RENDERFEATURE_BRIGHTNESS:
+ return m_procCaps.m_Filters[D3D11_VIDEO_PROCESSOR_FILTER_BRIGHTNESS].bSupported;
+ case RENDERFEATURE_CONTRAST:
+ return m_procCaps.m_Filters[D3D11_VIDEO_PROCESSOR_FILTER_CONTRAST].bSupported;
+ case RENDERFEATURE_ROTATION:
+ return (m_procCaps.m_vcaps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_ROTATION);
+ default:
+ return false;
+ }
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h
index 65d2537830..943a64a0eb 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h
@@ -57,6 +57,7 @@ public:
static bool IsSuperResolutionSuitable(const VideoPicture& picture);
void TryEnableVideoSuperResolution();
bool IsVideoSuperResolutionEnabled() const { return m_superResolutionEnabled; }
+ bool Supports(ERENDERFEATURE feature) const;
protected:
bool ReInit();
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp
index 2fcf54f3f5..42b8287315 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp
@@ -22,6 +22,8 @@
#include "windowing/WinSystem.h"
#include "windowing/linux/WinSystemEGL.h"
+#include <memory>
+
using namespace KODI::UTILS::EGL;
CRendererDRMPRIMEGLES::~CRendererDRMPRIMEGLES()
@@ -110,7 +112,7 @@ bool CRendererDRMPRIMEGLES::Configure(const VideoPicture& picture,
if (!buf.fence)
{
buf.texture.Init(eglDisplay);
- buf.fence.reset(new CEGLFence(eglDisplay));
+ buf.fence = std::make_unique<CEGLFence>(eglDisplay);
}
}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.cpp
index d268b6ca81..c065762a73 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.cpp
@@ -16,6 +16,8 @@
#include "utils/log.h"
#include "windowing/wayland/WinSystemWaylandWebOS.h"
+#include <appswitching-control-block/AcbAPI.h>
+
CRendererStarfish::CRendererStarfish()
{
CLog::LogF(LOGINFO, "CRendererStarfish: Instanced");
@@ -32,6 +34,12 @@ CBaseRenderer* CRendererStarfish::Create(CVideoBuffer* buffer)
bool CRendererStarfish::Configure(const VideoPicture& picture, float fps, unsigned int orientation)
{
+ auto buffer = static_cast<CStarfishVideoBuffer*>(picture.videoBuffer);
+ m_acbId = buffer->m_acbId;
+ if (m_acbId)
+ {
+ EnableAlwaysClip();
+ }
m_iFlags = GetFlagsChromaPosition(picture.chroma_position) |
GetFlagsColorMatrix(picture.color_space, picture.iWidth, picture.iHeight) |
GetFlagsColorPrimaries(picture.color_primaries) |
@@ -75,8 +83,28 @@ bool CRendererStarfish::Register()
void CRendererStarfish::ManageRenderArea()
{
+ // this hack is needed to get the 2D mode of a 3D movie going
+ RENDER_STEREO_MODE stereoMode = CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
+ if (stereoMode == RENDER_STEREO_MODE_MONO)
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_LEFT);
+
CBaseRenderer::ManageRenderArea();
+ if (stereoMode == RENDER_STEREO_MODE_MONO)
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_OFF);
+
+ switch (stereoMode)
+ {
+ case RENDER_STEREO_MODE_SPLIT_HORIZONTAL:
+ m_destRect.y2 *= 2.0f;
+ break;
+ case RENDER_STEREO_MODE_SPLIT_VERTICAL:
+ m_destRect.x2 *= 2.0f;
+ break;
+ default:
+ break;
+ }
+
if ((m_exportedDestRect != m_destRect || m_exportedSourceRect != m_sourceRect) &&
!m_sourceRect.IsEmpty() && !m_destRect.IsEmpty())
{
@@ -84,8 +112,16 @@ void CRendererStarfish::ManageRenderArea()
CRect{0, 0, static_cast<float>(m_sourceWidth), static_cast<float>(m_sourceHeight)};
using namespace KODI::WINDOWING::WAYLAND;
auto winSystem = static_cast<CWinSystemWaylandWebOS*>(CServiceBroker::GetWinSystem());
-
- winSystem->SetExportedWindow(origRect, m_sourceRect, m_destRect);
+ if (winSystem->SupportsExportedWindow())
+ {
+ winSystem->SetExportedWindow(origRect, m_sourceRect, m_destRect);
+ }
+ else if (m_acbId)
+ {
+ AcbAPI_setCustomDisplayWindow(m_acbId, m_sourceRect.x1, m_sourceRect.y1, m_sourceRect.Width(),
+ m_sourceRect.Height(), m_destRect.x1, m_destRect.y1,
+ m_destRect.Width(), m_destRect.Height(), false, nullptr);
+ }
m_exportedSourceRect = m_sourceRect;
m_exportedDestRect = m_destRect;
}
@@ -144,8 +180,6 @@ void CRendererStarfish::Update()
{
return;
}
-
- ManageRenderArea();
}
void CRendererStarfish::RenderUpdate(
@@ -155,4 +189,6 @@ void CRendererStarfish::RenderUpdate(
{
return;
}
+
+ ManageRenderArea();
}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.h
index ca52c57209..5b261e60a7 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.h
@@ -48,4 +48,5 @@ private:
CRect m_exportedSourceRect;
CRect m_exportedDestRect;
bool m_configured{false};
+ long m_acbId{0};
};
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.cpp
index 4b2a19b1b5..d3d35ec20a 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGL.cpp
@@ -13,6 +13,8 @@
#include "utils/GLUtils.h"
#include "utils/log.h"
+#include <memory>
+
using namespace VAAPI;
IVaapiWinSystem* CRendererVAAPIGL::m_pWinSystem = nullptr;
@@ -95,11 +97,11 @@ bool CRendererVAAPIGL::Configure(const VideoPicture& picture, float fps, unsigne
{
if (useVaapi2)
{
- tex.reset(new VAAPI::CVaapi2Texture);
+ tex = std::make_unique<VAAPI::CVaapi2Texture>();
}
else
{
- tex.reset(new VAAPI::CVaapi1Texture);
+ tex = std::make_unique<VAAPI::CVaapi1Texture>();
}
tex->Init(interop);
}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.cpp
index 65e1e84d02..b4ce2c18b1 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVAAPIGLES.cpp
@@ -17,6 +17,8 @@
#include "utils/GLUtils.h"
#include "utils/log.h"
+#include <memory>
+
using namespace VAAPI;
using namespace KODI::UTILS::EGL;
@@ -97,18 +99,18 @@ bool CRendererVAAPIGLES::Configure(const VideoPicture& picture, float fps, unsig
{
if (useVaapi2)
{
- tex.reset(new VAAPI::CVaapi2Texture);
+ tex = std::make_unique<VAAPI::CVaapi2Texture>();
}
else
{
- tex.reset(new VAAPI::CVaapi1Texture);
+ tex = std::make_unique<VAAPI::CVaapi1Texture>();
}
tex->Init(interop);
}
for (auto& fence : m_fences)
{
- fence.reset(new CEGLFence(CRendererVAAPIGLES::m_pWinSystem->GetEGLDisplay()));
+ fence = std::make_unique<CEGLFence>(CRendererVAAPIGLES::m_pWinSystem->GetEGLDisplay());
}
return CLinuxRendererGLES::Configure(picture, fps, orientation);
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVDPAU.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVDPAU.cpp
index 048323ce9d..47d973d8a4 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVDPAU.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVDPAU.cpp
@@ -175,8 +175,7 @@ bool CRendererVDPAU::Supports(ESCALINGMETHOD method) const
float scaleX = fabs(((float)m_sourceWidth - m_destRect.Width())/m_sourceWidth)*100;
float scaleY = fabs(((float)m_sourceHeight - m_destRect.Height())/m_sourceHeight)*100;
int minScale = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("videoplayer.hqscalers");
- if (scaleX < minScale && scaleY < minScale)
- return false;
+ return !(scaleX < minScale && scaleY < minScale);
}
return false;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp
index 0069425afb..d567c4e7f0 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp
@@ -14,11 +14,7 @@
#include "cores/VideoPlayer/DVDCodecs/Video/VTB.h"
#include "utils/log.h"
#include "windowing/WinSystem.h"
-#if defined(HAS_SDL)
-#include "windowing/osx/SDL/WinSystemOSXSDL.h"
-#else
#include "windowing/osx/WinSystemOSX.h"
-#endif
#include "platform/darwin/osx/CocoaInterface.h"
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp
index 233e6310bb..34d1ab6235 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp
@@ -105,7 +105,7 @@ bool CVideoLayerBridgeDRMPRIME::Map(CVideoBufferDRMPRIME* buffer)
{
int object = layer->planes[plane].object_index;
uint32_t handle = buffer->m_handles[object];
- if (handle && layer->planes[plane].pitch)
+ if (handle)
{
handles[plane] = handle;
pitches[plane] = layer->planes[plane].pitch;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp
index b4a028877b..1dd642c5d8 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp
@@ -33,11 +33,15 @@
#include "windowing/GraphicContext.h"
#include "windowing/WinSystem.h"
+#ifdef TARGET_DARWIN_OSX
+#include "platform/darwin/osx/CocoaInterface.h"
+#endif
+
#include <locale.h>
+#include <memory>
#include <mutex>
-#ifdef TARGET_DARWIN_OSX
-#include "platform/darwin/osx/CocoaInterface.h"
+#if defined(TARGET_DARWIN_OSX)
#include <CoreVideo/CoreVideo.h>
#include <OpenGL/CGLIOSurface.h>
#endif
@@ -119,7 +123,7 @@ CLinuxRendererGL::CLinuxRendererGL()
m_fbo.width = 0.0;
m_fbo.height = 0.0;
- m_ColorManager.reset(new CColorManager());
+ m_ColorManager = std::make_unique<CColorManager>();
m_tCLUTTex = 0;
m_CLUT = NULL;
m_CLUTsize = 0;
@@ -210,7 +214,7 @@ bool CLinuxRendererGL::Configure(const VideoPicture &picture, float fps, unsigne
m_iFlags = GetFlagsChromaPosition(picture.chroma_position) |
GetFlagsStereoMode(picture.stereoMode);
- m_srcPrimaries = GetSrcPrimaries(picture.color_primaries, picture.iWidth, picture.iHeight);
+ m_srcPrimaries = picture.color_primaries;
m_toneMap = false;
// Calculate the input frame aspect ratio.
@@ -2713,10 +2717,9 @@ void CLinuxRendererGL::CheckVideoParameters(int index)
const CPictureBuffer& buf = m_buffers[index];
ETONEMAPMETHOD method = m_videoSettings.m_ToneMapMethod;
- AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height);
- if (srcPrim != m_srcPrimaries)
+ if (buf.m_srcPrimaries != m_srcPrimaries)
{
- m_srcPrimaries = srcPrim;
+ m_srcPrimaries = buf.m_srcPrimaries;
m_reloadShaders = true;
}
@@ -2737,19 +2740,6 @@ void CLinuxRendererGL::CheckVideoParameters(int index)
m_toneMapMethod = method;
}
-AVColorPrimaries CLinuxRendererGL::GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned int width, unsigned int height)
-{
- AVColorPrimaries ret = srcPrimaries;
- if (ret == AVCOL_PRI_UNSPECIFIED)
- {
- if (width > 1024 || height >= 600)
- ret = AVCOL_PRI_BT709;
- else
- ret = AVCOL_PRI_BT470BG;
- }
- return ret;
-}
-
CRenderCapture* CLinuxRendererGL::GetRenderCapture()
{
return new CRenderCaptureGL;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
index 75189c278b..85ab951b11 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
@@ -114,7 +114,7 @@ bool CLinuxRendererGLES::Configure(const VideoPicture &picture, float fps, unsig
m_sourceHeight = picture.iHeight;
m_renderOrientation = orientation;
- m_srcPrimaries = GetSrcPrimaries(picture.color_primaries, picture.iWidth, picture.iHeight);
+ m_srcPrimaries = picture.color_primaries;
m_toneMap = false;
// Calculate the input frame aspect ratio.
@@ -840,10 +840,9 @@ void CLinuxRendererGLES::RenderSinglePass(int index, int field)
CPictureBuffer &buf = m_buffers[index];
CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[field];
- AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height);
- if (srcPrim != m_srcPrimaries)
+ if (buf.m_srcPrimaries != m_srcPrimaries)
{
- m_srcPrimaries = srcPrim;
+ m_srcPrimaries = buf.m_srcPrimaries;
m_reloadShaders = true;
}
@@ -975,10 +974,9 @@ void CLinuxRendererGLES::RenderToFBO(int index, int field)
CPictureBuffer &buf = m_buffers[index];
CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[field];
- AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height);
- if (srcPrim != m_srcPrimaries)
+ if (buf.m_srcPrimaries != m_srcPrimaries)
{
- m_srcPrimaries = srcPrim;
+ m_srcPrimaries = buf.m_srcPrimaries;
m_reloadShaders = true;
}
@@ -1757,24 +1755,6 @@ bool CLinuxRendererGLES::IsGuiLayer()
return true;
}
-AVColorPrimaries CLinuxRendererGLES::GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned int width, unsigned int height)
-{
- AVColorPrimaries ret = srcPrimaries;
- if (ret == AVCOL_PRI_UNSPECIFIED)
- {
- if (width > 1024 || height >= 600)
- {
- ret = AVCOL_PRI_BT709;
- }
- else
- {
- ret = AVCOL_PRI_BT470BG;
- }
- }
-
- return ret;
-}
-
CRenderCapture* CLinuxRendererGLES::GetRenderCapture()
{
return new CRenderCaptureGLES;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp
index 432934e62d..4d363848cb 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/RenderManager.cpp
@@ -8,6 +8,9 @@
#include "RenderManager.h"
+/* to use the same as player */
+#include "../VideoPlayer/DVDClock.h"
+#include "../VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h"
#include "RenderCapture.h"
#include "RenderFactory.h"
#include "RenderFlags.h"
@@ -25,12 +28,9 @@
#include "windowing/GraphicContext.h"
#include "windowing/WinSystem.h"
+#include <memory>
#include <mutex>
-/* to use the same as player */
-#include "../VideoPlayer/DVDClock.h"
-#include "../VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h"
-
using namespace std::chrono_literals;
void CRenderManager::CClockSync::Reset()
@@ -141,7 +141,7 @@ bool CRenderManager::Configure(const VideoPicture& picture, float fps, unsigned
m_stateEvent.Reset();
m_clockSync.Reset();
m_dvdClock.SetVsyncAdjust(0);
- m_pConfigPicture.reset(new VideoPicture());
+ m_pConfigPicture = std::make_unique<VideoPicture>();
m_pConfigPicture->CopyRef(picture);
std::unique_lock<CCriticalSection> lock2(m_presentlock);
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/CMakeLists.txt b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/CMakeLists.txt
index 76f18f0f20..5bb02199e7 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/CMakeLists.txt
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/CMakeLists.txt
@@ -6,15 +6,19 @@ set(HEADERS ConvolutionKernels.h
if(CORE_SYSTEM_NAME STREQUAL windows OR CORE_SYSTEM_NAME STREQUAL windowsstore)
list(APPEND SOURCES ConversionMatrix.cpp
+ ToneMappers.cpp
WinVideoFilter.cpp)
list(APPEND HEADERS ConversionMatrix.h
+ ToneMappers.h
WinVideoFilter.h)
endif()
if(TARGET OpenGL::GL OR TARGET OpenGL::GLES)
- list(APPEND SOURCES ConversionMatrix.cpp)
- list(APPEND HEADERS ConversionMatrix.h)
+ list(APPEND SOURCES ConversionMatrix.cpp
+ ToneMappers.cpp)
+ list(APPEND HEADERS ConversionMatrix.h
+ ToneMappers.h)
endif()
if(TARGET OpenGL::GL)
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.cpp
new file mode 100644
index 0000000000..f98cd71c01
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ToneMappers.h"
+
+float CToneMappers::GetLuminanceValue(bool hasDisplayMetadata,
+ const AVMasteringDisplayMetadata& displayMetadata,
+ bool hasLightMetadata,
+ const AVContentLightMetadata& lightMetadata)
+{
+ // default for bad quality HDR-PQ sources (missing or invalid metadata)
+ const float defaultLuminance = 400.0f;
+ float lum1 = defaultLuminance;
+
+ unsigned int maxLuminance = static_cast<unsigned int>(defaultLuminance);
+
+ if (hasDisplayMetadata && displayMetadata.has_luminance && displayMetadata.max_luminance.den)
+ {
+ const uint16_t lum = displayMetadata.max_luminance.num / displayMetadata.max_luminance.den;
+
+ if (lum > 0)
+ maxLuminance = lum;
+ }
+
+ if (hasLightMetadata)
+ {
+ float lum2;
+
+ if (lightMetadata.MaxCLL >= maxLuminance)
+ {
+ lum1 = static_cast<float>(maxLuminance);
+ lum2 = static_cast<float>(lightMetadata.MaxCLL);
+ }
+ else
+ {
+ lum1 = static_cast<float>(lightMetadata.MaxCLL);
+ lum2 = static_cast<float>(maxLuminance);
+ }
+ const float lum3 = static_cast<float>(lightMetadata.MaxFALL);
+
+ lum1 = (lum1 * 0.5f) + (lum2 * 0.2f) + (lum3 * 0.3f);
+ }
+ else if (hasDisplayMetadata && displayMetadata.has_luminance)
+ {
+ lum1 = static_cast<float>(maxLuminance);
+ }
+
+ return lum1;
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.h
new file mode 100644
index 0000000000..0d668838cb
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ToneMappers.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+extern "C"
+{
+#include <libavutil/mastering_display_metadata.h>
+}
+
+class CToneMappers
+{
+public:
+ static float GetLuminanceValue(bool hasDisplayMetadata,
+ const AVMasteringDisplayMetadata& displayMetadata,
+ bool hasLightMetadata,
+ const AVContentLightMetadata& lightMetadata);
+};
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp
index 83f146b248..e68976ca9c 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp
@@ -9,6 +9,7 @@
#include "WinVideoFilter.h"
#include "ConvolutionKernels.h"
+#include "ToneMappers.h"
#include "Util.h"
#include "VideoRenderers/windows/RendererBase.h"
#include "cores/VideoPlayer/VideoRenderers/VideoShaders/dither.h"
@@ -206,53 +207,24 @@ void COutputShader::ApplyEffectParameters(CD3DEffect &effect, unsigned sourceWid
}
else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_ACES)
{
- float lumin = GetLuminanceValue();
+ const float lumin = CToneMappers::GetLuminanceValue(m_hasDisplayMetadata, m_displayMetadata,
+ m_hasLightMetadata, m_lightMetadata);
effect.SetScalar("g_luminance", lumin);
effect.SetScalar("g_toneP1", m_toneMappingParam);
m_toneMappingDebug = lumin;
}
else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_HABLE)
{
- float lumin = GetLuminanceValue();
- float lumin_factor = (10000.0f / lumin) * (2.0f / m_toneMappingParam);
- float lumin_div100 = lumin / (100.0f * m_toneMappingParam);
+ const float lumin = CToneMappers::GetLuminanceValue(m_hasDisplayMetadata, m_displayMetadata,
+ m_hasLightMetadata, m_lightMetadata);
+ const float lumin_factor = (10000.0f / lumin) * (2.0f / m_toneMappingParam);
+ const float lumin_div100 = lumin / (100.0f * m_toneMappingParam);
effect.SetScalar("g_toneP1", lumin_factor);
effect.SetScalar("g_toneP2", lumin_div100);
m_toneMappingDebug = lumin;
}
}
-float COutputShader::GetLuminanceValue() const
-{
- float lum1 = 400.0f; // default for bad quality HDR-PQ sources (with no metadata)
- float lum2 = lum1;
- float lum3 = lum1;
-
- if (m_hasLightMetadata)
- {
- uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den;
- if (m_lightMetadata.MaxCLL >= lum)
- {
- lum1 = static_cast<float>(lum);
- lum2 = static_cast<float>(m_lightMetadata.MaxCLL);
- }
- else
- {
- lum1 = static_cast<float>(m_lightMetadata.MaxCLL);
- lum2 = static_cast<float>(lum);
- }
- lum3 = static_cast<float>(m_lightMetadata.MaxFALL);
- lum1 = (lum1 * 0.5f) + (lum2 * 0.2f) + (lum3 * 0.3f);
- }
- else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance)
- {
- uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den;
- lum1 = static_cast<float>(lum);
- }
-
- return lum1;
-}
-
void COutputShader::GetDefines(DefinesMap& map) const
{
if (m_useLut)
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h
index de901afcc1..d559dda519 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h
@@ -91,7 +91,6 @@ private:
void PrepareParameters(unsigned sourceWidth, unsigned sourceHeight, CRect sourceRect, const CPoint points[4]);
void SetShaderParameters(CD3DTexture &sourceTexture, unsigned range, float contrast, float brightness);
void CreateDitherView();
- float GetLuminanceValue() const;
bool m_useLut = false;
bool m_useDithering = false;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp
index 4491ad2464..e9ff471f3d 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp
@@ -12,6 +12,7 @@
#include "../RenderFlags.h"
#include "ConvolutionKernels.h"
#include "ServiceBroker.h"
+#include "ToneMappers.h"
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
#include "utils/GLUtils.h"
@@ -192,13 +193,16 @@ bool BaseYUV2RGBGLSLShader::OnEnabled()
}
else if (m_toneMappingMethod == VS_TONEMAPMETHOD_ACES)
{
- glUniform1f(m_hLuminance, GetLuminanceValue());
+ const float lumin = CToneMappers::GetLuminanceValue(m_hasDisplayMetadata, m_displayMetadata,
+ m_hasLightMetadata, m_lightMetadata);
+ glUniform1f(m_hLuminance, lumin);
glUniform1f(m_hToneP1, m_toneMappingParam);
}
else if (m_toneMappingMethod == VS_TONEMAPMETHOD_HABLE)
{
- float lumin = GetLuminanceValue();
- float param = (10000.0f / lumin) * (2.0f / m_toneMappingParam);
+ const float lumin = CToneMappers::GetLuminanceValue(m_hasDisplayMetadata, m_displayMetadata,
+ m_hasLightMetadata, m_lightMetadata);
+ const float param = (10000.0f / lumin) * (2.0f / m_toneMappingParam);
glUniform1f(m_hLuminance, lumin);
glUniform1f(m_hToneP1, param);
}
@@ -256,38 +260,6 @@ void BaseYUV2RGBGLSLShader::SetToneMapParam(ETONEMAPMETHOD method, float param)
m_toneMappingParam = param;
}
-float BaseYUV2RGBGLSLShader::GetLuminanceValue() const //Maybe move this to linuxrenderer?! same as in baserenderer
-{
- float lum1 = 400.0f; // default for bad quality HDR-PQ sources (with no metadata)
- float lum2 = lum1;
- float lum3 = lum1;
-
- if (m_hasLightMetadata)
- {
- uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den;
- if (m_lightMetadata.MaxCLL >= lum)
- {
- lum1 = static_cast<float>(lum);
- lum2 = static_cast<float>(m_lightMetadata.MaxCLL);
- }
- else
- {
- lum1 = static_cast<float>(m_lightMetadata.MaxCLL);
- lum2 = static_cast<float>(lum);
- }
- lum3 = static_cast<float>(m_lightMetadata.MaxFALL);
- lum1 = (lum1 * 0.5f) + (lum2 * 0.2f) + (lum3 * 0.3f);
- }
- else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance &&
- m_displayMetadata.max_luminance.num)
- {
- uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den;
- lum1 = static_cast<float>(lum);
- }
-
- return lum1;
-}
-
//////////////////////////////////////////////////////////////////////
// YUV2RGBProgressiveShader - YUV2RGB with no deinterlacing
// Use for weave deinterlacing / progressive
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h
index 18e10faf91..6604291049 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h
@@ -54,7 +54,6 @@ public:
bool hasLightMetadata,
AVContentLightMetadata lightMetadata);
void SetToneMapParam(ETONEMAPMETHOD method, float param);
- float GetLuminanceValue() const;
void SetConvertFullColorRange(bool convertFullRange) { m_convertFullRange = convertFullRange; }
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp
index 4682a3db1a..63e4d304a4 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp
@@ -10,6 +10,7 @@
#include "YUV2RGBShaderGLES.h"
#include "../RenderFlags.h"
+#include "ToneMappers.h"
#include "settings/AdvancedSettings.h"
#include "utils/GLUtils.h"
#include "utils/log.h"
@@ -158,13 +159,16 @@ bool BaseYUV2RGBGLSLShader::OnEnabled()
}
else if (m_toneMappingMethod == VS_TONEMAPMETHOD_ACES)
{
- glUniform1f(m_hLuminance, GetLuminanceValue());
+ const float lumin = CToneMappers::GetLuminanceValue(m_hasDisplayMetadata, m_displayMetadata,
+ m_hasLightMetadata, m_lightMetadata);
+ glUniform1f(m_hLuminance, lumin);
glUniform1f(m_hToneP1, m_toneMappingParam);
}
else if (m_toneMappingMethod == VS_TONEMAPMETHOD_HABLE)
{
- float lumin = GetLuminanceValue();
- float param = (10000.0f / lumin) * (2.0f / m_toneMappingParam);
+ const float lumin = CToneMappers::GetLuminanceValue(m_hasDisplayMetadata, m_displayMetadata,
+ m_hasLightMetadata, m_lightMetadata);
+ const float param = (10000.0f / lumin) * (2.0f / m_toneMappingParam);
glUniform1f(m_hLuminance, lumin);
glUniform1f(m_hToneP1, param);
}
@@ -211,38 +215,6 @@ void BaseYUV2RGBGLSLShader::SetDisplayMetadata(bool hasDisplayMetadata,
m_lightMetadata = lightMetadata;
}
-float BaseYUV2RGBGLSLShader::GetLuminanceValue() const
-{
- float lum1 = 400.0f; // default for bad quality HDR-PQ sources (with no metadata)
- float lum2 = lum1;
- float lum3 = lum1;
-
- if (m_hasLightMetadata)
- {
- uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den;
- if (m_lightMetadata.MaxCLL >= lum)
- {
- lum1 = static_cast<float>(lum);
- lum2 = static_cast<float>(m_lightMetadata.MaxCLL);
- }
- else
- {
- lum1 = static_cast<float>(m_lightMetadata.MaxCLL);
- lum2 = static_cast<float>(lum);
- }
- lum3 = static_cast<float>(m_lightMetadata.MaxFALL);
- lum1 = (lum1 * 0.5f) + (lum2 * 0.2f) + (lum3 * 0.3f);
- }
- else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance &&
- m_displayMetadata.max_luminance.num > 0)
- {
- uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den;
- lum1 = static_cast<float>(lum);
- }
-
- return lum1;
-}
-
//////////////////////////////////////////////////////////////////////
// YUV2RGBProgressiveShader - YUV2RGB with no deinterlacing
// Use for weave deinterlacing / progressive
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h
index df369f518f..44b1a8221a 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h
@@ -45,7 +45,6 @@ class BaseYUV2RGBGLSLShader : public CGLSLShaderProgram
bool hasLightMetadata,
AVContentLightMetadata lightMetadata);
void SetToneMapParam(float param) { m_toneMappingParam = param; }
- float GetLuminanceValue() const;
GLint GetVertexLoc() { return m_hVertex; }
GLint GetYcoordLoc() { return m_hYcoord; }
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/WinRenderer.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/WinRenderer.cpp
index 3ec27f8c6d..777600c139 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/WinRenderer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/WinRenderer.cpp
@@ -272,23 +272,10 @@ bool CWinRenderer::Flush(bool saveBuffers)
bool CWinRenderer::Supports(ERENDERFEATURE feature) const
{
- if(feature == RENDERFEATURE_BRIGHTNESS)
- return true;
-
- if(feature == RENDERFEATURE_CONTRAST)
- return true;
-
- if (feature == RENDERFEATURE_STRETCH ||
- feature == RENDERFEATURE_NONLINSTRETCH ||
- feature == RENDERFEATURE_ZOOM ||
- feature == RENDERFEATURE_VERTICAL_SHIFT ||
- feature == RENDERFEATURE_PIXEL_RATIO ||
- feature == RENDERFEATURE_ROTATION ||
- feature == RENDERFEATURE_POSTPROCESS ||
- feature == RENDERFEATURE_TONEMAP)
- return true;
+ if (!m_bConfigured)
+ return false;
- return false;
+ return m_renderer->Supports(feature);
}
bool CWinRenderer::Supports(ESCALINGMETHOD method) const
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp
index 4156c403af..ac57da7d20 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp
@@ -28,6 +28,7 @@ void CRenderBuffer::AppendPicture(const VideoPicture& picture)
videoBuffer->Acquire();
pictureFlags = picture.iFlags;
+ m_originalPrimaries = picture.m_originalColorPrimaries;
primaries = picture.color_primaries;
color_space = picture.color_space;
color_transfer = picture.color_transfer;
@@ -674,11 +675,14 @@ DEBUG_INFO_VIDEO CRendererBase::GetDebugInfo(int idx)
const char* px = av_get_pix_fmt_name(rb->pixelFormat);
const char* pr = av_color_primaries_name(rb->primaries);
+ const char* opr = av_color_primaries_name(rb->m_originalPrimaries);
const char* tr = av_color_transfer_name(rb->color_transfer);
const std::string pixel = px ? px : "unknown";
- const std::string prim = pr ? pr : "unknown";
+ const std::string oprim = opr ? opr : "unknown";
const std::string trans = tr ? tr : "unknown";
+ const std::string prim =
+ (rb->primaries != rb->m_originalPrimaries) ? (oprim + ">" + (pr ? pr : "unknown")) : oprim;
const int max = static_cast<int>(std::exp2(rb->bits));
const int range_min = rb->full_range ? 0 : (max * 16) / 256;
@@ -770,3 +774,15 @@ bool CRendererBase::IntendToRenderAsHDR(const VideoPicture& picture)
return streamIsHDR && canDisplayHDR;
}
+
+bool CRendererBase::Supports(ERENDERFEATURE feature) const
+{
+ if (feature == RENDERFEATURE_BRIGHTNESS || feature == RENDERFEATURE_CONTRAST ||
+ feature == RENDERFEATURE_STRETCH || feature == RENDERFEATURE_NONLINSTRETCH ||
+ feature == RENDERFEATURE_ZOOM || feature == RENDERFEATURE_VERTICAL_SHIFT ||
+ feature == RENDERFEATURE_PIXEL_RATIO || feature == RENDERFEATURE_ROTATION ||
+ feature == RENDERFEATURE_POSTPROCESS || feature == RENDERFEATURE_TONEMAP)
+ return true;
+
+ return false;
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h
index 475d471b96..ce0087f607 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h
@@ -76,6 +76,7 @@ public:
AVPixelFormat av_format;
CVideoBuffer* videoBuffer = nullptr;
unsigned int pictureFlags = 0;
+ AVColorPrimaries m_originalPrimaries = AVCOL_PRI_BT709;
AVColorPrimaries primaries = AVCOL_PRI_BT709;
AVColorSpace color_space = AVCOL_SPC_BT709;
AVColorTransferCharacteristic color_transfer = AVCOL_TRC_BT709;
@@ -115,6 +116,8 @@ public:
virtual CRenderInfo GetRenderInfo();
virtual bool Configure(const VideoPicture &picture, float fps, unsigned int orientation);
virtual bool Supports(ESCALINGMETHOD method) const = 0;
+ virtual bool Supports(ERENDERFEATURE feature) const;
+
virtual bool WantsDoublePass() { return false; }
virtual bool NeedBuffer(int idx) { return false; }
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp
index 6a2d0d955a..2ef7543242 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp
@@ -304,6 +304,20 @@ void CRendererDXVA::FillBuffersSet(CRenderBuffer* (&buffers)[8])
}
}
+bool CRendererDXVA::Supports(ERENDERFEATURE feature) const
+{
+ if (feature == RENDERFEATURE_BRIGHTNESS || feature == RENDERFEATURE_CONTRAST ||
+ feature == RENDERFEATURE_ROTATION)
+ {
+ if (m_processor)
+ return m_processor->Supports(feature);
+
+ return false;
+ }
+
+ return CRendererBase::Supports(feature);
+}
+
bool CRendererDXVA::Supports(ESCALINGMETHOD method) const
{
if (method == VS_SCALINGMETHOD_DXVA_HARDWARE)
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h
index 46fe5adef0..02708d6d18 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h
@@ -29,6 +29,7 @@ public:
CRenderInfo GetRenderInfo() override;
bool Supports(ESCALINGMETHOD method) const override;
+ bool Supports(ERENDERFEATURE feature) const override;
bool WantsDoublePass() override { return true; }
bool Configure(const VideoPicture& picture, float fps, unsigned orientation) override;
bool NeedBuffer(int idx) override;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.cpp
index 6f000855d8..c79e7689b8 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.cpp
@@ -98,6 +98,7 @@ bool CRendererShaders::Configure(const VideoPicture& picture, float fps, unsigne
if (!IsHWPicSupported(picture))
m_format = GetAVFormat(dxgi_format);
}
+ m_srcPrimaries = picture.color_primaries;
CreateIntermediateTarget(m_sourceWidth, m_sourceHeight, false, CalcIntermediateTargetFormat());
return true;
@@ -136,11 +137,10 @@ void CRendererShaders::CheckVideoParameters()
__super::CheckVideoParameters();
CRenderBuffer* buf = m_renderBuffers[m_iBufferIndex];
- const AVColorPrimaries srcPrim = GetSrcPrimaries(buf->primaries, buf->GetWidth(), buf->GetHeight());
- if (srcPrim != m_srcPrimaries)
+ if (buf->primaries != m_srcPrimaries)
{
// source params is changed, reset shader
- m_srcPrimaries = srcPrim;
+ m_srcPrimaries = buf->primaries;
m_colorShader.reset();
}
}
@@ -189,19 +189,6 @@ bool CRendererShaders::IsHWPicSupported(const VideoPicture& picture)
return false;
}
-AVColorPrimaries CRendererShaders::GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned width, unsigned height)
-{
- AVColorPrimaries ret = srcPrimaries;
- if (ret == AVCOL_PRI_UNSPECIFIED)
- {
- if (width > 1024 || height >= 600)
- ret = AVCOL_PRI_BT709;
- else
- ret = AVCOL_PRI_BT470BG;
- }
- return ret;
-}
-
DXGI_FORMAT CRendererShaders::CalcIntermediateTargetFormat() const
{
// Default value: same as the back buffer
diff --git a/xbmc/cores/paplayer/PAPlayer.cpp b/xbmc/cores/paplayer/PAPlayer.cpp
index 496596efce..1165b1c480 100644
--- a/xbmc/cores/paplayer/PAPlayer.cpp
+++ b/xbmc/cores/paplayer/PAPlayer.cpp
@@ -29,6 +29,7 @@
#include "utils/log.h"
#include "video/Bookmark.h"
+#include <memory>
#include <mutex>
using namespace std::chrono_literals;
@@ -296,7 +297,7 @@ bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn)
file.GetStartOffset() == m_currentStream->m_fileItem->GetEndOffset() && m_currentStream &&
m_currentStream->m_prepareTriggered)
{
- m_currentStream->m_nextFileItem.reset(new CFileItem(file));
+ m_currentStream->m_nextFileItem = std::make_unique<CFileItem>(file);
m_upcomingCrossfadeMS = 0;
return true;
}
diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
index 419d34fd18..8f5d0ccd7f 100644
--- a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
+++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
@@ -146,10 +146,13 @@ void CPlayerCoreFactory::GetPlayers(const CFileItem& item, std::vector<std::stri
int idx = GetPlayerIndex("videodefaultplayer");
if (idx > -1)
{
- std::string eVideoDefault = GetPlayerName(idx);
- CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding videodefaultplayer ({})",
- eVideoDefault);
- players.push_back(eVideoDefault);
+ const std::string videoDefault = GetPlayerName(idx);
+ if (std::find(players.cbegin(), players.cend(), videoDefault) == players.cend())
+ {
+ players.emplace_back(videoDefault);
+ CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding videodefaultplayer ({})",
+ videoDefault);
+ }
}
GetPlayers(players, false, true); // Video-only players
GetPlayers(players, true, true); // Audio & video players
@@ -163,10 +166,13 @@ void CPlayerCoreFactory::GetPlayers(const CFileItem& item, std::vector<std::stri
int idx = GetPlayerIndex("audiodefaultplayer");
if (idx > -1)
{
- std::string eAudioDefault = GetPlayerName(idx);
- CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding audiodefaultplayer ({})",
- eAudioDefault);
- players.push_back(eAudioDefault);
+ const std::string audioDefault = GetPlayerName(idx);
+ if (std::find(players.cbegin(), players.cend(), audioDefault) == players.cend())
+ {
+ players.emplace_back(audioDefault);
+ CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding audiodefaultplayer ({})",
+ audioDefault);
+ }
}
GetPlayers(players, true, false); // Audio-only players
GetPlayers(players, true, true); // Audio & video players
@@ -248,6 +254,11 @@ std::string CPlayerCoreFactory::GetPlayerType(const std::string& player) const
return m_vecPlayerConfigs[idx]->m_type;
}
+bool CPlayerCoreFactory::IsExternalPlayer(const std::string& player) const
+{
+ return (GetPlayerType(player) == "external");
+}
+
bool CPlayerCoreFactory::PlaysAudio(const std::string& player) const
{
std::unique_lock<CCriticalSection> lock(m_section);
diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.h b/xbmc/cores/playercorefactory/PlayerCoreFactory.h
index e2963e8a36..50f45c624b 100644
--- a/xbmc/cores/playercorefactory/PlayerCoreFactory.h
+++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.h
@@ -44,6 +44,7 @@ public:
void GetPlayers(std::vector<std::string>&players, std::string &type) const;
void GetRemotePlayers(std::vector<std::string>&players) const; //All remote players we can attach to
std::string GetPlayerType(const std::string &player) const;
+ bool IsExternalPlayer(const std::string& player) const;
bool PlaysAudio(const std::string &player) const;
bool PlaysVideo(const std::string &player) const;
diff --git a/xbmc/dbwrappers/Database.cpp b/xbmc/dbwrappers/Database.cpp
index 1c2c48c18b..dc17d4d843 100644
--- a/xbmc/dbwrappers/Database.cpp
+++ b/xbmc/dbwrappers/Database.cpp
@@ -12,6 +12,9 @@
#include "DbUrl.h"
#include "ServiceBroker.h"
#include "filesystem/SpecialProtocol.h"
+#if defined(HAS_MYSQL) || defined(HAS_MARIADB)
+#include "mysqldataset.h"
+#endif
#include "profiles/ProfileManager.h"
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
@@ -20,14 +23,12 @@
#include "utils/StringUtils.h"
#include "utils/log.h"
-#if defined(HAS_MYSQL) || defined(HAS_MARIADB)
-#include "mysqldataset.h"
-#endif
-
#ifdef TARGET_POSIX
#include "platform/posix/ConvUtils.h"
#endif
+#include <memory>
+
using namespace dbiplus;
#define MAX_COMPRESS_COUNT 20
@@ -271,7 +272,8 @@ std::string CDatabase::PrepareSQL(std::string strStmt, ...) const
return strResult;
}
-std::string CDatabase::GetSingleValue(const std::string& query, std::unique_ptr<Dataset>& ds)
+std::string CDatabase::GetSingleValue(const std::string& query,
+ const std::unique_ptr<Dataset>& ds) const
{
std::string ret;
try
@@ -294,7 +296,7 @@ std::string CDatabase::GetSingleValue(const std::string& query, std::unique_ptr<
std::string CDatabase::GetSingleValue(const std::string& strTable,
const std::string& strColumn,
const std::string& strWhereClause /* = std::string() */,
- const std::string& strOrderBy /* = std::string() */)
+ const std::string& strOrderBy /* = std::string() */) const
{
std::string query = PrepareSQL("SELECT %s FROM %s", strColumn.c_str(), strTable.c_str());
if (!strWhereClause.empty())
@@ -305,12 +307,12 @@ std::string CDatabase::GetSingleValue(const std::string& strTable,
return GetSingleValue(query, m_pDS);
}
-std::string CDatabase::GetSingleValue(const std::string& query)
+std::string CDatabase::GetSingleValue(const std::string& query) const
{
return GetSingleValue(query, m_pDS);
}
-int CDatabase::GetSingleValueInt(const std::string& query, std::unique_ptr<Dataset>& ds)
+int CDatabase::GetSingleValueInt(const std::string& query, const std::unique_ptr<Dataset>& ds) const
{
int ret = 0;
try
@@ -333,13 +335,13 @@ int CDatabase::GetSingleValueInt(const std::string& query, std::unique_ptr<Datas
int CDatabase::GetSingleValueInt(const std::string& strTable,
const std::string& strColumn,
const std::string& strWhereClause /* = std::string() */,
- const std::string& strOrderBy /* = std::string() */)
+ const std::string& strOrderBy /* = std::string() */) const
{
std::string strResult = GetSingleValue(strTable, strColumn, strWhereClause, strOrderBy);
return static_cast<int>(strtol(strResult.c_str(), NULL, 10));
}
-int CDatabase::GetSingleValueInt(const std::string& query)
+int CDatabase::GetSingleValueInt(const std::string& query) const
{
return GetSingleValueInt(query, m_pDS);
}
@@ -583,12 +585,12 @@ bool CDatabase::Connect(const std::string& dbName, const DatabaseSettings& dbSet
// create the appropriate database structure
if (dbSettings.type == "sqlite3")
{
- m_pDB.reset(new SqliteDatabase());
+ m_pDB = std::make_unique<SqliteDatabase>();
}
#if defined(HAS_MYSQL) || defined(HAS_MARIADB)
else if (dbSettings.type == "mysql")
{
- m_pDB.reset(new MysqlDatabase());
+ m_pDB = std::make_unique<MysqlDatabase>();
}
#endif
else
@@ -814,7 +816,9 @@ void CDatabase::UpdateVersionNumber()
m_pDS->exec(strSQL);
}
-bool CDatabase::BuildSQL(const std::string& strQuery, const Filter& filter, std::string& strSQL)
+bool CDatabase::BuildSQL(const std::string& strQuery,
+ const Filter& filter,
+ std::string& strSQL) const
{
strSQL = strQuery;
diff --git a/xbmc/dbwrappers/Database.h b/xbmc/dbwrappers/Database.h
index 2f3d35db29..edca0f7b19 100644
--- a/xbmc/dbwrappers/Database.h
+++ b/xbmc/dbwrappers/Database.h
@@ -124,15 +124,16 @@ public:
std::string GetSingleValue(const std::string& strTable,
const std::string& strColumn,
const std::string& strWhereClause = std::string(),
- const std::string& strOrderBy = std::string());
- std::string GetSingleValue(const std::string& query);
+ const std::string& strOrderBy = std::string()) const;
+ std::string GetSingleValue(const std::string& query) const;
/*! \brief Get a single value from a query on a dataset.
\param query the query in question.
\param ds the dataset to use for the query.
\return the value from the query, empty on failure.
*/
- std::string GetSingleValue(const std::string& query, std::unique_ptr<dbiplus::Dataset>& ds);
+ std::string GetSingleValue(const std::string& query,
+ const std::unique_ptr<dbiplus::Dataset>& ds) const;
/*!
* @brief Get a single integer value from a table.
@@ -146,15 +147,16 @@ public:
int GetSingleValueInt(const std::string& strTable,
const std::string& strColumn,
const std::string& strWhereClause = std::string(),
- const std::string& strOrderBy = std::string());
- int GetSingleValueInt(const std::string& query);
+ const std::string& strOrderBy = std::string()) const;
+ int GetSingleValueInt(const std::string& query) const;
/*! \brief Get a single integer value from a query on a dataset.
\param query the query in question.
\param ds the dataset to use for the query.
\return the value from the query, 0 on failure.
*/
- int GetSingleValueInt(const std::string& query, std::unique_ptr<dbiplus::Dataset>& ds);
+ int GetSingleValueInt(const std::string& query,
+ const std::unique_ptr<dbiplus::Dataset>& ds) const;
/*!
* @brief Delete values from a table.
@@ -294,7 +296,7 @@ protected:
int GetDBVersion();
- bool BuildSQL(const std::string& strQuery, const Filter& filter, std::string& strSQL);
+ bool BuildSQL(const std::string& strQuery, const Filter& filter, std::string& strSQL) const;
bool m_sqlite; ///< \brief whether we use sqlite (defaults to true)
diff --git a/xbmc/dialogs/GUIDialogBusy.cpp b/xbmc/dialogs/GUIDialogBusy.cpp
index e53f1fca82..aa9b8d0121 100644
--- a/xbmc/dialogs/GUIDialogBusy.cpp
+++ b/xbmc/dialogs/GUIDialogBusy.cpp
@@ -127,7 +127,7 @@ void CGUIDialogBusy::DoProcess(unsigned int currentTime, CDirtyRegionList &dirty
{
bool visible = CServiceBroker::GetGUI()->GetWindowManager().IsModalDialogTopmost(WINDOW_DIALOG_BUSY);
if(!visible && m_bLastVisible)
- dirtyregions.push_back(CDirtyRegion(m_renderRegion));
+ dirtyregions.emplace_back(m_renderRegion);
m_bLastVisible = visible;
CGUIDialog::DoProcess(currentTime, dirtyregions);
diff --git a/xbmc/dialogs/GUIDialogBusyNoCancel.cpp b/xbmc/dialogs/GUIDialogBusyNoCancel.cpp
index 2bddd01e41..a223351471 100644
--- a/xbmc/dialogs/GUIDialogBusyNoCancel.cpp
+++ b/xbmc/dialogs/GUIDialogBusyNoCancel.cpp
@@ -33,7 +33,7 @@ void CGUIDialogBusyNoCancel::DoProcess(unsigned int currentTime, CDirtyRegionLis
{
bool visible = CServiceBroker::GetGUI()->GetWindowManager().IsModalDialogTopmost(WINDOW_DIALOG_BUSY_NOCANCEL);
if (!visible && m_bLastVisible)
- dirtyregions.push_back(CDirtyRegion(m_renderRegion));
+ dirtyregions.emplace_back(m_renderRegion);
m_bLastVisible = visible;
CGUIDialog::DoProcess(currentTime, dirtyregions);
diff --git a/xbmc/dialogs/GUIDialogColorPicker.cpp b/xbmc/dialogs/GUIDialogColorPicker.cpp
index a73e47b142..5b6842996b 100644
--- a/xbmc/dialogs/GUIDialogColorPicker.cpp
+++ b/xbmc/dialogs/GUIDialogColorPicker.cpp
@@ -19,6 +19,7 @@
#include "utils/log.h"
#include <algorithm>
+#include <memory>
#include <utility>
#include <vector>
@@ -143,7 +144,7 @@ void CGUIDialogColorPicker::Reset()
void CGUIDialogColorPicker::AddItem(const CFileItem& item)
{
- m_vecList->Add(CFileItemPtr(new CFileItem(item)));
+ m_vecList->Add(std::make_shared<CFileItem>(item));
}
void CGUIDialogColorPicker::SetItems(const CFileItemList& pList)
diff --git a/xbmc/dialogs/GUIDialogContextMenu.h b/xbmc/dialogs/GUIDialogContextMenu.h
index 8389449f94..cd682be73c 100644
--- a/xbmc/dialogs/GUIDialogContextMenu.h
+++ b/xbmc/dialogs/GUIDialogContextMenu.h
@@ -48,9 +48,7 @@ enum CONTEXT_BUTTON
CONTEXT_BUTTON_MOVE_ITEM_UP,
CONTEXT_BUTTON_MOVE_ITEM_DOWN,
CONTEXT_BUTTON_CLEAR,
- CONTEXT_BUTTON_QUEUE_ITEM,
CONTEXT_BUTTON_PLAY_ITEM,
- CONTEXT_BUTTON_PLAY_WITH,
CONTEXT_BUTTON_PLAY_PARTYMODE,
CONTEXT_BUTTON_PLAY_PART,
CONTEXT_BUTTON_EDIT,
@@ -84,12 +82,13 @@ enum CONTEXT_BUTTON
CONTEXT_BUTTON_TAGS_REMOVE_ITEMS,
CONTEXT_BUTTON_SET_MOVIESET,
CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS,
- CONTEXT_BUTTON_BROWSE_INTO,
CONTEXT_BUTTON_EDIT_SORTTITLE,
CONTEXT_BUTTON_DELETE_ALL,
CONTEXT_BUTTON_HELP,
CONTEXT_BUTTON_PLAY_NEXT,
CONTEXT_BUTTON_NAVIGATE,
+ CONTEXT_BUTTON_CONVERT_VIDEOVERSION,
+ CONTEXT_BUTTON_MANAGE_VIDEOVERSION,
};
class CContextButtons : public std::vector< std::pair<size_t, std::string> >
diff --git a/xbmc/dialogs/GUIDialogMediaFilter.cpp b/xbmc/dialogs/GUIDialogMediaFilter.cpp
index 498562789b..14340c85bb 100644
--- a/xbmc/dialogs/GUIDialogMediaFilter.cpp
+++ b/xbmc/dialogs/GUIDialogMediaFilter.cpp
@@ -437,9 +437,9 @@ void CGUIDialogMediaFilter::InitializeSettings()
value = filter.rule->m_operator == CDatabaseQueryRule::OPERATOR_TRUE ? CHECK_YES : CHECK_NO;
TranslatableIntegerSettingOptions entries;
- entries.push_back(TranslatableIntegerSettingOption(CHECK_LABEL_ALL, CHECK_ALL));
- entries.push_back(TranslatableIntegerSettingOption(CHECK_LABEL_NO, CHECK_NO));
- entries.push_back(TranslatableIntegerSettingOption(CHECK_LABEL_YES, CHECK_YES));
+ entries.emplace_back(CHECK_LABEL_ALL, CHECK_ALL);
+ entries.emplace_back(CHECK_LABEL_NO, CHECK_NO);
+ entries.emplace_back(CHECK_LABEL_YES, CHECK_YES);
filter.setting = AddSpinner(group, settingId, filter.label, SettingLevel::Basic, value, entries, true);
}
diff --git a/xbmc/dialogs/GUIDialogSelect.cpp b/xbmc/dialogs/GUIDialogSelect.cpp
index 0f125b2884..daa11d551d 100644
--- a/xbmc/dialogs/GUIDialogSelect.cpp
+++ b/xbmc/dialogs/GUIDialogSelect.cpp
@@ -14,6 +14,8 @@
#include "input/Key.h"
#include "utils/StringUtils.h"
+#include <memory>
+
#define CONTROL_HEADING 1
#define CONTROL_NUMBER_OF_ITEMS 2
#define CONTROL_SIMPLE_LIST 3
@@ -195,7 +197,7 @@ int CGUIDialogSelect::Add(const std::string& strLabel)
int CGUIDialogSelect::Add(const CFileItem& item)
{
- m_vecList->Add(CFileItemPtr(new CFileItem(item)));
+ m_vecList->Add(std::make_shared<CFileItem>(item));
return m_vecList->Size() - 1;
}
@@ -217,7 +219,7 @@ const CFileItemPtr CGUIDialogSelect::GetSelectedFileItem() const
{
if (m_selectedItem)
return m_selectedItem;
- return CFileItemPtr(new CFileItem);
+ return std::make_shared<CFileItem>();
}
const std::vector<int>& CGUIDialogSelect::GetSelectedItems() const
diff --git a/xbmc/events/EventLogManager.cpp b/xbmc/events/EventLogManager.cpp
index d56b88acb0..8fc306c257 100644
--- a/xbmc/events/EventLogManager.cpp
+++ b/xbmc/events/EventLogManager.cpp
@@ -10,6 +10,7 @@
#include "EventLog.h"
+#include <memory>
#include <mutex>
#include <utility>
@@ -20,7 +21,7 @@ CEventLog& CEventLogManager::GetEventLog(unsigned int profileId)
auto eventLog = m_eventLogs.find(profileId);
if (eventLog == m_eventLogs.end())
{
- m_eventLogs.insert(std::make_pair(profileId, std::unique_ptr<CEventLog>(new CEventLog)));
+ m_eventLogs.insert(std::make_pair(profileId, std::make_unique<CEventLog>()));
eventLog = m_eventLogs.find(profileId);
}
diff --git a/xbmc/favourites/CMakeLists.txt b/xbmc/favourites/CMakeLists.txt
index beee8bd679..4397a1db79 100644
--- a/xbmc/favourites/CMakeLists.txt
+++ b/xbmc/favourites/CMakeLists.txt
@@ -1,5 +1,4 @@
set(SOURCES ContextMenus.cpp
- GUIDialogFavourites.cpp
GUIViewStateFavourites.cpp
GUIWindowFavourites.cpp
FavouritesService.cpp
@@ -7,7 +6,6 @@ set(SOURCES ContextMenus.cpp
FavouritesUtils.cpp)
set(HEADERS ContextMenus.h
- GUIDialogFavourites.h
GUIViewStateFavourites.h
GUIWindowFavourites.h
FavouritesService.h
diff --git a/xbmc/favourites/ContextMenus.cpp b/xbmc/favourites/ContextMenus.cpp
index 821841f7a7..13be28f862 100644
--- a/xbmc/favourites/ContextMenus.cpp
+++ b/xbmc/favourites/ContextMenus.cpp
@@ -8,73 +8,208 @@
#include "ContextMenus.h"
+#include "ContextMenuManager.h"
#include "FileItem.h"
#include "ServiceBroker.h"
#include "favourites/FavouritesService.h"
+#include "favourites/FavouritesURL.h"
#include "favourites/FavouritesUtils.h"
+#include "guilib/LocalizeStrings.h"
#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/guilib/GUIContentUtils.h"
+#include "video/VideoUtils.h"
-namespace CONTEXTMENU
+using namespace CONTEXTMENU;
+
+bool CFavouriteContextMenuAction::IsVisible(const CFileItem& item) const
{
- bool CFavouriteContextMenuAction::IsVisible(const CFileItem& item) const
- {
- return URIUtils::IsProtocol(item.GetPath(), "favourites");
- }
+ return URIUtils::IsProtocol(item.GetPath(), "favourites");
+}
- bool CFavouriteContextMenuAction::Execute(const std::shared_ptr<CFileItem>& item) const
- {
- CFileItemList items;
- CServiceBroker::GetFavouritesService().GetAll(items);
- for (const auto& favourite : items)
- {
- if (favourite->GetPath() == item->GetPath())
- {
- if (DoExecute(items, favourite))
- return CServiceBroker::GetFavouritesService().Save(items);
- }
- }
- return false;
- }
+bool CFavouriteContextMenuAction::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ CFileItemList items;
+ CServiceBroker::GetFavouritesService().GetAll(items);
+
+ const auto it = std::find_if(items.cbegin(), items.cend(), [&item](const auto& favourite) {
+ return favourite->GetPath() == item->GetPath();
+ });
+
+ if ((it != items.cend()) && DoExecute(items, *it))
+ return CServiceBroker::GetFavouritesService().Save(items);
- bool CMoveUpFavourite::DoExecute(CFileItemList& items,
+ return false;
+}
+
+bool CMoveUpFavourite::DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const
+{
+ return FAVOURITES_UTILS::MoveItem(items, item, -1);
+}
+
+bool CMoveUpFavourite::IsVisible(const CFileItem& item) const
+{
+ return CFavouriteContextMenuAction::IsVisible(item) && FAVOURITES_UTILS::ShouldEnableMoveItems();
+}
+
+bool CMoveDownFavourite::DoExecute(CFileItemList& items,
const std::shared_ptr<CFileItem>& item) const
- {
- return FAVOURITES_UTILS::MoveItem(items, item, -1);
- }
+{
+ return FAVOURITES_UTILS::MoveItem(items, item, +1);
+}
- bool CMoveUpFavourite::IsVisible(const CFileItem& item) const
- {
- return CFavouriteContextMenuAction::IsVisible(item) &&
- FAVOURITES_UTILS::ShouldEnableMoveItems();
- }
+bool CMoveDownFavourite::IsVisible(const CFileItem& item) const
+{
+ return CFavouriteContextMenuAction::IsVisible(item) && FAVOURITES_UTILS::ShouldEnableMoveItems();
+}
- bool CMoveDownFavourite::DoExecute(CFileItemList& items,
- const std::shared_ptr<CFileItem>& item) const
- {
- return FAVOURITES_UTILS::MoveItem(items, item, +1);
- }
+bool CRemoveFavourite::DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const
+{
+ return FAVOURITES_UTILS::RemoveItem(items, item);
+}
+
+bool CRenameFavourite::DoExecute(CFileItemList&, const std::shared_ptr<CFileItem>& item) const
+{
+ return FAVOURITES_UTILS::ChooseAndSetNewName(*item);
+}
- bool CMoveDownFavourite::IsVisible(const CFileItem& item) const
+bool CChooseThumbnailForFavourite::DoExecute(CFileItemList&,
+ const std::shared_ptr<CFileItem>& item) const
+{
+ return FAVOURITES_UTILS::ChooseAndSetNewThumbnail(*item);
+}
+
+namespace
+{
+std::shared_ptr<CFileItem> ResolveFavouriteItem(const CFileItem& item)
+{
+ std::shared_ptr<CFileItem> targetItem{
+ CServiceBroker::GetFavouritesService().ResolveFavourite(item)};
+ if (targetItem)
+ targetItem->SetProperty("hide_add_remove_favourite", CVariant{true});
+
+ return targetItem;
+}
+
+bool IsPlayMediaFavourite(const CFileItem& item)
+{
+ if (item.IsFavourite())
{
- return CFavouriteContextMenuAction::IsVisible(item) &&
- FAVOURITES_UTILS::ShouldEnableMoveItems();
+ const CFavouritesURL favURL{item, -1};
+ if (favURL.IsValid())
+ return favURL.GetAction() == CFavouritesURL::Action::PLAY_MEDIA;
}
+ return false;
+}
- bool CRemoveFavourite::DoExecute(CFileItemList& items,
- const std::shared_ptr<CFileItem>& item) const
+bool IsActivateWindowFavourite(const CFileItem& item)
+{
+ if (item.IsFavourite())
{
- return FAVOURITES_UTILS::RemoveItem(items, item);
+ const CFavouritesURL favURL{item, -1};
+ if (favURL.IsValid())
+ return favURL.GetAction() == CFavouritesURL::Action::ACTIVATE_WINDOW;
}
+ return false;
+}
+} // unnamed namespace
+
+bool CFavouritesTargetBrowse::IsVisible(const CFileItem& item) const
+{
+ return IsActivateWindowFavourite(item);
+}
+
+bool CFavouritesTargetBrowse::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ return FAVOURITES_UTILS::ExecuteAction({*item, -1});
+}
+
+std::string CFavouritesTargetResume::GetLabel(const CFileItem& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(item)};
+ if (targetItem)
+ return VIDEO_UTILS::GetResumeString(*targetItem);
+
+ return {};
+}
- bool CRenameFavourite::DoExecute(CFileItemList&, const std::shared_ptr<CFileItem>& item) const
+bool CFavouritesTargetResume::IsVisible(const CFileItem& item) const
+{
+ if (IsPlayMediaFavourite(item))
{
- return FAVOURITES_UTILS::ChooseAndSetNewName(*item);
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(item)};
+ if (targetItem)
+ return VIDEO_UTILS::GetItemResumeInformation(*targetItem).isResumable;
}
+ return false;
+}
+
+bool CFavouritesTargetResume::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(*item)};
+ if (targetItem)
+ return FAVOURITES_UTILS::ExecuteAction({"PlayMedia", *targetItem, "resume"});
+
+ return false;
+}
- bool CChooseThumbnailForFavourite::DoExecute(CFileItemList&,
- const std::shared_ptr<CFileItem>& item) const
+std::string CFavouritesTargetPlay::GetLabel(const CFileItem& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(item)};
+ if (targetItem && VIDEO_UTILS::GetItemResumeInformation(*targetItem).isResumable)
+ return g_localizeStrings.Get(12021); // Play from beginning
+
+ return g_localizeStrings.Get(208); // Play
+}
+
+bool CFavouritesTargetPlay::IsVisible(const CFileItem& item) const
+{
+ return IsPlayMediaFavourite(item);
+}
+
+bool CFavouritesTargetPlay::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(*item)};
+ if (targetItem)
+ return FAVOURITES_UTILS::ExecuteAction({"PlayMedia", *targetItem, "noresume"});
+
+ return false;
+}
+
+bool CFavouritesTargetInfo::IsVisible(const CFileItem& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(item)};
+ if (targetItem)
+ return UTILS::GUILIB::CGUIContentUtils::HasInfoForItem(*targetItem);
+
+ return false;
+}
+
+bool CFavouritesTargetInfo::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(*item)};
+ if (targetItem)
+ return UTILS::GUILIB::CGUIContentUtils::ShowInfoForItem(*targetItem);
+
+ return false;
+}
+
+bool CFavouritesTargetContextMenu::IsVisible(const CFileItem& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(item)};
+ if (targetItem)
+ return CONTEXTMENU::HasAnyMenuItemsFor(targetItem, CContextMenuManager::MAIN);
+
+ return false;
+}
+
+bool CFavouritesTargetContextMenu::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavouriteItem(*item)};
+ if (targetItem)
{
- return FAVOURITES_UTILS::ChooseAndSetNewThumbnail(*item);
+ CONTEXTMENU::ShowFor(targetItem, CContextMenuManager::MAIN);
+ return true;
}
-
-} // namespace CONTEXTMENU
+ return false;
+}
diff --git a/xbmc/favourites/ContextMenus.h b/xbmc/favourites/ContextMenus.h
index fe1edc8629..52f505f90d 100644
--- a/xbmc/favourites/ContextMenus.h
+++ b/xbmc/favourites/ContextMenus.h
@@ -73,4 +73,44 @@ protected:
bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const override;
};
-}
+class CFavouritesTargetBrowse : public CStaticContextMenuAction
+{
+public:
+ explicit CFavouritesTargetBrowse() : CStaticContextMenuAction(37015) {} // Browse into
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& item) const override;
+};
+
+class CFavouritesTargetResume : public IContextMenuItem
+{
+public:
+ std::string GetLabel(const CFileItem& item) const override;
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& item) const override;
+};
+
+class CFavouritesTargetPlay : public IContextMenuItem
+{
+public:
+ std::string GetLabel(const CFileItem& item) const override;
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& item) const override;
+};
+
+class CFavouritesTargetInfo : public CStaticContextMenuAction
+{
+public:
+ explicit CFavouritesTargetInfo() : CStaticContextMenuAction(19033) {} // Information
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& item) const override;
+};
+
+class CFavouritesTargetContextMenu : public CStaticContextMenuAction
+{
+public:
+ explicit CFavouritesTargetContextMenu() : CStaticContextMenuAction(22082) {} // More...
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& item) const override;
+};
+
+} // namespace CONTEXTMENU
diff --git a/xbmc/favourites/FavouritesService.cpp b/xbmc/favourites/FavouritesService.cpp
index fac1cdb190..ffff4c6d9d 100644
--- a/xbmc/favourites/FavouritesService.cpp
+++ b/xbmc/favourites/FavouritesService.cpp
@@ -13,6 +13,7 @@
#include "ServiceBroker.h"
#include "Util.h"
#include "favourites/FavouritesURL.h"
+#include "input/WindowTranslator.h"
#include "profiles/ProfileManager.h"
#include "settings/SettingsComponent.h"
#include "utils/ContentUtils.h"
@@ -146,8 +147,11 @@ CFavouritesService::CFavouritesService(std::string userDataFolder) : m_favourite
void CFavouritesService::ReInit(std::string userDataFolder)
{
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+
m_userDataFolder = std::move(userDataFolder);
m_favourites.Clear();
+ m_targets.clear();
m_favourites.SetContent("favourites");
std::string favourites = "special://xbmc/system/favourites.xml";
@@ -192,6 +196,7 @@ bool CFavouritesService::Save(const CFileItemList& items)
{
std::unique_lock<CCriticalSection> lock(m_criticalSection);
m_favourites.Clear();
+ m_targets.clear();
m_favourites.Copy(items);
Persist();
}
@@ -204,27 +209,29 @@ void CFavouritesService::OnUpdated()
m_events.Publish(FavouritesUpdated{});
}
-std::string CFavouritesService::GetFavouritesUrl(const CFileItem& item, int contextWindow) const
-{
- return CFavouritesURL(item, contextWindow).GetURL();
-}
-
bool CFavouritesService::AddOrRemove(const CFileItem& item, int contextWindow)
{
- auto favUrl = GetFavouritesUrl(item, contextWindow);
{
std::unique_lock<CCriticalSection> lock(m_criticalSection);
- CFileItemPtr match = m_favourites.Get(favUrl);
+
+ const std::shared_ptr<CFileItem> match{GetFavourite(item, contextWindow)};
if (match)
- { // remove the item
+ {
+ // remove the item
+ const auto it = m_targets.find(match->GetPath());
+ if (it != m_targets.end())
+ m_targets.erase(it);
+
m_favourites.Remove(match.get());
}
else
- { // create our new favourite item
- const CFileItemPtr favourite(std::make_shared<CFileItem>(item.GetLabel()));
+ {
+ // create our new favourite item
+ const auto favourite{std::make_shared<CFileItem>(item.GetLabel())};
if (item.GetLabel().empty())
favourite->SetLabel(CUtil::GetTitleFromPath(item.GetPath(), item.m_bIsFolder));
favourite->SetArt("thumb", ContentUtils::GetPreferredArtImage(item));
+ const std::string favUrl{CFavouritesURL(item, contextWindow).GetURL()};
favourite->SetPath(favUrl);
m_favourites.Add(favourite);
}
@@ -234,10 +241,76 @@ bool CFavouritesService::AddOrRemove(const CFileItem& item, int contextWindow)
return true;
}
-bool CFavouritesService::IsFavourited(const CFileItem& item, int contextWindow) const
+std::shared_ptr<CFileItem> CFavouritesService::GetFavourite(const CFileItem& item,
+ int contextWindow) const
{
std::unique_lock<CCriticalSection> lock(m_criticalSection);
- return m_favourites.Contains(GetFavouritesUrl(item, contextWindow));
+
+ const CFavouritesURL favURL{item, contextWindow};
+ const bool isVideoDb{URIUtils::IsVideoDb(favURL.GetTarget())};
+ const bool isMusicDb{URIUtils::IsMusicDb(favURL.GetTarget())};
+
+ for (const auto& favItem : m_favourites)
+ {
+ const CFavouritesURL favItemURL{*favItem, contextWindow};
+
+ // Compare the whole target URLs
+ if (favItemURL.GetTarget() == item.GetPath())
+ return favItem;
+
+ // Compare the target URLs ignoring optional parameters
+ if (favItemURL.GetAction() == favURL.GetAction() &&
+ (favItemURL.GetAction() != CFavouritesURL::Action::ACTIVATE_WINDOW ||
+ favItemURL.GetWindowID() == favURL.GetWindowID()))
+ {
+ if (favItemURL.GetTarget() == favURL.GetTarget())
+ return favItem;
+
+ // Check videodb and musicdb paths. Might be different strings pointing to same resource!
+ // Example: "musicdb://recentlyaddedalbums/4711/" and "musicdb://recentlyplayedalbums/4711/",
+ // both pointing to same album with db id 4711.
+ if ((isVideoDb && URIUtils::IsVideoDb(favItemURL.GetTarget())) ||
+ (isMusicDb && URIUtils::IsMusicDb(favItemURL.GetTarget())))
+ {
+ const std::shared_ptr<CFileItem> targetItem{ResolveFavourite(*favItem)};
+ if (targetItem && targetItem->IsSamePath(&item))
+ return favItem;
+ }
+ }
+ }
+ return {};
+}
+
+bool CFavouritesService::IsFavourited(const CFileItem& item, int contextWindow) const
+{
+ return (GetFavourite(item, contextWindow) != nullptr);
+}
+
+std::shared_ptr<CFileItem> CFavouritesService::ResolveFavourite(const CFileItem& item) const
+{
+ if (item.IsFavourite())
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+
+ const auto it = m_targets.find(item.GetPath());
+ if (it != m_targets.end())
+ return (*it).second;
+
+ const CFavouritesURL favURL{item.GetPath()};
+ if (favURL.IsValid())
+ {
+ auto targetItem{std::make_shared<CFileItem>(favURL.GetTarget(), favURL.IsDir())};
+ targetItem->LoadDetails();
+ if (favURL.GetWindowID() != -1)
+ {
+ const std::string window{CWindowTranslator::TranslateWindow(favURL.GetWindowID())};
+ targetItem->SetProperty("targetwindow", CVariant{window});
+ }
+ m_targets.insert({item.GetPath(), targetItem});
+ return targetItem;
+ }
+ }
+ return {};
}
void CFavouritesService::GetAll(CFileItemList& items) const
diff --git a/xbmc/favourites/FavouritesService.h b/xbmc/favourites/FavouritesService.h
index 5a16459557..65f56ff0c9 100644
--- a/xbmc/favourites/FavouritesService.h
+++ b/xbmc/favourites/FavouritesService.h
@@ -12,7 +12,9 @@
#include "threads/CriticalSection.h"
#include "utils/EventStream.h"
+#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
class CFavouritesService
@@ -25,6 +27,9 @@ public:
void ReInit(std::string userDataFolder);
bool IsFavourited(const CFileItem& item, int contextWindow) const;
+ std::shared_ptr<CFileItem> GetFavourite(const CFileItem& item, int contextWindow) const;
+ std::shared_ptr<CFileItem> ResolveFavourite(const CFileItem& favItem) const;
+
void GetAll(CFileItemList& items) const;
bool AddOrRemove(const CFileItem& item, int contextWindow);
bool Save(const CFileItemList& items);
@@ -46,11 +51,10 @@ private:
void OnUpdated();
bool Persist();
- std::string GetFavouritesUrl(const CFileItem &item, int contextWindow) const;
std::string m_userDataFolder;
CFileItemList m_favourites;
+ mutable std::unordered_map<std::string, std::shared_ptr<CFileItem>> m_targets;
CEventSource<FavouritesUpdated> m_events;
mutable CCriticalSection m_criticalSection;
};
-
diff --git a/xbmc/favourites/FavouritesUtils.cpp b/xbmc/favourites/FavouritesUtils.cpp
index 8ccfca9d15..ef3844fdf2 100644
--- a/xbmc/favourites/FavouritesUtils.cpp
+++ b/xbmc/favourites/FavouritesUtils.cpp
@@ -11,12 +11,15 @@
#include "FileItem.h"
#include "ServiceBroker.h"
#include "dialogs/GUIDialogFileBrowser.h"
+#include "favourites/FavouritesURL.h"
#include "favourites/GUIWindowFavourites.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIMessage.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
#include "storage/MediaManager.h"
+#include "utils/ExecString.h"
#include "utils/Variant.h"
#include "view/GUIViewState.h"
@@ -84,15 +87,13 @@ bool MoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item, int
int nextItem = (itemPos + amount) % items.Size();
if (nextItem < 0)
{
- const auto& itemToAdd(item);
- items.Remove(itemPos);
- items.Add(itemToAdd);
+ items.Add(item);
+ items.Remove(0);
}
else if (nextItem == 0)
{
- const auto& itemToAdd(item);
- items.Remove(itemPos);
- items.AddFront(itemToAdd, 0);
+ items.AddFront(item, 0);
+ items.Remove(itemPos + 1);
}
else
{
@@ -121,4 +122,32 @@ bool ShouldEnableMoveItems()
return true;
}
+namespace
+{
+bool ExecuteAction(const std::string& execString)
+{
+ if (!execString.empty())
+ {
+ CGUIMessage message(GUI_MSG_EXECUTE, 0, 0);
+ message.SetStringParam(execString);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+ return true;
+ }
+ return false;
+}
+} // unnamed namespace
+
+bool ExecuteAction(const CExecString& execString)
+{
+ return ExecuteAction(execString.GetExecString());
+}
+
+bool ExecuteAction(const CFavouritesURL& favURL)
+{
+ if (favURL.IsValid())
+ return ExecuteAction(favURL.GetExecString());
+
+ return false;
+}
+
} // namespace FAVOURITES_UTILS
diff --git a/xbmc/favourites/FavouritesUtils.h b/xbmc/favourites/FavouritesUtils.h
index 0388b4de85..b57839dbbb 100644
--- a/xbmc/favourites/FavouritesUtils.h
+++ b/xbmc/favourites/FavouritesUtils.h
@@ -10,6 +10,8 @@
#include <memory>
+class CExecString;
+class CFavouritesURL;
class CFileItem;
class CFileItemList;
@@ -21,4 +23,7 @@ bool MoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item, int
bool RemoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item);
bool ShouldEnableMoveItems();
+bool ExecuteAction(const CExecString& execString);
+bool ExecuteAction(const CFavouritesURL& favURL);
+
} // namespace FAVOURITES_UTILS
diff --git a/xbmc/favourites/GUIDialogFavourites.cpp b/xbmc/favourites/GUIDialogFavourites.cpp
deleted file mode 100644
index 7af6d29578..0000000000
--- a/xbmc/favourites/GUIDialogFavourites.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2005-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 "GUIDialogFavourites.h"
-
-#include "ContextMenuManager.h"
-#include "ServiceBroker.h"
-#include "dialogs/GUIDialogContextMenu.h"
-#include "favourites/FavouritesURL.h"
-#include "favourites/FavouritesUtils.h"
-#include "guilib/GUIComponent.h"
-#include "guilib/GUIMessage.h"
-#include "guilib/GUIWindowManager.h"
-#include "input/actions/ActionIDs.h"
-
-#define FAVOURITES_LIST 450
-
-CGUIDialogFavourites::CGUIDialogFavourites() :
- CGUIDialog(WINDOW_DIALOG_FAVOURITES, "DialogFavourites.xml"),
- m_favouritesService(CServiceBroker::GetFavouritesService())
-{
- m_favourites = new CFileItemList;
- m_loadType = KEEP_IN_MEMORY;
-}
-
-CGUIDialogFavourites::~CGUIDialogFavourites(void)
-{
- delete m_favourites;
-}
-
-bool CGUIDialogFavourites::OnMessage(CGUIMessage &message)
-{
- if (message.GetMessage() == GUI_MSG_CLICKED)
- {
- if (message.GetSenderId() == FAVOURITES_LIST)
- {
- int item = GetSelectedItem();
- int action = message.GetParam1();
- if (action == ACTION_SELECT_ITEM || action == ACTION_MOUSE_LEFT_CLICK)
- OnClick(item);
- else if (action == ACTION_MOVE_ITEM_UP)
- OnMoveItem(item, -1);
- else if (action == ACTION_MOVE_ITEM_DOWN)
- OnMoveItem(item, 1);
- else if (action == ACTION_CONTEXT_MENU || action == ACTION_MOUSE_RIGHT_CLICK)
- OnPopupMenu(item);
- else if (action == ACTION_DELETE_ITEM)
- OnDelete(item);
- else
- return false;
- return true;
- }
- }
- else if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
- {
- CGUIDialog::OnMessage(message);
- // clear our favourites
- CGUIMessage message(GUI_MSG_LABEL_RESET, GetID(), FAVOURITES_LIST);
- OnMessage(message);
- m_favourites->Clear();
- return true;
- }
- return CGUIDialog::OnMessage(message);
-}
-
-void CGUIDialogFavourites::OnInitWindow()
-{
- m_favouritesService.GetAll(*m_favourites);
- UpdateList();
- CGUIWindow::OnInitWindow();
-}
-
-int CGUIDialogFavourites::GetSelectedItem()
-{
- CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), FAVOURITES_LIST);
- OnMessage(message);
- return message.GetParam1();
-}
-
-void CGUIDialogFavourites::OnClick(int item)
-{
- if (item < 0 || item >= m_favourites->Size())
- return;
-
- CGUIMessage message(GUI_MSG_EXECUTE, 0, GetID());
- message.SetStringParam(CFavouritesURL(*(*m_favourites)[item], GetID()).GetExecString());
-
- Close();
-
- CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
-}
-
-void CGUIDialogFavourites::OnPopupMenu(int item)
-{
- if (item < 0 || item >= m_favourites->Size())
- return;
-
- // highlight the item
- (*m_favourites)[item]->Select(true);
-
- CContextButtons choices;
- if (m_favourites->Size() > 1)
- {
- choices.Add(1, 13332); // Move up
- choices.Add(2, 13333); // Move down
- }
- choices.Add(3, 20019); // Choose thumbnail
- choices.Add(4, 118); // Rename
- choices.Add(5, 15015); // Remove
-
- CFileItemPtr itemPtr = m_favourites->Get(item);
-
- //temporary workaround until the context menu ids are removed
- const int addonItemOffset = 10000;
-
- auto addonItems = CServiceBroker::GetContextMenuManager().GetAddonItems(*itemPtr);
-
- for (size_t i = 0; i < addonItems.size(); ++i)
- choices.Add(addonItemOffset + i, addonItems[i]->GetLabel(*itemPtr));
-
- int button = CGUIDialogContextMenu::ShowAndGetChoice(choices);
-
- // unhighlight the item
- (*m_favourites)[item]->Select(false);
-
- if (button == 1)
- OnMoveItem(item, -1);
- else if (button == 2)
- OnMoveItem(item, +1);
- else if (button == 3)
- OnSetThumb(item);
- else if (button == 4)
- OnRename(item);
- else if (button == 5)
- OnDelete(item);
- else if (button >= addonItemOffset)
- CONTEXTMENU::LoopFrom(*addonItems.at(button - addonItemOffset), itemPtr);
-}
-
-void CGUIDialogFavourites::OnMoveItem(int item, int amount)
-{
- if (item < 0 || item >= m_favourites->Size() || m_favourites->Size() <= 1 || 0 == amount) return;
-
- int nextItem = (item + amount) % m_favourites->Size();
- if (nextItem < 0) nextItem += m_favourites->Size();
-
- m_favourites->Swap(item, nextItem);
- m_favouritesService.Save(*m_favourites);
-
- CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), FAVOURITES_LIST, nextItem);
- OnMessage(message);
-
- UpdateList();
-}
-
-void CGUIDialogFavourites::OnDelete(int item)
-{
- if (item < 0 || item >= m_favourites->Size())
- return;
- m_favourites->Remove(item);
- m_favouritesService.Save(*m_favourites);
-
- CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), FAVOURITES_LIST, item < m_favourites->Size() ? item : item - 1);
- OnMessage(message);
-
- UpdateList();
-}
-
-void CGUIDialogFavourites::OnRename(int item)
-{
- if (item < 0 || item >= m_favourites->Size())
- return;
-
- if (FAVOURITES_UTILS::ChooseAndSetNewName(*(*m_favourites)[item]))
- {
- m_favouritesService.Save(*m_favourites);
- UpdateList();
- }
-}
-
-void CGUIDialogFavourites::OnSetThumb(int item)
-{
- if (item < 0 || item >= m_favourites->Size())
- return;
-
- if (FAVOURITES_UTILS::ChooseAndSetNewThumbnail(*(*m_favourites)[item]))
- {
- m_favouritesService.Save(*m_favourites);
- UpdateList();
- }
-}
-
-void CGUIDialogFavourites::UpdateList()
-{
- int currentItem = GetSelectedItem();
- CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), FAVOURITES_LIST, currentItem >= 0 ? currentItem : 0, 0, m_favourites);
- OnMessage(message);
-}
-
-CFileItemPtr CGUIDialogFavourites::GetCurrentListItem(int offset)
-{
- int currentItem = GetSelectedItem();
- if (currentItem < 0 || !m_favourites->Size()) return CFileItemPtr();
-
- int item = (currentItem + offset) % m_favourites->Size();
- if (item < 0) item += m_favourites->Size();
- return (*m_favourites)[item];
-}
diff --git a/xbmc/favourites/GUIDialogFavourites.h b/xbmc/favourites/GUIDialogFavourites.h
deleted file mode 100644
index 72d26bd659..0000000000
--- a/xbmc/favourites/GUIDialogFavourites.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2005-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 "favourites/FavouritesService.h"
-#include "guilib/GUIDialog.h"
-
-class CFileItem;
-class CFileItemList;
-
-class CGUIDialogFavourites :
- public CGUIDialog
-{
-public:
- CGUIDialogFavourites(void);
- ~CGUIDialogFavourites(void) override;
- bool OnMessage(CGUIMessage &message) override;
- void OnInitWindow() override;
-
- CFileItemPtr GetCurrentListItem(int offset = 0) override;
-
- bool HasListItems() const override { return true; }
-
-protected:
- int GetSelectedItem();
- void OnClick(int item);
- void OnPopupMenu(int item);
- void OnMoveItem(int item, int amount);
- void OnDelete(int item);
- void OnRename(int item);
- void OnSetThumb(int item);
- void UpdateList();
-
-private:
- CFileItemList* m_favourites;
- CFavouritesService& m_favouritesService;
-};
diff --git a/xbmc/favourites/GUIWindowFavourites.cpp b/xbmc/favourites/GUIWindowFavourites.cpp
index 0d43b59562..10cfc0180f 100644
--- a/xbmc/favourites/GUIWindowFavourites.cpp
+++ b/xbmc/favourites/GUIWindowFavourites.cpp
@@ -8,6 +8,7 @@
#include "GUIWindowFavourites.h"
+#include "ContextMenuManager.h"
#include "FileItem.h"
#include "ServiceBroker.h"
#include "favourites/FavouritesURL.h"
@@ -20,6 +21,10 @@
#include "messaging/ApplicationMessenger.h"
#include "utils/PlayerUtils.h"
#include "utils/StringUtils.h"
+#include "utils/guilib/GUIContentUtils.h"
+#include "video/VideoUtils.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
+#include "video/guilib/VideoSelectActionProcessor.h"
CGUIWindowFavourites::CGUIWindowFavourites()
: CGUIMediaWindow(WINDOW_FAVOURITES, "MyFavourites.xml")
@@ -42,25 +47,104 @@ void CGUIWindowFavourites::OnFavouritesEvent(const CFavouritesService::Favourite
namespace
{
-bool ExecuteAction(const std::string& execute)
+class CVideoSelectActionProcessor : public VIDEO::GUILIB::CVideoSelectActionProcessorBase
{
- if (!execute.empty())
+public:
+ explicit CVideoSelectActionProcessor(const std::shared_ptr<CFileItem>& item)
+ : CVideoSelectActionProcessorBase(item)
{
- CGUIMessage message(GUI_MSG_EXECUTE, 0, 0);
- message.SetStringParam(execute);
- CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+ }
+
+protected:
+ bool OnPlayPartSelected(unsigned int part) override
+ {
+ // part numbers are 1-based
+ FAVOURITES_UTILS::ExecuteAction(
+ {"PlayMedia", *m_item, StringUtils::Format("playoffset={}", part - 1)});
return true;
}
- return false;
-}
+
+ bool OnResumeSelected() override
+ {
+ FAVOURITES_UTILS::ExecuteAction({"PlayMedia", *m_item, "resume"});
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ FAVOURITES_UTILS::ExecuteAction({"PlayMedia", *m_item, "noresume"});
+ return true;
+ }
+
+ bool OnQueueSelected() override
+ {
+ FAVOURITES_UTILS::ExecuteAction({"QueueMedia", *m_item, ""});
+ return true;
+ }
+
+ bool OnInfoSelected() override
+ {
+ return UTILS::GUILIB::CGUIContentUtils::ShowInfoForItem(*m_item);
+ }
+
+ bool OnMoreSelected() override
+ {
+ CONTEXTMENU::ShowFor(m_item, CContextMenuManager::MAIN);
+ return true;
+ }
+};
+
+class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
+{
+public:
+ explicit CVideoPlayActionProcessor(const ::std::shared_ptr<CFileItem>& item)
+ : CVideoPlayActionProcessorBase(item)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ FAVOURITES_UTILS::ExecuteAction({"PlayMedia", *m_item, "resume"});
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ FAVOURITES_UTILS::ExecuteAction({"PlayMedia", *m_item, "noresume"});
+ return true;
+ }
+};
} // namespace
-bool CGUIWindowFavourites::OnSelect(int item)
+bool CGUIWindowFavourites::OnSelect(int itemIdx)
{
- if (item < 0 || item >= m_vecItems->Size())
+ if (itemIdx < 0 || itemIdx >= m_vecItems->Size())
return false;
- return ExecuteAction(CFavouritesURL(*(*m_vecItems)[item], GetID()).GetExecString());
+ const auto item{(*m_vecItems)[itemIdx]};
+ const CFavouritesURL favURL{*item, GetID()};
+ if (!favURL.IsValid())
+ return false;
+
+ const bool isPlayMedia{favURL.GetAction() == CFavouritesURL::Action::PLAY_MEDIA};
+
+ const auto target{CServiceBroker::GetFavouritesService().ResolveFavourite(*item)};
+ if (!target)
+ return false;
+
+ CFileItem targetItem{*target};
+
+ // video select action setting is for files only, except exec func is playmedia...
+ if (targetItem.HasVideoInfoTag() && (!targetItem.m_bIsFolder || isPlayMedia))
+ {
+ CVideoSelectActionProcessor proc{std::make_shared<CFileItem>(targetItem)};
+ if (proc.ProcessDefaultAction())
+ return true;
+ }
+
+ // exec the execute string for the original (!) item
+ return FAVOURITES_UTILS::ExecuteAction(favURL);
}
bool CGUIWindowFavourites::OnAction(const CAction& action)
@@ -71,33 +155,41 @@ bool CGUIWindowFavourites::OnAction(const CAction& action)
if (action.GetID() == ACTION_PLAYER_PLAY)
{
- const CFavouritesURL favURL((*m_vecItems)[selectedItem]->GetPath());
- if (!favURL.IsValid())
+ const auto target{
+ CServiceBroker::GetFavouritesService().ResolveFavourite(*(*m_vecItems)[selectedItem])};
+ if (!target)
return false;
- // If action is playmedia, just play it
- if (favURL.GetAction() == CFavouritesURL::Action::PLAY_MEDIA)
- return ExecuteAction(favURL.GetExecString());
+ const auto item{std::make_shared<CFileItem>(*target)};
+
+ // video play action setting is for files and folders...
+ if (item->HasVideoInfoTag() || (item->m_bIsFolder && VIDEO_UTILS::IsItemPlayable(*item)))
+ {
+ CVideoPlayActionProcessor proc{item};
+ if (proc.ProcessDefaultAction())
+ return true;
+ }
- // Resolve and check the target
- const auto item = std::make_shared<CFileItem>(favURL.GetTarget(), favURL.IsDir());
if (CPlayerUtils::IsItemPlayable(*item))
{
- CFavouritesURL target(*item, {});
- if (target.GetAction() == CFavouritesURL::Action::PLAY_MEDIA)
- {
- return ExecuteAction(target.GetExecString());
- }
- else
+ CFavouritesURL target{*item, {}};
+ if (target.GetAction() != CFavouritesURL::Action::PLAY_MEDIA)
{
- // build and execute a playmedia execute string
- target = CFavouritesURL(CFavouritesURL::Action::PLAY_MEDIA,
- {StringUtils::Paramify(item->GetPath())});
- return ExecuteAction(target.GetExecString());
+ // build a playmedia execute string for given target
+ target = CFavouritesURL{CFavouritesURL::Action::PLAY_MEDIA,
+ {StringUtils::Paramify(item->GetPath())}};
}
+ return FAVOURITES_UTILS::ExecuteAction(target);
}
return false;
}
+ else if (action.GetID() == ACTION_SHOW_INFO)
+ {
+ const auto targetItem{
+ CServiceBroker::GetFavouritesService().ResolveFavourite(*(*m_vecItems)[selectedItem])};
+
+ return UTILS::GUILIB::CGUIContentUtils::ShowInfoForItem(*targetItem);
+ }
else if (action.GetID() == ACTION_MOVE_ITEM_UP)
{
if (FAVOURITES_UTILS::ShouldEnableMoveItems())
diff --git a/xbmc/filesystem/BlurayDirectory.cpp b/xbmc/filesystem/BlurayDirectory.cpp
index dbfb41bd74..b6c339a110 100644
--- a/xbmc/filesystem/BlurayDirectory.cpp
+++ b/xbmc/filesystem/BlurayDirectory.cpp
@@ -24,6 +24,7 @@
#include <array>
#include <cassert>
#include <climits>
+#include <memory>
#include <stdlib.h>
#include <string>
@@ -187,7 +188,7 @@ void CBlurayDirectory::GetRoot(CFileItemList &items)
CFileItemPtr item;
path.SetFileName(URIUtils::AddFileToFolder(m_url.GetFileName(), "titles"));
- item.reset(new CFileItem());
+ item = std::make_shared<CFileItem>();
item->SetPath(path.Get());
item->m_bIsFolder = true;
item->SetLabel(g_localizeStrings.Get(25002) /* All titles */);
@@ -202,7 +203,7 @@ void CBlurayDirectory::GetRoot(CFileItemList &items)
}
path.SetFileName("menu");
- item.reset(new CFileItem());
+ item = std::make_shared<CFileItem>();
item->SetPath(path.Get());
item->m_bIsFolder = false;
item->SetLabel(g_localizeStrings.Get(25003) /* Menus */);
diff --git a/xbmc/filesystem/Directory.cpp b/xbmc/filesystem/Directory.cpp
index daad2b2122..cf15cf0a44 100644
--- a/xbmc/filesystem/Directory.cpp
+++ b/xbmc/filesystem/Directory.cpp
@@ -305,6 +305,41 @@ bool CDirectory::GetDirectory(const CURL& url,
return false;
}
+bool CDirectory::EnumerateDirectory(const std::string& path,
+ DirectoryEnumerationCallback callback,
+ bool fileOnly /* = false */,
+ const std::string& mask /* = "" */,
+ int flags /* = DIR_FLAG_DEFAULTS */)
+{
+ CFileItemList items;
+
+ // get items in specified directory
+ if (!CDirectory::GetDirectory(path, items, mask, flags))
+ return false;
+
+ // process all files
+ for (const auto& item : items)
+ {
+ if (!item->m_bIsFolder)
+ callback(item);
+ }
+
+ // process all directories
+ for (const auto& item : items)
+ {
+ if (item->m_bIsFolder)
+ {
+ if (!fileOnly)
+ callback(item);
+
+ if (!EnumerateDirectory(item->GetPath(), callback, fileOnly, mask, flags))
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool CDirectory::Create(const std::string& strPath)
{
const CURL pathToUrl(strPath);
diff --git a/xbmc/filesystem/Directory.h b/xbmc/filesystem/Directory.h
index 0af92f3f35..443303c7ba 100644
--- a/xbmc/filesystem/Directory.h
+++ b/xbmc/filesystem/Directory.h
@@ -10,9 +10,12 @@
#include "IDirectory.h"
+#include <functional>
#include <memory>
#include <string>
+class CFileItem;
+
namespace XFILE
{
/*!
@@ -66,6 +69,14 @@ public:
, CFileItemList &items
, const CHints &hints);
+ using DirectoryEnumerationCallback = std::function<void(const std::shared_ptr<CFileItem>& item)>;
+
+ static bool EnumerateDirectory(const std::string& path,
+ DirectoryEnumerationCallback callback,
+ bool fileOnly = false,
+ const std::string& mask = "",
+ int flags = DIR_FLAG_DEFAULTS);
+
static bool Create(const std::string& strPath);
static bool Exists(const std::string& strPath, bool bUseCache = true);
static bool Remove(const std::string& strPath);
diff --git a/xbmc/filesystem/DirectoryCache.cpp b/xbmc/filesystem/DirectoryCache.cpp
index b945e4cf7c..f3fd1fb7ae 100644
--- a/xbmc/filesystem/DirectoryCache.cpp
+++ b/xbmc/filesystem/DirectoryCache.cpp
@@ -105,7 +105,7 @@ void CDirectoryCache::SetDirectory(const std::string& strPath, const CFileItemLi
CDir dir(cacheType);
dir.m_Items->Copy(items);
dir.SetLastAccess(m_accessCounter);
- m_cache.emplace(std::make_pair(storedPath, std::move(dir)));
+ m_cache.emplace(storedPath, std::move(dir));
}
void CDirectoryCache::ClearFile(const std::string& strFile)
diff --git a/xbmc/filesystem/FTPParse.cpp b/xbmc/filesystem/FTPParse.cpp
index 664cb2c290..dbbc033a40 100644
--- a/xbmc/filesystem/FTPParse.cpp
+++ b/xbmc/filesystem/FTPParse.cpp
@@ -8,9 +8,7 @@
#include "FTPParse.h"
-#include <cmath>
-
-#include <pcrecpp.h>
+#include <regex>
CFTPParse::CFTPParse()
{
@@ -44,86 +42,46 @@ time_t CFTPParse::getTime()
return m_time;
}
-void CFTPParse::setTime(const std::string& str)
+namespace
+{
+const std::string months = "janfebmaraprmayjunjulaugsepoctnovdec";
+
+// set time_struct.tm_mon from the 3-letter "abbreviated month name"
+void setMonFromName(struct tm& time_struct, const std::string& name)
{
- /* Variables used to capture patterns via the regexes */
- std::string month;
- std::string day;
- std::string year;
- std::string hour;
- std::string minute;
- std::string second;
- std::string am_or_pm;
+ std::smatch match;
+ if (std::regex_search(months, match, std::regex(name, std::regex_constants::icase)))
+ {
+ int pos = match.position();
+ if (name.size() == 3 && pos % 3 == 0)
+ time_struct.tm_mon = pos / 3;
+ }
+}
+} // namespace
+void CFTPParse::setTime(const std::string& str)
+{
/* time struct used to set the time_t variable */
struct tm time_struct = {};
- /* Regex to read Unix, NetWare and NetPresenz time format */
- pcrecpp::RE unix_re("^([A-Za-z]{3})" // month
- "\\s+(\\d{1,2})" // day of month
- "\\s+([:\\d]{4,5})$" // time of day or year
- );
-
- /* Regex to read MultiNet time format */
- pcrecpp::RE multinet_re("^(\\d{1,2})" // day of month
- "-([A-Za-z]{3})" // month
- "-(\\d{4})" // year
- "\\s+(\\d{2})" // hour
- ":(\\d{2})" // minute
- "(:(\\d{2}))?$" // second
- );
-
- /* Regex to read MSDOS time format */
- pcrecpp::RE msdos_re("^(\\d{2})" // month
- "-(\\d{2})" // day of month
- "-(\\d{2})" // year
- "\\s+(\\d{2})" // hour
- ":(\\d{2})" // minute
- "([AP]M)$" // AM or PM
- );
-
- if (unix_re.FullMatch(str, &month, &day, &year))
+ // sub matches from regex_match() calls below
+ std::smatch match;
+
+ // Regex to read Unix, NetWare and NetPresenz time format
+ if (std::regex_match(str, match,
+ std::regex("^([A-Za-z]{3})" // month
+ "\\s+(\\d{1,2})" // day of month
+ "\\s+([:\\d]{4,5})$"))) // time of day or year
{
+ auto month = match[1].str();
+ auto day = match[2].str();
+ auto year = match[3].str();
+
/* set the month */
- if (pcrecpp::RE("jan",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 0;
- else if (pcrecpp::RE("feb",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 1;
- else if (pcrecpp::RE("mar",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 2;
- else if (pcrecpp::RE("apr",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 3;
- else if (pcrecpp::RE("may",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 4;
- else if (pcrecpp::RE("jun",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 5;
- else if (pcrecpp::RE("jul",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 6;
- else if (pcrecpp::RE("aug",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 7;
- else if (pcrecpp::RE("sep",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 8;
- else if (pcrecpp::RE("oct",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 9;
- else if (pcrecpp::RE("nov",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 10;
- else if (pcrecpp::RE("dec",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 11;
+ setMonFromName(time_struct, month);
/* set the day of the month */
- time_struct.tm_mday = atoi(day.c_str());
+ time_struct.tm_mday = std::stol(day);
time_t t = time(NULL);
struct tm *current_time;
@@ -133,11 +91,11 @@ void CFTPParse::setTime(const std::string& str)
#else
current_time = localtime(&t);
#endif
- if (pcrecpp::RE("(\\d{2}):(\\d{2})").FullMatch(year, &hour, &minute))
+ if (std::regex_match(year, match, std::regex("(\\d{2}):(\\d{2})")))
{
/* set the hour and minute */
- time_struct.tm_hour = atoi(hour.c_str());
- time_struct.tm_min = atoi(minute.c_str());
+ time_struct.tm_hour = std::stol(match[1].str());
+ time_struct.tm_min = std::stol(match[2].str());
/* set the year */
if ((current_time->tm_mon - time_struct.tm_mon < 0) ||
@@ -150,261 +108,113 @@ void CFTPParse::setTime(const std::string& str)
else
{
/* set the year */
- time_struct.tm_year = atoi(year.c_str()) - 1900;
+ time_struct.tm_year = std::stol(year) - 1900;
}
-
- /* set the day of the week */
- time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
- time_struct.tm_mday,
- time_struct.tm_year + 1900);
}
- else if (multinet_re.FullMatch(str, &day, &month, &year,
- &hour, &minute, (void*)NULL, &second))
+ // Regex to read MultiNet time format
+ else if (std::regex_match(str, match,
+ std::regex("^(\\d{1,2})" // day of month
+ "-([A-Za-z]{3})" // month
+ "-(\\d{4})" // year
+ "\\s+(\\d{2})" // hour
+ ":(\\d{2})" // minute
+ "(:(\\d{2}))?$"))) // second
{
+ auto day = match[1].str();
+ auto month = match[2].str();
+ auto year = match[3].str();
+ auto hour = match[4].str();
+ auto minute = match[5].str();
+ // match[6] ignored
+ auto second = match[7].str();
+
/* set the month */
- if (pcrecpp::RE("jan",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 0;
- else if (pcrecpp::RE("feb",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 1;
- else if (pcrecpp::RE("mar",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 2;
- else if (pcrecpp::RE("apr",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 3;
- else if (pcrecpp::RE("may",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 4;
- else if (pcrecpp::RE("jun",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 5;
- else if (pcrecpp::RE("jul",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 6;
- else if (pcrecpp::RE("aug",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 7;
- else if (pcrecpp::RE("sep",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 8;
- else if (pcrecpp::RE("oct",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 9;
- else if (pcrecpp::RE("nov",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 10;
- else if (pcrecpp::RE("dec",
- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
- time_struct.tm_mon = 11;
+ setMonFromName(time_struct, month);
/* set the day of the month and year */
- time_struct.tm_mday = atoi(day.c_str());
- time_struct.tm_year = atoi(year.c_str()) - 1900;
+ time_struct.tm_mday = std::stol(day);
+ time_struct.tm_year = std::stol(year) - 1900;
/* set the hour and minute */
- time_struct.tm_hour = atoi(hour.c_str());
- time_struct.tm_min = atoi(minute.c_str());
+ time_struct.tm_hour = std::stol(hour);
+ time_struct.tm_min = std::stol(minute);
/* set the second if given*/
if (second.length() > 0)
- time_struct.tm_sec = atoi(second.c_str());
-
- /* set the day of the week */
- time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
- time_struct.tm_mday,
- time_struct.tm_year + 1900);
+ time_struct.tm_sec = std::stol(second);
}
- else if (msdos_re.FullMatch(str, &month, &day,
- &year, &hour, &minute, &am_or_pm))
+ // Regex to read MSDOS time format
+ else if (std::regex_match(str, match,
+ std::regex("^(\\d{2})" // month
+ "-(\\d{2})" // day of month
+ "-(\\d{2})" // year
+ "\\s+(\\d{2})" // hour
+ ":(\\d{2})" // minute
+ "([AP]M)$"))) // AM or PM
{
+ auto month = match[1].str();
+ auto day = match[2].str();
+ auto year = match[3].str();
+ auto hour = match[4].str();
+ auto minute = match[5].str();
+ auto am_or_pm = match[6].str();
+
/* set the month and the day of the month */
- time_struct.tm_mon = atoi(month.c_str()) - 1;
- time_struct.tm_mday = atoi(day.c_str());
+ time_struct.tm_mon = std::stol(month) - 1;
+ time_struct.tm_mday = std::stol(day);
/* set the year */
- time_struct.tm_year = atoi(year.c_str());
+ time_struct.tm_year = std::stoi(year);
if (time_struct.tm_year < 70)
time_struct.tm_year += 100;
/* set the hour */
- time_struct.tm_hour = atoi(hour.c_str());
+ time_struct.tm_hour = std::stoi(hour);
if (time_struct.tm_hour == 12)
time_struct.tm_hour -= 12;
- if (pcrecpp::RE("PM").FullMatch(am_or_pm))
+ if (am_or_pm == "PM")
time_struct.tm_hour += 12;
/* set the minute */
- time_struct.tm_min = atoi(minute.c_str());
-
- /* set the day of the week */
- time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
- time_struct.tm_mday,
- time_struct.tm_year + 1900);
+ time_struct.tm_min = std::stoi(minute);
}
/* now set m_time */
m_time = mktime(&time_struct);
}
-int CFTPParse::getDayOfWeek(int month, int date, int year)
-{
- /* Here we use the Doomsday rule to calculate the day of the week */
-
- /* First determine the anchor day */
- int anchor;
- if (year >= 1900 && year < 2000)
- anchor = 3;
- else if (year >= 2000 && year < 2100)
- anchor = 2;
- else if (year >= 2100 && year < 2200)
- anchor = 0;
- else if (year >= 2200 && year < 2300)
- anchor = 5;
- else // must have been given an invalid year :-/
- return -1;
-
- /* Now determine the doomsday */
- int y = year % 100;
- int dday =
- ((y/12 + (y % 12) + ((y % 12)/4)) % 7) + anchor;
-
- /* Determine if the given year is a leap year */
- int leap_year = 0;
- if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
- leap_year = 1;
-
- /* Now select a doomsday for the given month */
- int mdday = 1;
- if (month == 1)
- {
- if (leap_year)
- mdday = 4;
- else
- mdday = 3;
- }
- if (month == 2)
- {
- if (leap_year)
- mdday = 1;
- else
- mdday = 7;
- }
- if (month == 3)
- mdday = 7;
- if (month == 4)
- mdday = 4;
- if (month == 5)
- mdday = 9;
- if (month == 6)
- mdday = 6;
- if (month == 7)
- mdday = 11;
- if (month == 8)
- mdday = 8;
- if (month == 9)
- mdday = 5;
- if (month == 10)
- mdday = 10;
- if (month == 11)
- mdday = 9;
- if (month == 12)
- mdday = 12;
-
- /* Now calculate the day of the week
- * Sunday = 0, Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4,
- * Friday = 5, Saturday = 6.
- */
- int day_of_week = ((date - 1) % 7) - ((mdday - 1) % 7) + dday;
- if (day_of_week >= 7)
- day_of_week -= 7;
-
- return day_of_week;
-}
-
int CFTPParse::FTPParse(const std::string& str)
{
- /* Various variable to capture patterns via the regexes */
- std::string permissions;
- std::string link_count;
- std::string owner;
- std::string group;
- std::string size;
- std::string date;
- std::string name;
- std::string type;
- std::string stuff;
- std::string facts;
- std::string version;
- std::string file_id;
-
- /* Regex for standard Unix listing formats */
- pcrecpp::RE unix_re("^([-bcdlps])" // type
- "([-rwxXsStT]{9})" // permissions
- "\\s+(\\d+)" // hard link count
- "\\s+(\\w+)" // owner
- "\\s+(\\w+)" // group
- "\\s+(\\d+)" // size
- "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
- "\\s+(.+)$" // name
- );
-
- /* Regex for NetWare listing formats */
- /* See http://www.novell.com/documentation/oes/ftp_enu/data/a3ep22p.html#fbhbaijf */
- pcrecpp::RE netware_re("^([-d])" // type
- "\\s+(\\[[-SRWCIEMFA]{8}\\])" // rights
- "\\s+(\\w+)" // owner
- "\\s+(\\d+)" // size
- "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // time
- "\\s+(.+)$" // name
- );
-
- /* Regex for NetPresenz */
- /* See http://files.stairways.com/other/ftp-list-specs-info.txt */
- /* Here we will capture permissions and size if given */
- pcrecpp::RE netpresenz_re("^([-dl])" // type
- "([-rwx]{9}|)" // permissions
- "\\s+(.*)" // stuff
- "\\s+(\\d+|)" // size
- "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
- "\\s+(.+)$" // name
- );
-
- /* Regex for EPLF */
- /* See http://cr.yp.to/ftp/list/eplf.html */
- /* SAVE: "(/,|r,|s\\d+,|m\\d+,|i[\\d!#@$%^&*()]+(\\.[\\d!#@$%^&*()]+|),)+" */
- pcrecpp::RE eplf_re("^\\+" // initial "plus" sign
- "([^\\s]+)" // facts
- "\\s(.+)$" // name
- );
-
- /* Regex for MultiNet */
- /* Best documentation found was
- * http://www-sld.slac.stanford.edu/SLDWWW/workbook/vms_files.html */
- pcrecpp::RE multinet_re("^([^;]+)" // name
- ";(\\d+)" // version
- "\\s+([\\d/]+)" // file id
- "\\s+(\\d{1,2}-[A-Za-z]{3}-\\d{4}\\s+\\d{2}:\\d{2}(:\\d{2})?)" // date
- "\\s+\\[([^\\]]+)\\]" // owner,group
- "\\s+\\(([^\\)]+)\\)$" // permissions
- );
-
- /* Regex for MSDOS */
- pcrecpp::RE msdos_re("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
- "\\s+(<DIR>|[\\d]+)" // dir or size
- "\\s+(.+)$" // name
- );
-
- if (unix_re.FullMatch(str, &type, &permissions, &link_count, &owner, &group, &size, &date, &name))
+ // sub matches from regex_match() calls below
+ std::smatch match;
+
+ // Regex for standard Unix listing formats
+ if (std::regex_match(
+ str, match,
+ std::regex("^([-bcdlps])" // type
+ "([-rwxXsStT]{9})" // permissions
+ "\\s+(\\d+)" // hard link count
+ "\\s+(\\w+)" // owner
+ "\\s+(\\w+)" // group
+ "\\s+(\\d+)" // size
+ "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
+ "\\s+(.+)$"))) // name
{
- m_name = name;
- m_size = (uint64_t)strtod(size.c_str(), NULL);
- if (pcrecpp::RE("d").FullMatch(type))
+ auto type = match[1].str();
+ auto permissions = match[2].str();
+ auto link_count = match[3].str();
+ auto owner = match[4].str();
+ auto group = match[5].str();
+ auto size = match[6].str();
+ auto date = match[7].str();
+ m_name = match[8].str();
+
+ m_size = std::stoull(size);
+ if (type == "d")
m_flagtrycwd = 1;
- if (pcrecpp::RE("-").FullMatch(type))
+ if (type == "-")
m_flagtryretr = 1;
- if (pcrecpp::RE("l").FullMatch(type))
+ if (type == "l")
{
m_flagtrycwd = m_flagtryretr = 1;
// handle symlink
@@ -416,27 +226,57 @@ int CFTPParse::FTPParse(const std::string& str)
return 1;
}
- if (netware_re.FullMatch(str, &type, &permissions, &owner, &size, &date, &name))
+ // Regex for NetWare listing formats
+ // See http://www.novell.com/documentation/oes/ftp_enu/data/a3ep22p.html#fbhbaijf
+ if (std::regex_match(str, match,
+ std::regex("^([-d])" // type
+ "\\s+(\\[[-SRWCIEMFA]{8}\\])" // rights
+ "\\s+(\\w+)" // owner
+ "\\s+(\\d+)" // size
+ "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // time
+ "\\s+(.+)$"))) // name
{
- m_name = name;
- m_size = (uint64_t)strtod(size.c_str(), NULL);
- if (pcrecpp::RE("d").FullMatch(type))
+ auto type = match[1].str();
+ auto permissions = match[2].str();
+ auto owner = match[3].str();
+ auto size = match[4].str();
+ auto date = match[5].str();
+ m_name = match[6].str();
+
+ m_size = std::stoull(size);
+ if (type == "d")
m_flagtrycwd = 1;
- if (pcrecpp::RE("-").FullMatch(type))
+ if (type == "-")
m_flagtryretr = 1;
setTime(date);
return 1;
}
- if (netpresenz_re.FullMatch(str, &type, &permissions, &stuff, &size, &date, &name))
+ // Regex for NetPresenz
+ // See http://files.stairways.com/other/ftp-list-specs-info.txt
+ // Here we will capture permissions and size if given
+ if (std::regex_match(
+ str, match,
+ std::regex("^([-dl])" // type
+ "([-rwx]{9}|)" // permissions
+ "\\s+(.*)" // stuff
+ "\\s+(\\d+|)" // size
+ "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
+ "\\s+(.+)$"))) // name
{
- m_name = name;
- m_size = (uint64_t)strtod(size.c_str(), NULL);
- if (pcrecpp::RE("d").FullMatch(type))
+ auto type = match[1].str();
+ auto permissions = match[2].str();
+ auto stuff = match[3].str();
+ auto size = match[4].str();
+ auto date = match[5].str();
+ m_name = match[6].str();
+
+ m_size = std::stoull(size);
+ if (type == "d")
m_flagtrycwd = 1;
- if (pcrecpp::RE("-").FullMatch(type))
+ if (type == "-")
m_flagtryretr = 1;
- if (pcrecpp::RE("l").FullMatch(type))
+ if (type == "l")
{
m_flagtrycwd = m_flagtryretr = 1;
// handle symlink
@@ -448,27 +288,56 @@ int CFTPParse::FTPParse(const std::string& str)
return 1;
}
- if (eplf_re.FullMatch(str, &facts, &name))
+ // Regex for EPLF
+ // See http://cr.yp.to/ftp/list/eplf.html
+ // SAVE: "(/,|r,|s\\d+,|m\\d+,|i[\\d!#@$%^&*()]+(\\.[\\d!#@$%^&*()]+|),)+"
+ if (std::regex_match(str, match,
+ std::regex("^\\+" // initial "plus" sign
+ "([^\\s]+)" // facts
+ "\\s(.+)$"))) // name
{
- /* Get the type, size, and date from the facts */
- pcrecpp::RE("(\\+|,)(r|/),").PartialMatch(facts, (void*)NULL, &type);
- pcrecpp::RE("(\\+|,)s(\\d+),").PartialMatch(facts, (void*)NULL, &size);
- pcrecpp::RE("(\\+|,)m(\\d+),").PartialMatch(facts, (void*)NULL, &date);
+ auto facts = match[1].str();
+ m_name = match[2].str();
- m_name = name;
- m_size = (uint64_t)strtod(size.c_str(), NULL);
- if (pcrecpp::RE("/").FullMatch(type))
+ /* Get the type, size, and date from the facts */
+ std::regex_search(facts, match, std::regex("(?:\\+|,)(r|/),"));
+ auto type = match[1].str();
+ std::regex_search(facts, match, std::regex("(?:\\+|,)s(\\d+),"));
+ auto size = match[1].str();
+ std::regex_search(facts, match, std::regex("(?:\\+|,)m(\\d+),"));
+ auto date = match[1].str();
+
+ m_size = std::stoull(size);
+ if (type == "/")
m_flagtrycwd = 1;
- if (pcrecpp::RE("r").FullMatch(type))
+ if (type == "r")
m_flagtryretr = 1;
/* eplf stores the date in time_t format already */
- m_time = atoi(date.c_str());
+ m_time = std::stoi(date);
return 1;
}
- if (multinet_re.FullMatch(str, &name, &version, &file_id, &date, (void*)NULL, &owner, &permissions))
+ // Regex for MultiNet
+ // Best documentation found was
+ // http://www-sld.slac.stanford.edu/SLDWWW/workbook/vms_files.html
+ if (std::regex_match(
+ str, match,
+ std::regex("^([^;]+)" // name
+ ";(\\d+)" // version
+ "\\s+([\\d/]+)" // file id
+ "\\s+(\\d{1,2}-[A-Za-z]{3}-\\d{4}\\s+\\d{2}:\\d{2}(:\\d{2})?)" // date
+ "\\s+\\[([^\\]]+)\\]" // owner,group
+ "\\s+\\(([^\\)]+)\\)$"))) // permissions
{
- if (pcrecpp::RE("\\.DIR$").PartialMatch(name))
+ auto name = match[1].str();
+ auto version = match[2].str();
+ auto file_id = match[3].str();
+ auto date = match[4].str();
+ // match[5] ignored
+ auto owner = match[6].str();
+ auto permissions = match[7].str();
+
+ if (std::regex_search(name, std::regex("\\.DIR$")))
{
name.resize(name.size() - 4);
m_flagtrycwd = 1;
@@ -482,10 +351,16 @@ int CFTPParse::FTPParse(const std::string& str)
return 1;
}
- if (msdos_re.FullMatch(str, &date, &size, &name))
+ // Regex for MSDOS
+ if (std::regex_match(str, match,
+ std::regex("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
+ "\\s+(<DIR>|[\\d]+)" // dir or size
+ "\\s+(.+)$"))) // name
{
- m_name = name;
- if (pcrecpp::RE("<DIR>").FullMatch(size))
+ auto date = match[1].str();
+ auto size = match[2].str();
+ m_name = match[3].str();
+ if (size == "<DIR>")
{
m_flagtrycwd = 1;
m_size = 0;
@@ -493,7 +368,7 @@ int CFTPParse::FTPParse(const std::string& str)
else
{
m_flagtryretr = 1;
- m_size = (uint64_t)strtod(size.c_str(), NULL);
+ m_size = std::stoull(size);
}
setTime(date);
diff --git a/xbmc/filesystem/FTPParse.h b/xbmc/filesystem/FTPParse.h
index 5f56981d64..a853b02869 100644
--- a/xbmc/filesystem/FTPParse.h
+++ b/xbmc/filesystem/FTPParse.h
@@ -29,5 +29,4 @@ private:
uint64_t m_size; // number of octets
time_t m_time = 0; // modification time
void setTime(const std::string& str); // Method used to set m_time from a string
- int getDayOfWeek(int month, int date, int year); // Method to get day of week
};
diff --git a/xbmc/filesystem/File.cpp b/xbmc/filesystem/File.cpp
index 3786611a01..ed0f30f3f4 100644
--- a/xbmc/filesystem/File.cpp
+++ b/xbmc/filesystem/File.cpp
@@ -20,7 +20,7 @@
#include "application/ApplicationComponents.h"
#include "application/ApplicationPowerHandling.h"
#include "commons/Exception.h"
-#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "utils/BitstreamStats.h"
#include "utils/StringUtils.h"
@@ -287,15 +287,19 @@ bool CFile::Open(const CURL& file, const unsigned int flags)
if (URIUtils::IsDVD(pathToUrl) || URIUtils::IsBluray(pathToUrl) ||
(m_flags & READ_AUDIO_VIDEO))
{
- const unsigned int iCacheBufferMode =
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cacheBufferMode;
- if ((iCacheBufferMode == CACHE_BUFFER_MODE_INTERNET &&
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ const int cacheBufferMode = (settings)
+ ? settings->GetInt(CSettings::SETTING_FILECACHE_BUFFERMODE)
+ : CACHE_BUFFER_MODE_NETWORK;
+
+ if ((cacheBufferMode == CACHE_BUFFER_MODE_INTERNET &&
URIUtils::IsInternetStream(pathToUrl, true)) ||
- (iCacheBufferMode == CACHE_BUFFER_MODE_TRUE_INTERNET &&
+ (cacheBufferMode == CACHE_BUFFER_MODE_TRUE_INTERNET &&
URIUtils::IsInternetStream(pathToUrl, false)) ||
- (iCacheBufferMode == CACHE_BUFFER_MODE_NETWORK &&
+ (cacheBufferMode == CACHE_BUFFER_MODE_NETWORK &&
URIUtils::IsNetworkFilesystem(pathToUrl)) ||
- (iCacheBufferMode == CACHE_BUFFER_MODE_ALL &&
+ (cacheBufferMode == CACHE_BUFFER_MODE_ALL &&
(URIUtils::IsNetworkFilesystem(pathToUrl) || URIUtils::IsHD(pathToUrl))))
{
m_flags |= READ_CACHED;
diff --git a/xbmc/filesystem/FileCache.cpp b/xbmc/filesystem/FileCache.cpp
index 5d9598931a..b872466905 100644
--- a/xbmc/filesystem/FileCache.cpp
+++ b/xbmc/filesystem/FileCache.cpp
@@ -11,7 +11,7 @@
#include "CircularCache.h"
#include "ServiceBroker.h"
#include "URL.h"
-#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "threads/Thread.h"
#include "utils/log.h"
@@ -111,11 +111,12 @@ bool CFileCache::Open(const CURL& url)
return false;
}
- const auto advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
- if (!advancedSettings)
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (!settings)
return false;
- const unsigned int cacheMemSize = advancedSettings->m_cacheMemSize;
+ const unsigned int cacheMemSize =
+ settings->GetInt(CSettings::SETTING_FILECACHE_MEMORYSIZE) * 1024 * 1024;
m_source.IoControl(IOCTRL_SET_CACHE, this);
@@ -126,8 +127,8 @@ bool CFileCache::Open(const CURL& url)
m_seekPossible = m_source.IoControl(IOCTRL_SEEK_POSSIBLE, NULL);
// Determine the best chunk size we can use
- m_chunkSize =
- CFile::DetermineChunkSize(m_source.GetChunkSize(), advancedSettings->m_cacheChunkSize);
+ m_chunkSize = CFile::DetermineChunkSize(m_source.GetChunkSize(),
+ settings->GetInt(CSettings::SETTING_FILECACHE_CHUNKSIZE));
CLog::Log(LOGDEBUG,
"CFileCache::{} - <{}> source chunk size is {}, setting cache chunk size to {}",
__FUNCTION__, m_sourcePath, m_source.GetChunkSize(), m_chunkSize);
@@ -232,11 +233,11 @@ void CFileCache::Process()
return;
}
- const auto advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
- if (!advancedSettings)
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (!settings)
return;
- const float readFactor = advancedSettings->m_cacheReadFactor;
+ const float readFactor = settings->GetInt(CSettings::SETTING_FILECACHE_READFACTOR) / 100.0f;
CWriteRate limiter;
CWriteRate average;
@@ -604,6 +605,8 @@ int CFileCache::IoControl(EIoControl request, void* param)
if (request == IOCTRL_CACHE_STATUS)
{
SCacheStatus* status = (SCacheStatus*)param;
+ status->maxforward =
+ (m_forwardCacheSize != 0) ? m_forwardCacheSize : static_cast<uint64_t>(m_fileSize);
status->forward = m_pCache->WaitForData(0, 0ms);
status->maxrate = m_writeRate;
status->currate = m_writeRateActual;
diff --git a/xbmc/filesystem/IFileTypes.h b/xbmc/filesystem/IFileTypes.h
index 524d8715ca..4f594ca291 100644
--- a/xbmc/filesystem/IFileTypes.h
+++ b/xbmc/filesystem/IFileTypes.h
@@ -48,12 +48,22 @@ struct SNativeIoControl
struct SCacheStatus
{
+ uint64_t maxforward; /**< forward cache max capacity in bytes */
uint64_t forward; /**< number of bytes cached forward of current position */
uint32_t maxrate; /**< maximum allowed read(fill) rate (bytes/second) */
uint32_t currate; /**< average read rate (bytes/second) since last position change */
uint32_t lowrate; /**< low speed read rate (bytes/second) (if any, else 0) */
};
+enum CACHE_BUFFER_MODES
+{
+ CACHE_BUFFER_MODE_INTERNET = 0,
+ CACHE_BUFFER_MODE_ALL = 1,
+ CACHE_BUFFER_MODE_TRUE_INTERNET = 2,
+ CACHE_BUFFER_MODE_NONE = 3,
+ CACHE_BUFFER_MODE_NETWORK = 4,
+};
+
typedef enum {
IOCTRL_NATIVE = 1, /**< SNativeIoControl structure, containing what should be passed to native ioctrl */
IOCTRL_SEEK_POSSIBLE = 2, /**< return 0 if known not to work, 1 if it should work */
diff --git a/xbmc/filesystem/NFSFile.cpp b/xbmc/filesystem/NFSFile.cpp
index 5e0c148770..b96b2bc4fa 100644
--- a/xbmc/filesystem/NFSFile.cpp
+++ b/xbmc/filesystem/NFSFile.cpp
@@ -56,6 +56,7 @@ constexpr auto IDLE_TIMEOUT = 30s; // close fast unused contexts when no active
constexpr int NFS4ERR_EXPIRED = -11; // client session expired due idle time greater than lease_time
constexpr auto SETTING_NFS_VERSION = "nfs.version";
+constexpr auto SETTING_NFS_CHUNKSIZE = "nfs.chunksize";
} // unnamed namespace
CNfsConnection::CNfsConnection()
@@ -334,15 +335,38 @@ bool CNfsConnection::Connect(const CURL& url, std::string &relativePath)
m_readChunkSize = nfs_get_readmax(m_pNfsContext);
m_writeChunkSize = nfs_get_writemax(m_pNfsContext);
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const uint64_t chunkSize =
+ settings ? (settings->GetInt(SETTING_NFS_CHUNKSIZE) * 1024) : (128 * 1024);
+
if (m_readChunkSize == 0)
{
- CLog::Log(LOGDEBUG, "NFS Server did not return max read chunksize - Using 128K default");
- m_readChunkSize = 128 * 1024; // 128K
+ CLog::Log(LOGDEBUG, "NFS Server did not return max read chunksize - Using setting value {}",
+ chunkSize);
+ m_readChunkSize = chunkSize;
+ }
+ else if (chunkSize < m_readChunkSize)
+ {
+ CLog::Log(LOGDEBUG,
+ "NFS Server max read chunksize ({}) is bigger than client setting - Using client "
+ "value {}",
+ m_readChunkSize, chunkSize);
+ m_readChunkSize = chunkSize;
}
+
if (m_writeChunkSize == 0)
{
- CLog::Log(LOGDEBUG, "NFS Server did not return max write chunksize - Using 128K default");
- m_writeChunkSize = 128 * 1024; // 128K
+ CLog::Log(LOGDEBUG, "NFS Server did not return max write chunksize - Using setting value {}",
+ chunkSize);
+ m_writeChunkSize = chunkSize;
+ }
+ else if (chunkSize < m_writeChunkSize)
+ {
+ CLog::Log(LOGDEBUG,
+ "NFS Server max write chunksize ({}) is bigger than client setting - Using client "
+ "value {}",
+ m_writeChunkSize, chunkSize);
+ m_writeChunkSize = chunkSize;
}
if (contextRet == CNfsConnection::ContextStatus::NEW)
diff --git a/xbmc/filesystem/NptXbmcFile.cpp b/xbmc/filesystem/NptXbmcFile.cpp
index 90252ba0b4..aba25873dc 100644
--- a/xbmc/filesystem/NptXbmcFile.cpp
+++ b/xbmc/filesystem/NptXbmcFile.cpp
@@ -13,7 +13,9 @@
+---------------------------------------------------------------------*/
#include "File.h"
#include "FileFactory.h"
+#include "PasswordManager.h"
#include "URL.h"
+#include "utils/URIUtils.h"
#include <limits>
@@ -294,16 +296,17 @@ NPT_XbmcFile::Open(NPT_File::OpenMode mode)
}
bool result;
- CURL* url = new CURL(name);
+ CURL url(URIUtils::SubstitutePath(name));
+
+ if (CPasswordManager::GetInstance().IsURLSupported(url) && url.GetUserName().empty())
+ CPasswordManager::GetInstance().AuthenticateURL(url);
// compute mode
- if (mode & NPT_FILE_OPEN_MODE_WRITE) {
- result = file->OpenForWrite(*url, (mode & NPT_FILE_OPEN_MODE_TRUNCATE)?true:false);
- } else {
- result = file->Open(*url);
- }
+ if (mode & NPT_FILE_OPEN_MODE_WRITE)
+ result = file->OpenForWrite(url, (mode & NPT_FILE_OPEN_MODE_TRUNCATE) ? true : false);
+ else
+ result = file->Open(url);
- delete url;
if (!result) return NPT_ERROR_NO_SUCH_FILE;
}
diff --git a/xbmc/filesystem/ShoutcastFile.cpp b/xbmc/filesystem/ShoutcastFile.cpp
index 7488ee7d3d..861c001e01 100644
--- a/xbmc/filesystem/ShoutcastFile.cpp
+++ b/xbmc/filesystem/ShoutcastFile.cpp
@@ -31,6 +31,7 @@
#include "utils/UrlOptions.h"
#include <climits>
+#include <memory>
#include <mutex>
using namespace XFILE;
@@ -120,12 +121,12 @@ bool CShoutcastFile::Open(const CURL& url)
{
std::unique_lock<CCriticalSection> lock(m_tagSection);
- m_masterTag.reset(new CMusicInfoTag());
+ m_masterTag = std::make_shared<CMusicInfoTag>();
m_masterTag->SetStationName(icyTitle);
m_masterTag->SetGenre(icyGenre);
m_masterTag->SetLoaded(true);
- m_tags.push({1, m_masterTag});
+ m_tags.emplace(1, m_masterTag);
m_tagChange.Set();
}
@@ -303,7 +304,7 @@ bool CShoutcastFile::ExtractTagInfo(const char* buf)
tag->SetTitle(title);
tag->SetStationArt(coverURL);
- m_tags.push({m_file.GetPosition(), tag});
+ m_tags.emplace(m_file.GetPosition(), tag);
m_tagChange.Set();
}
}
diff --git a/xbmc/filesystem/SmartPlaylistDirectory.cpp b/xbmc/filesystem/SmartPlaylistDirectory.cpp
index 9e05599a20..a45e796071 100644
--- a/xbmc/filesystem/SmartPlaylistDirectory.cpp
+++ b/xbmc/filesystem/SmartPlaylistDirectory.cpp
@@ -26,6 +26,7 @@
#include "video/VideoDbUrl.h"
#include <math.h>
+#include <memory>
#define PROPERTY_PATH_DB "path.db"
#define PROPERTY_SORT_ORDER "sort.order"
@@ -82,7 +83,7 @@ namespace XFILE
playlist.GetVirtualFolders(virtualFolders);
for (const std::string& virtualFolder : virtualFolders)
{
- CFileItemPtr pItem = CFileItemPtr(new CFileItem(virtualFolder, true));
+ CFileItemPtr pItem = std::make_shared<CFileItem>(virtualFolder, true);
IFileDirectory *dir = CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get());
if (dir != NULL)
diff --git a/xbmc/filesystem/VideoDatabaseDirectory.cpp b/xbmc/filesystem/VideoDatabaseDirectory.cpp
index 6a2cb6a449..2c9caa7080 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory.cpp
+++ b/xbmc/filesystem/VideoDatabaseDirectory.cpp
@@ -75,6 +75,8 @@ std::string GetChildContentType(const std::unique_ptr<CDirectoryNode>& node)
return "sets";
case NODE_TYPE_TAGS:
return "tags";
+ case NODE_TYPE_VIDEOVERSIONS:
+ return "videoversions";
default:
break;
}
@@ -228,6 +230,10 @@ bool CVideoDatabaseDirectory::GetLabel(const std::string& strDirectory, std::str
strLabel += strTemp;
}
+ // get videoversions
+ if (params.GetVideoVersionId() != -1)
+ strLabel += videodatabase.GetVideoVersionById(params.GetVideoVersionId());
+
if (strLabel.empty())
{
switch (pNode->GetChildType())
@@ -250,6 +256,9 @@ bool CVideoDatabaseDirectory::GetLabel(const std::string& strDirectory, std::str
strLabel = g_localizeStrings.Get(20434); break;
case NODE_TYPE_TAGS: // Tags
strLabel = g_localizeStrings.Get(20459); break;
+ case NODE_TYPE_VIDEOVERSIONS: // Video versions
+ strLabel = g_localizeStrings.Get(40000);
+ break;
case NODE_TYPE_MOVIES_OVERVIEW: // Movies
strLabel = g_localizeStrings.Get(342); break;
case NODE_TYPE_TVSHOWS_OVERVIEW: // TV Shows
@@ -317,6 +326,8 @@ std::string CVideoDatabaseDirectory::GetIcon(const std::string &strDirectory)
return "DefaultSets.png";
case NODE_TYPE_TAGS: // Tags
return "DefaultTags.png";
+ case NODE_TYPE_VIDEOVERSIONS: // Video versions
+ return "DefaultVideoVersions.png";
case NODE_TYPE_YEAR: // Year
return "DefaultYear.png";
case NODE_TYPE_DIRECTOR: // Director
@@ -349,7 +360,11 @@ std::string CVideoDatabaseDirectory::GetIcon(const std::string &strDirectory)
bool CVideoDatabaseDirectory::ContainsMovies(const std::string &path)
{
VIDEODATABASEDIRECTORY::NODE_TYPE type = GetDirectoryChildType(path);
- if (type == VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_MOVIES || type == VIDEODATABASEDIRECTORY::NODE_TYPE_EPISODES || type == VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_MUSICVIDEOS) return true;
+ if (type == VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_MOVIES ||
+ type == VIDEODATABASEDIRECTORY::NODE_TYPE_EPISODES ||
+ type == VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_MUSICVIDEOS ||
+ type == VIDEODATABASEDIRECTORY::NODE_TYPE_VIDEOVERSIONS)
+ return true;
return false;
}
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.cpp b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.cpp
index 7f83cffcca..b62839c9bc 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.cpp
+++ b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.cpp
@@ -99,6 +99,7 @@ CDirectoryNode* CDirectoryNode::CreateNode(NODE_TYPE Type, const std::string& st
case NODE_TYPE_COUNTRY:
case NODE_TYPE_SETS:
case NODE_TYPE_TAGS:
+ case NODE_TYPE_VIDEOVERSIONS:
case NODE_TYPE_YEAR:
case NODE_TYPE_ACTOR:
case NODE_TYPE_DIRECTOR:
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.h b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.h
index 7ab27c59e5..11c2d1635a 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.h
+++ b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.h
@@ -22,7 +22,7 @@ namespace XFILE
typedef enum _NODE_TYPE
{
- NODE_TYPE_NONE=0,
+ NODE_TYPE_NONE = 0,
NODE_TYPE_MOVIES_OVERVIEW,
NODE_TYPE_TVSHOWS_OVERVIEW,
NODE_TYPE_GENRE,
@@ -45,7 +45,8 @@ namespace XFILE
NODE_TYPE_SETS,
NODE_TYPE_COUNTRY,
NODE_TYPE_TAGS,
- NODE_TYPE_INPROGRESS_TVSHOWS
+ NODE_TYPE_INPROGRESS_TVSHOWS,
+ NODE_TYPE_VIDEOVERSIONS
} NODE_TYPE;
typedef struct {
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeGrouped.cpp b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeGrouped.cpp
index 73ec1152c5..f4331857bb 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeGrouped.cpp
+++ b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeGrouped.cpp
@@ -89,6 +89,8 @@ std::string CDirectoryNodeGrouped::GetContentType(const CQueryParams &params) co
return "sets";
case NODE_TYPE_TAGS:
return "tags";
+ case NODE_TYPE_VIDEOVERSIONS:
+ return "videoversions";
case NODE_TYPE_YEAR:
return "years";
case NODE_TYPE_ACTOR:
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp
index 20ef46e8a1..9f415d09da 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp
+++ b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp
@@ -16,17 +16,20 @@
using namespace XFILE::VIDEODATABASEDIRECTORY;
+// clang-format off
Node MovieChildren[] = {
- { NODE_TYPE_GENRE, "genres", 135 },
- { NODE_TYPE_TITLE_MOVIES, "titles", 10024 },
- { NODE_TYPE_YEAR, "years", 652 },
- { NODE_TYPE_ACTOR, "actors", 344 },
- { NODE_TYPE_DIRECTOR, "directors", 20348 },
- { NODE_TYPE_STUDIO, "studios", 20388 },
- { NODE_TYPE_SETS, "sets", 20434 },
- { NODE_TYPE_COUNTRY, "countries", 20451 },
- { NODE_TYPE_TAGS, "tags", 20459 }
+ { NODE_TYPE_GENRE, "genres", 135 },
+ { NODE_TYPE_TITLE_MOVIES, "titles", 10024 },
+ { NODE_TYPE_YEAR, "years", 652 },
+ { NODE_TYPE_ACTOR, "actors", 344 },
+ { NODE_TYPE_DIRECTOR, "directors", 20348 },
+ { NODE_TYPE_STUDIO, "studios", 20388 },
+ { NODE_TYPE_SETS, "sets", 20434 },
+ { NODE_TYPE_COUNTRY, "countries", 20451 },
+ { NODE_TYPE_TAGS, "tags", 20459 },
+ { NODE_TYPE_VIDEOVERSIONS,"videoversions", 40000 },
};
+// clang-format on
CDirectoryNodeMoviesOverview::CDirectoryNodeMoviesOverview(const std::string& strName, CDirectoryNode* pParent)
: CDirectoryNode(NODE_TYPE_MOVIES_OVERVIEW, strName, pParent)
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.cpp b/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.cpp
index d4c78ff211..f4f067ef90 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.cpp
+++ b/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.cpp
@@ -91,6 +91,9 @@ void CQueryParams::SetQueryParam(NODE_TYPE NodeType, const std::string& strNodeN
case NODE_TYPE_TAGS:
m_idTag = idDb;
break;
+ case NODE_TYPE_VIDEOVERSIONS:
+ m_idVideoVersion = idDb;
+ break;
default:
break;
}
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.h b/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.h
index f9d7e4535b..74c73cc9cf 100644
--- a/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.h
+++ b/xbmc/filesystem/VideoDatabaseDirectory/QueryParams.h
@@ -33,6 +33,7 @@ namespace XFILE
long GetMVideoId() const { return m_idMVideo; }
long GetSetId() const { return m_idSet; }
long GetTagId() const { return m_idTag; }
+ long GetVideoVersionId() const { return m_idVideoVersion; }
protected:
void SetQueryParam(NODE_TYPE NodeType, const std::string& strNodeName);
@@ -54,6 +55,7 @@ namespace XFILE
long m_idAlbum;
long m_idSet;
long m_idTag;
+ long m_idVideoVersion{-1};
};
}
}
diff --git a/xbmc/filesystem/XbtDirectory.cpp b/xbmc/filesystem/XbtDirectory.cpp
index 5c7d39555c..d1a44d59af 100644
--- a/xbmc/filesystem/XbtDirectory.cpp
+++ b/xbmc/filesystem/XbtDirectory.cpp
@@ -52,7 +52,7 @@ bool CXbtDirectory::GetDirectory(const CURL& urlOrig, CFileItemList& items)
DirectorizeEntries<CXBTFFile> entries;
entries.reserve(files.size());
for (const auto& file : files)
- entries.push_back(DirectorizeEntry<CXBTFFile>(file.GetPath(), file));
+ entries.emplace_back(file.GetPath(), file);
Directorize(urlXbt, entries, XBTFFileToFileItem, items);
diff --git a/xbmc/filesystem/ZipDirectory.cpp b/xbmc/filesystem/ZipDirectory.cpp
index bfe43fbaae..cd29d907f8 100644
--- a/xbmc/filesystem/ZipDirectory.cpp
+++ b/xbmc/filesystem/ZipDirectory.cpp
@@ -53,7 +53,7 @@ namespace XFILE
DirectorizeEntries<SZipEntry> entries;
entries.reserve(zipEntries.size());
for (const auto& zipEntry : zipEntries)
- entries.push_back(DirectorizeEntry<SZipEntry>(zipEntry.name, zipEntry));
+ entries.emplace_back(zipEntry.name, zipEntry);
// directorize the ZIP entries into files and directories
Directorize(urlZip, entries, ZipEntryToFileItem, items);
diff --git a/xbmc/games/GameServices.cpp b/xbmc/games/GameServices.cpp
index 099c9a9154..da4377a4fd 100644
--- a/xbmc/games/GameServices.cpp
+++ b/xbmc/games/GameServices.cpp
@@ -57,6 +57,12 @@ ControllerVector CGameServices::GetControllers()
return m_controllerManager.GetControllers();
}
+std::string CGameServices::TranslateFeature(const std::string& controllerId,
+ const std::string& featureName)
+{
+ return m_controllerManager.TranslateFeature(controllerId, featureName);
+}
+
std::string CGameServices::GetSavestatesFolder() const
{
return m_profileManager.GetSavestatesFolder();
diff --git a/xbmc/games/GameServices.h b/xbmc/games/GameServices.h
index c736febd9c..41331e9ad6 100644
--- a/xbmc/games/GameServices.h
+++ b/xbmc/games/GameServices.h
@@ -34,6 +34,9 @@ class CGameAgentManager;
class CControllerManager;
class CGameSettings;
+/*!
+ * \ingroup games
+ */
class CGameServices
{
public:
@@ -50,6 +53,17 @@ public:
ControllerPtr GetDefaultMouse();
ControllerVector GetControllers();
+ /*!
+ * \brief Translate a feature on a controller into its localized name
+ *
+ * \param controllerId The controller ID that the feature belongs to
+ * \param featureName The feature name
+ *
+ * \return The localized feature name, or empty if the controller or feature
+ * doesn't exist
+ */
+ std::string TranslateFeature(const std::string& controllerId, const std::string& featureName);
+
std::string GetSavestatesFolder() const;
CGameSettings& GameSettings() { return *m_gameSettings; }
diff --git a/xbmc/games/GameSettings.h b/xbmc/games/GameSettings.h
index 63ee67b379..aca6ea3384 100644
--- a/xbmc/games/GameSettings.h
+++ b/xbmc/games/GameSettings.h
@@ -21,6 +21,9 @@ namespace KODI
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGameSettings : public ISettingCallback, public Observable
{
public:
diff --git a/xbmc/games/GameTypes.h b/xbmc/games/GameTypes.h
index 275894674c..76881aa00a 100644
--- a/xbmc/games/GameTypes.h
+++ b/xbmc/games/GameTypes.h
@@ -16,20 +16,65 @@ namespace KODI
namespace GAME
{
+class CGameAgent;
class CGameClient;
+class CGameClientDevice;
+class CGameClientPort;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Smart pointer to a game client (\ref CGameClient)
+ */
using GameClientPtr = std::shared_ptr<CGameClient>;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Vector of smart pointers to a game client (\ref CGameClient)
+ */
using GameClientVector = std::vector<GameClientPtr>;
-class CGameClientPort;
+/*!
+ * \ingroup games
+ *
+ * \brief Smart pointer to an input port for a game client (\ref CGameClientPort)
+ */
using GameClientPortPtr = std::unique_ptr<CGameClientPort>;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Vector of smart pointers to input ports for a game client (\ref CGameClientPort)
+ */
using GameClientPortVec = std::vector<GameClientPortPtr>;
-class CGameClientDevice;
+/*!
+ * \ingroup games
+ *
+ * \brief Smart pointer to an input device for a game client (\ref CGameClientDevice)
+ */
using GameClientDevicePtr = std::unique_ptr<CGameClientDevice>;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Vector of smart pointers to input devices for a game client (\ref CGameClientDevice)
+ */
using GameClientDeviceVec = std::vector<GameClientDevicePtr>;
-class CGameAgent;
+/*!
+ * \ingroup games
+ *
+ * \brief Smart pointer to a game-playing agent (\ref CGameAgent)
+ */
using GameAgentPtr = std::shared_ptr<CGameAgent>;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Vector of smart pointers to game-playing agents (\ref CGameAgent)
+ */
using GameAgentVec = std::vector<GameAgentPtr>;
} // namespace GAME
diff --git a/xbmc/games/GameUtils.h b/xbmc/games/GameUtils.h
index b49985c818..918f24e8ae 100644
--- a/xbmc/games/GameUtils.h
+++ b/xbmc/games/GameUtils.h
@@ -29,6 +29,7 @@ namespace GAME
{
/*!
* \ingroup games
+ *
* \brief Game related utilities.
*/
class CGameUtils
diff --git a/xbmc/games/addons/GameClient.cpp b/xbmc/games/addons/GameClient.cpp
index 4d60eb101c..45594fb1b4 100644
--- a/xbmc/games/addons/GameClient.cpp
+++ b/xbmc/games/addons/GameClient.cpp
@@ -41,6 +41,7 @@
#include <algorithm>
#include <cstring>
#include <iterator>
+#include <memory>
#include <mutex>
#include <utility>
@@ -312,7 +313,7 @@ bool CGameClient::InitializeGameplay(const std::string& gamePath,
m_gamePath = gamePath;
m_input = input;
- m_inGameSaves.reset(new CGameClientInGameSaves(this, m_ifc.game));
+ m_inGameSaves = std::make_unique<CGameClientInGameSaves>(this, m_ifc.game);
m_inGameSaves->Load();
return true;
diff --git a/xbmc/games/addons/GameClient.h b/xbmc/games/addons/GameClient.h
index 212039c8e9..3754697438 100644
--- a/xbmc/games/addons/GameClient.h
+++ b/xbmc/games/addons/GameClient.h
@@ -40,6 +40,7 @@ class IGameInputCallback;
/*!
* \ingroup games
+ *
* \brief Helper class to have "C" struct created before other parts becomes his pointer.
*/
class CGameClientStruct
diff --git a/xbmc/games/addons/GameClientCallbacks.h b/xbmc/games/addons/GameClientCallbacks.h
index f4bbe2d749..49c529178d 100644
--- a/xbmc/games/addons/GameClientCallbacks.h
+++ b/xbmc/games/addons/GameClientCallbacks.h
@@ -13,6 +13,8 @@ namespace KODI
namespace GAME
{
/*!
+ * \ingroup games
+ *
* \brief Input callbacks
*
* @todo Remove this file when Game API is updated for input polling
diff --git a/xbmc/games/addons/GameClientInGameSaves.h b/xbmc/games/addons/GameClientInGameSaves.h
index a75c155d67..503d9c4edf 100644
--- a/xbmc/games/addons/GameClientInGameSaves.h
+++ b/xbmc/games/addons/GameClientInGameSaves.h
@@ -21,6 +21,8 @@ namespace GAME
class CGameClient;
/*!
+ * \ingroup games
+ *
* \brief This class implements in-game saves.
*
* \details Some games do not implement state persistence on their own, but rely on the frontend for
diff --git a/xbmc/games/addons/GameClientProperties.h b/xbmc/games/addons/GameClientProperties.h
index 5cb1c6ac34..efb9a8b566 100644
--- a/xbmc/games/addons/GameClientProperties.h
+++ b/xbmc/games/addons/GameClientProperties.h
@@ -32,6 +32,7 @@ class CGameClient;
/**
* \ingroup games
+ *
* \brief C++ wrapper for properties to pass to the DLL
*
* Game client properties declared in addon-instance/Game.h.
diff --git a/xbmc/games/addons/GameClientSubsystem.cpp b/xbmc/games/addons/GameClientSubsystem.cpp
index 599125fef6..f350cf568f 100644
--- a/xbmc/games/addons/GameClientSubsystem.cpp
+++ b/xbmc/games/addons/GameClientSubsystem.cpp
@@ -15,6 +15,8 @@
#include "games/addons/input/GameClientInput.h"
#include "games/addons/streams/GameClientStreams.h"
+#include <memory>
+
using namespace KODI;
using namespace GAME;
@@ -34,9 +36,10 @@ GameClientSubsystems CGameClientSubsystem::CreateSubsystems(CGameClient& gameCli
GameClientSubsystems subsystems = {};
subsystems.Cheevos = std::make_unique<CGameClientCheevos>(gameClient, gameStruct);
- subsystems.Input.reset(new CGameClientInput(gameClient, gameStruct, clientAccess));
- subsystems.AddonProperties.reset(new CGameClientProperties(gameClient, *gameStruct.props));
- subsystems.Streams.reset(new CGameClientStreams(gameClient));
+ subsystems.Input = std::make_unique<CGameClientInput>(gameClient, gameStruct, clientAccess);
+ subsystems.AddonProperties =
+ std::make_unique<CGameClientProperties>(gameClient, *gameStruct.props);
+ subsystems.Streams = std::make_unique<CGameClientStreams>(gameClient);
return subsystems;
}
diff --git a/xbmc/games/addons/GameClientSubsystem.h b/xbmc/games/addons/GameClientSubsystem.h
index 4711011fd4..1bcc0f36e0 100644
--- a/xbmc/games/addons/GameClientSubsystem.h
+++ b/xbmc/games/addons/GameClientSubsystem.h
@@ -32,6 +32,8 @@ struct GameClientSubsystems
};
/*!
+ * \ingroup games
+ *
* \brief Base class for game client subsystems
*/
class CGameClientSubsystem
diff --git a/xbmc/games/addons/GameClientTranslator.h b/xbmc/games/addons/GameClientTranslator.h
index 6b6b448a2f..610bb519b2 100644
--- a/xbmc/games/addons/GameClientTranslator.h
+++ b/xbmc/games/addons/GameClientTranslator.h
@@ -24,6 +24,7 @@ namespace GAME
{
/*!
* \ingroup games
+ *
* \brief Translates data types from Game API to the corresponding format in Kodi.
*
* This class is stateless.
diff --git a/xbmc/games/addons/cheevos/GameClientCheevos.h b/xbmc/games/addons/cheevos/GameClientCheevos.h
index 2be8c7a4ca..b056cc08f9 100644
--- a/xbmc/games/addons/cheevos/GameClientCheevos.h
+++ b/xbmc/games/addons/cheevos/GameClientCheevos.h
@@ -25,6 +25,9 @@ namespace GAME
class CGameClient;
+/*!
+ * \ingroup games
+ */
class CGameClientCheevos
{
public:
diff --git a/xbmc/games/addons/input/GameClientController.h b/xbmc/games/addons/input/GameClientController.h
index 5445868ef7..0aa93ac1d2 100644
--- a/xbmc/games/addons/input/GameClientController.h
+++ b/xbmc/games/addons/input/GameClientController.h
@@ -33,6 +33,8 @@ namespace GAME
class CGameClientInput;
/*!
+ * \ingroup games
+ *
* \brief A container for the layout of a controller connected to a game
* client input port
*/
diff --git a/xbmc/games/addons/input/GameClientDevice.h b/xbmc/games/addons/input/GameClientDevice.h
index 403d76c853..0e4b747018 100644
--- a/xbmc/games/addons/input/GameClientDevice.h
+++ b/xbmc/games/addons/input/GameClientDevice.h
@@ -24,6 +24,7 @@ class CPhysicalPort;
/*!
* \ingroup games
+ *
* \brief Represents a device connected to a port
*/
class CGameClientDevice
diff --git a/xbmc/games/addons/input/GameClientHardware.h b/xbmc/games/addons/input/GameClientHardware.h
index 1ffc96bae8..10cbdba2f6 100644
--- a/xbmc/games/addons/input/GameClientHardware.h
+++ b/xbmc/games/addons/input/GameClientHardware.h
@@ -18,6 +18,7 @@ class CGameClient;
/*!
* \ingroup games
+ *
* \brief Handles events for hardware such as reset buttons
*/
class CGameClientHardware : public HARDWARE::IHardwareInput
diff --git a/xbmc/games/addons/input/GameClientInput.cpp b/xbmc/games/addons/input/GameClientInput.cpp
index 273f376fe3..95fcfef91d 100644
--- a/xbmc/games/addons/input/GameClientInput.cpp
+++ b/xbmc/games/addons/input/GameClientInput.cpp
@@ -35,6 +35,7 @@
#include "utils/log.h"
#include <algorithm>
+#include <memory>
#include <mutex>
using namespace KODI;
@@ -93,7 +94,7 @@ void CGameClientInput::Start(IGameInputCallback* input)
}
// Ensure hardware is open to receive events
- m_hardware.reset(new CGameClientHardware(m_gameClient));
+ m_hardware = std::make_unique<CGameClientHardware>(m_gameClient);
}
// Notify observers of the initial port configuration
@@ -237,7 +238,7 @@ void CGameClientInput::LoadTopology()
if (hardwarePorts.empty())
hardwarePorts.emplace_back(new CGameClientPort(GetControllers(m_gameClient)));
- m_topology.reset(new CGameClientTopology(std::move(hardwarePorts), playerLimit));
+ m_topology = std::make_unique<CGameClientTopology>(std::move(hardwarePorts), playerLimit);
}
void CGameClientInput::ActivateControllers(CControllerHub& hub)
@@ -263,7 +264,8 @@ void CGameClientInput::SetControllerLayouts(const ControllerVector& controllers)
{
const std::string controllerId = controller->ID();
if (m_controllerLayouts.find(controllerId) == m_controllerLayouts.end())
- m_controllerLayouts[controllerId].reset(new CGameClientController(*this, controller));
+ m_controllerLayouts[controllerId] =
+ std::make_unique<CGameClientController>(*this, controller);
}
std::vector<game_controller_layout> controllerStructs;
@@ -626,7 +628,8 @@ bool CGameClientInput::OpenJoystick(const std::string& portAddress, const Contro
return false;
}
- m_joysticks[portAddress].reset(new CGameClientJoystick(m_gameClient, portAddress, controller));
+ m_joysticks[portAddress] =
+ std::make_shared<CGameClientJoystick>(m_gameClient, portAddress, controller);
return true;
}
diff --git a/xbmc/games/addons/input/GameClientInput.h b/xbmc/games/addons/input/GameClientInput.h
index d7d6598d90..f909e10909 100644
--- a/xbmc/games/addons/input/GameClientInput.h
+++ b/xbmc/games/addons/input/GameClientInput.h
@@ -41,6 +41,9 @@ class CGameClientTopology;
class CPortManager;
class IGameInputCallback;
+/*!
+ * \ingroup games
+ */
class CGameClientInput : protected CGameClientSubsystem, public Observable
{
public:
diff --git a/xbmc/games/addons/input/GameClientJoystick.h b/xbmc/games/addons/input/GameClientJoystick.h
index d5144426b9..b50b7522a2 100644
--- a/xbmc/games/addons/input/GameClientJoystick.h
+++ b/xbmc/games/addons/input/GameClientJoystick.h
@@ -28,6 +28,7 @@ class CPortInput;
/*!
* \ingroup games
+ *
* \brief Handles game controller events for games.
*
* Listens to game controller events and forwards them to the games (as game_input_event).
diff --git a/xbmc/games/addons/input/GameClientKeyboard.h b/xbmc/games/addons/input/GameClientKeyboard.h
index a6103f2f7a..99f9ab1c03 100644
--- a/xbmc/games/addons/input/GameClientKeyboard.h
+++ b/xbmc/games/addons/input/GameClientKeyboard.h
@@ -24,6 +24,7 @@ class CGameClient;
/*!
* \ingroup games
+ *
* \brief Handles keyboard events for games.
*
* Listens to keyboard events and forwards them to the games (as game_input_event).
diff --git a/xbmc/games/addons/input/GameClientMouse.h b/xbmc/games/addons/input/GameClientMouse.h
index 4da592ffd5..781a885a64 100644
--- a/xbmc/games/addons/input/GameClientMouse.h
+++ b/xbmc/games/addons/input/GameClientMouse.h
@@ -24,6 +24,7 @@ class CGameClient;
/*!
* \ingroup games
+ *
* \brief Handles mouse events for games.
*
* Listens to mouse events and forwards them to the games (as game_input_event).
diff --git a/xbmc/games/addons/input/GameClientPort.h b/xbmc/games/addons/input/GameClientPort.h
index 80fcceff0e..b958f265a4 100644
--- a/xbmc/games/addons/input/GameClientPort.h
+++ b/xbmc/games/addons/input/GameClientPort.h
@@ -24,6 +24,7 @@ class CPhysicalPort;
/*!
* \ingroup games
+ *
* \brief Represents a port that devices can connect to
*/
class CGameClientPort
diff --git a/xbmc/games/addons/input/GameClientTopology.h b/xbmc/games/addons/input/GameClientTopology.h
index bd7a3ebe35..cba0ba01e5 100644
--- a/xbmc/games/addons/input/GameClientTopology.h
+++ b/xbmc/games/addons/input/GameClientTopology.h
@@ -17,6 +17,10 @@ namespace KODI
{
namespace GAME
{
+
+/*!
+ * \ingroup games
+ */
class CGameClientTopology
{
public:
@@ -47,5 +51,6 @@ private:
// Controller parameters
CControllerTree m_controllers;
};
+
} // namespace GAME
} // namespace KODI
diff --git a/xbmc/games/addons/streams/GameClientStreamAudio.h b/xbmc/games/addons/streams/GameClientStreamAudio.h
index d48b0a66fc..5cb57014a6 100644
--- a/xbmc/games/addons/streams/GameClientStreamAudio.h
+++ b/xbmc/games/addons/streams/GameClientStreamAudio.h
@@ -24,6 +24,9 @@ struct AudioStreamProperties;
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGameClientStreamAudio : public IGameClientStream
{
public:
diff --git a/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h b/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h
index 2b17dac96e..a5c6ad4033 100644
--- a/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h
+++ b/xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h
@@ -15,6 +15,9 @@ namespace KODI
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGameClientStreamSwFramebuffer : public CGameClientStreamVideo
{
public:
diff --git a/xbmc/games/addons/streams/GameClientStreamVideo.h b/xbmc/games/addons/streams/GameClientStreamVideo.h
index 4c90c2c1ce..4c08cc8581 100644
--- a/xbmc/games/addons/streams/GameClientStreamVideo.h
+++ b/xbmc/games/addons/streams/GameClientStreamVideo.h
@@ -23,6 +23,9 @@ struct VideoStreamProperties;
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGameClientStreamVideo : public IGameClientStream
{
public:
diff --git a/xbmc/games/addons/streams/GameClientStreams.cpp b/xbmc/games/addons/streams/GameClientStreams.cpp
index 7d005ea62f..b7936d46dd 100644
--- a/xbmc/games/addons/streams/GameClientStreams.cpp
+++ b/xbmc/games/addons/streams/GameClientStreams.cpp
@@ -97,17 +97,17 @@ std::unique_ptr<IGameClientStream> CGameClientStreams::CreateStream(
{
case GAME_STREAM_AUDIO:
{
- gameStream.reset(new CGameClientStreamAudio(m_gameClient.GetSampleRate()));
+ gameStream = std::make_unique<CGameClientStreamAudio>(m_gameClient.GetSampleRate());
break;
}
case GAME_STREAM_VIDEO:
{
- gameStream.reset(new CGameClientStreamVideo);
+ gameStream = std::make_unique<CGameClientStreamVideo>();
break;
}
case GAME_STREAM_SW_FRAMEBUFFER:
{
- gameStream.reset(new CGameClientStreamSwFramebuffer);
+ gameStream = std::make_unique<CGameClientStreamSwFramebuffer>();
break;
}
default:
diff --git a/xbmc/games/addons/streams/GameClientStreams.h b/xbmc/games/addons/streams/GameClientStreams.h
index 6f3f63993b..bf762cae5a 100644
--- a/xbmc/games/addons/streams/GameClientStreams.h
+++ b/xbmc/games/addons/streams/GameClientStreams.h
@@ -26,6 +26,9 @@ namespace GAME
class CGameClient;
class IGameClientStream;
+/*!
+ * \ingroup games
+ */
class CGameClientStreams
{
public:
diff --git a/xbmc/games/addons/streams/IGameClientStream.h b/xbmc/games/addons/streams/IGameClientStream.h
index c2374bc20a..4567455af6 100644
--- a/xbmc/games/addons/streams/IGameClientStream.h
+++ b/xbmc/games/addons/streams/IGameClientStream.h
@@ -22,6 +22,9 @@ class IRetroPlayerStream;
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class IGameClientStream
{
public:
diff --git a/xbmc/games/agents/GameAgent.h b/xbmc/games/agents/GameAgent.h
index d793c32f70..6de736619c 100644
--- a/xbmc/games/agents/GameAgent.h
+++ b/xbmc/games/agents/GameAgent.h
@@ -20,6 +20,14 @@ namespace GAME
{
class CGameAgentJoystick;
+/*!
+ * \ingroup games
+ *
+ * \brief Class to represent a game player (a.k.a. agent)
+ *
+ * The term "agent" is used to distinguish game players from the myriad of other
+ * uses of the term "player" in Kodi, such as media players and player cores.
+ */
class CGameAgent
{
public:
diff --git a/xbmc/games/agents/windows/GUIAgentList.h b/xbmc/games/agents/windows/GUIAgentList.h
index ba7b3e88fd..45aacbaba3 100644
--- a/xbmc/games/agents/windows/GUIAgentList.h
+++ b/xbmc/games/agents/windows/GUIAgentList.h
@@ -28,6 +28,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIAgentList : public IAgentList, public Observer
{
public:
diff --git a/xbmc/games/agents/windows/GUIAgentWindow.h b/xbmc/games/agents/windows/GUIAgentWindow.h
index d308f6cba8..1834e15bb1 100644
--- a/xbmc/games/agents/windows/GUIAgentWindow.h
+++ b/xbmc/games/agents/windows/GUIAgentWindow.h
@@ -20,6 +20,9 @@ namespace GAME
class IActivePortList;
class IAgentList;
+/*!
+ * \ingroup games
+ */
class CGUIAgentWindow : public CGUIDialog
{
public:
diff --git a/xbmc/games/agents/windows/IAgentList.h b/xbmc/games/agents/windows/IAgentList.h
index 7e4507c068..94521d5331 100644
--- a/xbmc/games/agents/windows/IAgentList.h
+++ b/xbmc/games/agents/windows/IAgentList.h
@@ -10,10 +10,6 @@
#include "games/GameTypes.h"
-/*!
- * \brief Game player (aka agent) setup window
- */
-
namespace KODI
{
namespace GAME
@@ -21,7 +17,20 @@ namespace GAME
/*!
* \ingroup games
*
- * \brief A list populated by game-playing agents
+ * \brief A list populated by game-playing agents (\ref CGameAgent)
+ *
+ * This class manages the behavior of the player list (with control ID 5) in
+ * the player dialog (<b>`GameAgents`</b> window).
+ *
+ * The list is populated dynamically by the players in the current game.
+ *
+ * Currently, this is identical to the connected joysticks, because Kodi doesn't
+ * yet have a player abstraction. This is planned for a later release along with
+ * a full-featured Player Manager.
+ *
+ * The active ports are determined by \ref IActivePortList.
+ *
+ * The list is populated by the \ref CGUIGameControllerProvider.
*/
class IAgentList
{
diff --git a/xbmc/games/controllers/Controller.h b/xbmc/games/controllers/Controller.h
index c0943e4dd7..acd8545a72 100644
--- a/xbmc/games/controllers/Controller.h
+++ b/xbmc/games/controllers/Controller.h
@@ -27,6 +27,9 @@ class CPhysicalTopology;
using JOYSTICK::FEATURE_TYPE;
+/*!
+ * \ingroup games
+ */
class CController : public ADDON::CAddon
{
public:
diff --git a/xbmc/games/controllers/ControllerLayout.h b/xbmc/games/controllers/ControllerLayout.h
index 529ebf9417..86bd3976e3 100644
--- a/xbmc/games/controllers/ControllerLayout.h
+++ b/xbmc/games/controllers/ControllerLayout.h
@@ -25,6 +25,9 @@ class CController;
class CPhysicalFeature;
class CPhysicalTopology;
+/*!
+ * \ingroup games
+ */
class CControllerLayout
{
public:
diff --git a/xbmc/games/controllers/ControllerManager.cpp b/xbmc/games/controllers/ControllerManager.cpp
index 707733b2f9..277c62554a 100644
--- a/xbmc/games/controllers/ControllerManager.cpp
+++ b/xbmc/games/controllers/ControllerManager.cpp
@@ -13,6 +13,7 @@
#include "addons/AddonEvents.h"
#include "addons/AddonManager.h"
#include "addons/addoninfo/AddonType.h"
+#include "games/controllers/input/PhysicalFeature.h"
#include <mutex>
@@ -89,6 +90,25 @@ ControllerVector CControllerManager::GetControllers()
return controllers;
}
+std::string CControllerManager::TranslateFeature(const std::string& controllerId,
+ const std::string& featureName)
+{
+ ControllerPtr controller = GetController(controllerId);
+ if (controller)
+ {
+ const std::vector<CPhysicalFeature>& features = controller->Features();
+
+ auto it = std::find_if(features.begin(), features.end(),
+ [&featureName](const CPhysicalFeature& feature)
+ { return feature.Name() == featureName; });
+
+ if (it != features.end())
+ return it->Label();
+ }
+
+ return "";
+}
+
void CControllerManager::OnEvent(const ADDON::AddonEvent& event)
{
if (typeid(event) == typeid(ADDON::AddonEvents::Enabled) || // Also called on install
diff --git a/xbmc/games/controllers/ControllerManager.h b/xbmc/games/controllers/ControllerManager.h
index 25ffe0fc88..805ca60c69 100644
--- a/xbmc/games/controllers/ControllerManager.h
+++ b/xbmc/games/controllers/ControllerManager.h
@@ -25,6 +25,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CControllerManager
{
public:
@@ -72,6 +75,17 @@ public:
*/
ControllerVector GetControllers();
+ /*!
+ * \brief Translate a feature on a controller into its localized name
+ *
+ * \param controllerId The controller ID that the feature belongs to
+ * \param featureName The feature name
+ *
+ * \return The localized feature name, or empty if the controller or feature
+ * doesn't exist
+ */
+ std::string TranslateFeature(const std::string& controllerId, const std::string& featureName);
+
private:
// Add-on event handler
void OnEvent(const ADDON::AddonEvent& event);
diff --git a/xbmc/games/controllers/ControllerTranslator.cpp b/xbmc/games/controllers/ControllerTranslator.cpp
index df8641be28..c840a1845d 100644
--- a/xbmc/games/controllers/ControllerTranslator.cpp
+++ b/xbmc/games/controllers/ControllerTranslator.cpp
@@ -450,6 +450,8 @@ KEYBOARD::KeySymbol CControllerTranslator::TranslateKeysym(const std::string& sy
return XBMCK_EURO;
if (symbol == "undo")
return XBMCK_UNDO;
+ if (symbol == "oem102")
+ return XBMCK_OEM_102;
return XBMCK_UNKNOWN;
}
@@ -736,6 +738,8 @@ const char* CControllerTranslator::TranslateKeycode(KEYBOARD::KeySymbol keycode)
return "euro";
case XBMCK_UNDO:
return "undo";
+ case XBMCK_OEM_102:
+ return "oem102";
default:
break;
}
diff --git a/xbmc/games/controllers/ControllerTranslator.h b/xbmc/games/controllers/ControllerTranslator.h
index 0bf4bf16a7..ec10859898 100644
--- a/xbmc/games/controllers/ControllerTranslator.h
+++ b/xbmc/games/controllers/ControllerTranslator.h
@@ -18,6 +18,9 @@ namespace KODI
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CControllerTranslator
{
public:
diff --git a/xbmc/games/controllers/ControllerTypes.h b/xbmc/games/controllers/ControllerTypes.h
index 838a07dbd0..31e49ffa0a 100644
--- a/xbmc/games/controllers/ControllerTypes.h
+++ b/xbmc/games/controllers/ControllerTypes.h
@@ -16,10 +16,24 @@ namespace KODI
namespace GAME
{
class CController;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Smart pointer to a game controller (\ref CController)
+ */
using ControllerPtr = std::shared_ptr<CController>;
+
+/*!
+ * \ingroup games
+ *
+ * \brief Vector of smart pointers to a game controller (\ref CController)
+ */
using ControllerVector = std::vector<ControllerPtr>;
/*!
+ * \ingroup games
+ *
* \brief Type of input provided by a hardware or controller port
*/
enum class PORT_TYPE
diff --git a/xbmc/games/controllers/dialogs/ControllerInstaller.h b/xbmc/games/controllers/dialogs/ControllerInstaller.h
index 9863e2754b..d4f9670477 100644
--- a/xbmc/games/controllers/dialogs/ControllerInstaller.h
+++ b/xbmc/games/controllers/dialogs/ControllerInstaller.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CControllerInstaller : public CThread
{
public:
diff --git a/xbmc/games/controllers/dialogs/ControllerSelect.h b/xbmc/games/controllers/dialogs/ControllerSelect.h
index 83a1b416ce..acf1653bce 100644
--- a/xbmc/games/controllers/dialogs/ControllerSelect.h
+++ b/xbmc/games/controllers/dialogs/ControllerSelect.h
@@ -17,6 +17,10 @@ namespace KODI
{
namespace GAME
{
+
+/*!
+ * \ingroup games
+ */
class CControllerSelect : public CThread
{
public:
diff --git a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h
index 4ca0d27146..d0ec787b92 100644
--- a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h
+++ b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h
@@ -18,6 +18,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIDialogAxisDetection : public CGUIDialogButtonCapture
{
public:
diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h
index 97795c698f..db63c9a21b 100644
--- a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h
+++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h
@@ -20,6 +20,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIDialogButtonCapture : public JOYSTICK::IButtonMapper, public Observer, protected CThread
{
public:
diff --git a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h
index e1d3922438..b7557df74c 100644
--- a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h
+++ b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h
@@ -18,6 +18,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIDialogIgnoreInput : public CGUIDialogButtonCapture
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUICardinalFeatureButton.h b/xbmc/games/controllers/guicontrols/GUICardinalFeatureButton.h
index 48eb0e885d..d1ae92306d 100644
--- a/xbmc/games/controllers/guicontrols/GUICardinalFeatureButton.h
+++ b/xbmc/games/controllers/guicontrols/GUICardinalFeatureButton.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUICardinalFeatureButton : public CGUIFeatureButton
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIControlTypes.h b/xbmc/games/controllers/guicontrols/GUIControlTypes.h
index bf91b6ea4f..daf3712b4c 100644
--- a/xbmc/games/controllers/guicontrols/GUIControlTypes.h
+++ b/xbmc/games/controllers/guicontrols/GUIControlTypes.h
@@ -13,6 +13,8 @@ namespace KODI
namespace GAME
{
/*!
+ * \ingroup games
+ *
* \brief Types of button controls that can populate the feature list
*/
enum class BUTTON_TYPE
diff --git a/xbmc/games/controllers/guicontrols/GUIControllerButton.h b/xbmc/games/controllers/guicontrols/GUIControllerButton.h
index ebdc9249e4..520160c23f 100644
--- a/xbmc/games/controllers/guicontrols/GUIControllerButton.h
+++ b/xbmc/games/controllers/guicontrols/GUIControllerButton.h
@@ -16,6 +16,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIControllerButton : public CGUIButtonControl
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIFeatureButton.h b/xbmc/games/controllers/guicontrols/GUIFeatureButton.h
index b69f521b7f..3fb745c916 100644
--- a/xbmc/games/controllers/guicontrols/GUIFeatureButton.h
+++ b/xbmc/games/controllers/guicontrols/GUIFeatureButton.h
@@ -18,6 +18,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIFeatureButton : public CGUIButtonControl, public IFeatureButton
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIFeatureControls.h b/xbmc/games/controllers/guicontrols/GUIFeatureControls.h
index c72a11a0df..d5eec3dc9e 100644
--- a/xbmc/games/controllers/guicontrols/GUIFeatureControls.h
+++ b/xbmc/games/controllers/guicontrols/GUIFeatureControls.h
@@ -17,6 +17,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIFeatureGroupTitle : public CGUILabelControl
{
public:
@@ -27,6 +30,9 @@ public:
~CGUIFeatureGroupTitle() override = default;
};
+/*!
+ * \ingroup games
+ */
class CGUIFeatureSeparator : public CGUIImage
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIFeatureFactory.h b/xbmc/games/controllers/guicontrols/GUIFeatureFactory.h
index e35070dbae..e55696502e 100644
--- a/xbmc/games/controllers/guicontrols/GUIFeatureFactory.h
+++ b/xbmc/games/controllers/guicontrols/GUIFeatureFactory.h
@@ -19,6 +19,9 @@ namespace GAME
class CPhysicalFeature;
class IConfigurationWizard;
+/*!
+ * \ingroup games
+ */
class CGUIFeatureFactory
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIFeatureTranslator.h b/xbmc/games/controllers/guicontrols/GUIFeatureTranslator.h
index 8e28d161ba..9de634e4fb 100644
--- a/xbmc/games/controllers/guicontrols/GUIFeatureTranslator.h
+++ b/xbmc/games/controllers/guicontrols/GUIFeatureTranslator.h
@@ -15,6 +15,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIFeatureTranslator
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIGameController.cpp b/xbmc/games/controllers/guicontrols/GUIGameController.cpp
index d145c2f2ec..b7a3ce4a1e 100644
--- a/xbmc/games/controllers/guicontrols/GUIGameController.cpp
+++ b/xbmc/games/controllers/guicontrols/GUIGameController.cpp
@@ -199,19 +199,12 @@ void CGUIGameController::ActivateController(const std::string& controllerId)
void CGUIGameController::ActivateController(const ControllerPtr& controller)
{
- if (!controller)
- return;
+ m_currentController = controller;
- bool updated = false;
-
- if (!m_currentController || controller->ID() != m_currentController->ID())
- {
- m_currentController = controller;
- updated = true;
- }
-
- if (updated)
+ if (m_currentController)
SetFileName(m_currentController->Layout().ImagePath());
+ else
+ SetFileName("");
}
std::string CGUIGameController::GetPortAddress()
diff --git a/xbmc/games/controllers/guicontrols/GUIGameController.dox b/xbmc/games/controllers/guicontrols/GUIGameController.dox
new file mode 100644
index 0000000000..c5da94d2bc
--- /dev/null
+++ b/xbmc/games/controllers/guicontrols/GUIGameController.dox
@@ -0,0 +1,120 @@
+/*!
+
+\page Game_Controller Game Controller
+\brief **Used to display a game controller, with optional effects.**
+
+\tableofcontents
+
+The game controller control is used for displaying a game controller, such as
+joysticks, keyboards, mice, lightguns, etc.
+
+In v21 Omega, the control was expanded to give skinners more control over how
+it behaves, including manually specifying a controller to show, allowing for
+a fallback image, and applying a highlighting diffuse effect when the underlying
+controller is active.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect1 Example
+
+~~~~~~~~~~~~~
+<control type="gamecontroller">
+ <description>My first game controller</description>
+ <posx>80</posx>
+ <posy>60</posy>
+ <width>250</width>
+ <height>200</height>
+ <controllerid>game.controller.snes</controllerid>
+</control>
+~~~~~~~~~~~~~
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect2 Available tags
+
+The [default control](http://kodi.wiki/view/Default_Control_Tags) tags are
+applicable to this control. Note that each tag is lower case only. This is
+important, as xml tags are case-sensitive.
+
+In v21 and above, the control derives from an \ref Image_Control "Image Control"
+to allow for a fallback texture. All tags and attributes for images can be used,
+and any game-specific tags will cause the texture to be overridden.
+
+The game-specific tags added in v21 are:
+
+| Tag | Description |
+|-------------------:|:--------------------------------------------------------------|
+| controllerid | The add-on ID of the controller profile to render, e.g. <b>`game.controller.snes`</b>.
+| controllerdiffuse | A diffuse color used to highlight the controller when activity is detected on the in-game port or on the underlying peripheral held by the user.
+| controlleraddress | The in-game "address" of the controller, e.g. <b>`/1/game.controller.snes`</b> for a SNES controller connected to console port 1. Used to highlight the controller on port activity. Overrides <b>`<controllerid>`</b> and <b>`<portaddress>`</b>.
+| portaddress | The in-game "address" of the port the controller is connected to, e.g. <b>`/1`</b> for port 1 of a SNES emulator. Used to highlight the controller on port activity.
+| peripherallocation | The location of the underlying peripheral providing input, e.g. <b>`/joystick/0`</b> for the first physical controller held by the user. Used to hightlight the controller on peripheral activity.
+
+@skinning_v21 The control has been greatly expanded to allow for more use cases in the new Player Viewer (<b>`GameAgents`</b>) dialog.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect3 Controller address
+
+The controller address is connected to the in-game input. It's formed by the
+emulated console's port, followed by the controller ID.
+
+For example, on SNES controllers, the address of the first port is <b>`/1`</b>.
+The address of a SNES controller connected to that port is
+<b>`/1/game.controller.snes`</b>.
+
+Old consoles used multitaps (controller hubs) to allow for more players than
+console ports. A multitap connected to port <b>`/2`</b> would have the address
+<b>`/2/game.controller.snes.multitap`</b>.
+
+A SNES controller connected to port 1 on a multitap would then have the address
+<b>`/2/game.controller.snes.multitap/1/game.controller.snes`</b>.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect4 Peripheral location
+
+Peripherals are located by their driver and peripheral index. For example, the
+first controller connected via the peripheral.joystick add-on (<b>`joystick`</b>
+driver) would be <b>`/joystick/0`</b>.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect5 List item info
+
+List item info can be used for all tag values. For example, if the control defintion looks like:
+
+~~~~~~~~~~~~~
+<itemlayout width="96" height="96">
+ <control type="gamecontroller">
+ <texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(controllerid)]</controllerid>
+ </control>
+</itemlayout>
+~~~~~~~~~~~~~
+
+Static list items can be provided and each control will inherit the properties:
+
+~~~~~~~~~~~~~
+<content>
+ <item>
+ <icon>DefaultAddonNone.png</icon>
+ </item>
+ <item>
+ <property name="controllerid">game.controller.snes</property>
+ </item>
+</content>
+~~~~~~~~~~~~~
+
+The in-game dialogs that highlight game controllers on button presses (<b>`GamePorts`</b> and <b>`GameAgents`</b>) use a similar strategy with list items populated by core.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect6 See also
+
+#### Development:
+
+- [Add-on development](http://kodi.wiki/view/Add-on_development)
+- [Skinning](http://kodi.wiki/view/Skinning)
+
+*/
diff --git a/xbmc/games/controllers/guicontrols/GUIGameController.h b/xbmc/games/controllers/guicontrols/GUIGameController.h
index 651c4d05ee..b20856f641 100644
--- a/xbmc/games/controllers/guicontrols/GUIGameController.h
+++ b/xbmc/games/controllers/guicontrols/GUIGameController.h
@@ -17,6 +17,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIGameController : public CGUIImage
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIGameControllerList.dox b/xbmc/games/controllers/guicontrols/GUIGameControllerList.dox
new file mode 100644
index 0000000000..279ad081bb
--- /dev/null
+++ b/xbmc/games/controllers/guicontrols/GUIGameControllerList.dox
@@ -0,0 +1,77 @@
+/*!
+
+\page Game_Controller_List Game Controller List
+\brief **Used to display a list of game controllers, allowing for special effects such as controller highlighting and port selection.**
+
+\tableofcontents
+
+The game controller list is used for displaying a list of game controllers.
+It is provided as a container to allow for special behavior of the inner game
+controllers, such as visualizing the current in-game port for each controller
+in the <b>`GameAgents`</b> dialog.
+
+The control was introduced in v21 to support the new Player Viewer (<b>`GameAgents`</b>) dialog.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_List_sect1 Example
+
+~~~~~~~~~~~~~
+<control type="gamecontrollerlist">
+ <description>My first game controller list, showing two items: A "controller disconnected" icon and a SNES controller</description>
+ <width>192</width>
+ <height>96</height>
+ <orientation>horizontal</orientation>
+ <align>left</align>
+ <itemlayout width="96" height="96">
+ <control type="gamecontroller">
+ <texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(controllerid)]</controllerid>
+ </control>
+ </itemlayout>
+ <focusedlayout width="96" height="96">
+ <control type="gamecontroller">
+ <texture>$INFO[ListItem.Icon]</texture>
+ <controllerid>$INFO[ListItem.Property(controllerid)]</controllerid>
+ </control>
+ </focusedlayout>
+ <content>
+ <item>
+ <icon>DefaultAddonNone.png</icon>
+ </item>
+ <item>
+ <property name="controllerid">game.controller.snes</property>
+ </item>
+ </content>
+</control>
+~~~~~~~~~~~~~
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_sect2 Available tags
+
+The [default control](http://kodi.wiki/view/Default_Control_Tags) tags are
+applicable to this control. Note that each tag is lower case only. This is
+important, as xml tags are case-sensitive.
+
+The game controller list derives from a \ref List_Container "List Container", so
+all list tags and attributes can be used.
+
+In addition, the following game-related tags are available:
+
+| Tag | Description |
+|------------------:|:--------------------------------------------------------------|
+| align | Align controllers to the <b>`left`</b> or <b>`right`</b>, depending on how many in-game ports are available. Only used in-game for list item content provided by core, such as in the Player Viewer (<b>`GameAgents`</b>) dialog. Ignored if static content is provided.
+
+@skinning_v21 Game Controller List was added.
+
+
+--------------------------------------------------------------------------------
+\section Game_Controller_List_sect3 See also
+
+#### Development:
+
+- [Add-on development](http://kodi.wiki/view/Add-on_development)
+- [Skinning](http://kodi.wiki/view/Skinning)
+
+*/
diff --git a/xbmc/games/controllers/guicontrols/GUIGameControllerList.h b/xbmc/games/controllers/guicontrols/GUIGameControllerList.h
index 5e0c2531ed..ec2b1c021a 100644
--- a/xbmc/games/controllers/guicontrols/GUIGameControllerList.h
+++ b/xbmc/games/controllers/guicontrols/GUIGameControllerList.h
@@ -24,6 +24,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIGameControllerList : public CGUIListContainer
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIScalarFeatureButton.h b/xbmc/games/controllers/guicontrols/GUIScalarFeatureButton.h
index 6505684fb9..d0658470a2 100644
--- a/xbmc/games/controllers/guicontrols/GUIScalarFeatureButton.h
+++ b/xbmc/games/controllers/guicontrols/GUIScalarFeatureButton.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIScalarFeatureButton : public CGUIFeatureButton
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUISelectKeyButton.h b/xbmc/games/controllers/guicontrols/GUISelectKeyButton.h
index e96f78b005..3465e8a40d 100644
--- a/xbmc/games/controllers/guicontrols/GUISelectKeyButton.h
+++ b/xbmc/games/controllers/guicontrols/GUISelectKeyButton.h
@@ -15,6 +15,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUISelectKeyButton : public CGUIFeatureButton
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIThrottleButton.h b/xbmc/games/controllers/guicontrols/GUIThrottleButton.h
index 72b7ee39e7..05cacfcdd8 100644
--- a/xbmc/games/controllers/guicontrols/GUIThrottleButton.h
+++ b/xbmc/games/controllers/guicontrols/GUIThrottleButton.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIThrottleButton : public CGUIFeatureButton
{
public:
diff --git a/xbmc/games/controllers/guicontrols/GUIWheelButton.h b/xbmc/games/controllers/guicontrols/GUIWheelButton.h
index 937703aac3..d6554ee3fc 100644
--- a/xbmc/games/controllers/guicontrols/GUIWheelButton.h
+++ b/xbmc/games/controllers/guicontrols/GUIWheelButton.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIWheelButton : public CGUIFeatureButton
{
public:
diff --git a/xbmc/games/controllers/input/InputSink.h b/xbmc/games/controllers/input/InputSink.h
index 5dc331f7be..d8ec34c1b3 100644
--- a/xbmc/games/controllers/input/InputSink.h
+++ b/xbmc/games/controllers/input/InputSink.h
@@ -16,6 +16,9 @@ namespace GAME
{
class CGameClient;
+/*!
+ * \ingroup games
+ */
class CInputSink : public JOYSTICK::IInputHandler
{
public:
diff --git a/xbmc/games/controllers/input/PhysicalFeature.h b/xbmc/games/controllers/input/PhysicalFeature.h
index a1c5c01c63..a98f7efbef 100644
--- a/xbmc/games/controllers/input/PhysicalFeature.h
+++ b/xbmc/games/controllers/input/PhysicalFeature.h
@@ -24,6 +24,9 @@ namespace KODI
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CPhysicalFeature
{
public:
diff --git a/xbmc/games/controllers/input/PhysicalTopology.h b/xbmc/games/controllers/input/PhysicalTopology.h
index 2468312c16..04a3e89587 100644
--- a/xbmc/games/controllers/input/PhysicalTopology.h
+++ b/xbmc/games/controllers/input/PhysicalTopology.h
@@ -23,6 +23,8 @@ namespace GAME
{
/*!
+ * \ingroup games
+ *
* \brief Represents the physical topology of controller add-ons
*
* The physical topology of a controller defines how many ports it has and
diff --git a/xbmc/games/controllers/listproviders/GUIGameControllerProvider.h b/xbmc/games/controllers/listproviders/GUIGameControllerProvider.h
index fdaa503927..bf40526f48 100644
--- a/xbmc/games/controllers/listproviders/GUIGameControllerProvider.h
+++ b/xbmc/games/controllers/listproviders/GUIGameControllerProvider.h
@@ -20,9 +20,38 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ *
+ * \brief Controller list provider for the \ref IAgentList control in the
+ * Player Viewer (<b>`GameAgents`</b>) window
+ *
+ * This list provider populates a game controller list with items that show
+ * which emulator port a player's controller is connected to. Most items are
+ * empty to pad the controller to its correct position in the list.
+ *
+ * The number of list items is determined by \ref MAX_PORT_COUNT, plus an item
+ * for the "disconnected" icon. The list items are updated when the port count
+ * or port index changes.
+ *
+ * An alignment can be specified to align the available ports to the left or
+ * right side of the list.
+ */
class CGUIGameControllerProvider : public IListProvider
{
public:
+ /*!
+ * \brief Construct a game controller provider for the player's controller
+ * list in the Player Viewer dialog
+ *
+ * \param portCount The number of open ports for the emulator
+ * \param portIndex The index of the port the controller is connected to
+ * \param peripheralLocation The location of the underlying peripheral
+ * providing input
+ * \param alignment The alignment of the list items (<b>`XBFONT_LEFT`</b>
+ * or <b>`XBFONT_RIGHT`</b>)
+ * \param parentID The ID of the parent window
+ */
CGUIGameControllerProvider(unsigned int portCount,
int portIndex,
const std::string& peripheralLocation,
diff --git a/xbmc/games/controllers/types/ControllerGrid.h b/xbmc/games/controllers/types/ControllerGrid.h
index 44ec1d3cec..a036a59964 100644
--- a/xbmc/games/controllers/types/ControllerGrid.h
+++ b/xbmc/games/controllers/types/ControllerGrid.h
@@ -19,6 +19,8 @@ namespace KODI
namespace GAME
{
/*!
+ * \ingroup games
+ *
* \brief Vertex in the grid of controllers
*/
struct ControllerVertex
@@ -32,6 +34,8 @@ struct ControllerVertex
};
/*!
+ * \ingroup games
+ *
* \brief Column of controllers in the grid
*/
struct ControllerColumn
@@ -40,11 +44,15 @@ struct ControllerColumn
};
/*!
+ * \ingroup games
+ *
* \brief Collection of controllers in a grid layout
*/
using ControllerGrid = std::vector<ControllerColumn>;
/*!
+ * \ingroup games
+ *
* \brief Class to encapsulate grid operations
*/
class CControllerGrid
diff --git a/xbmc/games/controllers/types/ControllerHub.h b/xbmc/games/controllers/types/ControllerHub.h
index 1ddfd9ade2..e52d47d2f6 100644
--- a/xbmc/games/controllers/types/ControllerHub.h
+++ b/xbmc/games/controllers/types/ControllerHub.h
@@ -18,6 +18,8 @@ namespace KODI
namespace GAME
{
/*!
+ * \ingroup games
+ *
* \brief A branch in the controller tree
*/
class CControllerHub
diff --git a/xbmc/games/controllers/types/ControllerNode.cpp b/xbmc/games/controllers/types/ControllerNode.cpp
index 701b2d1ba6..c371474465 100644
--- a/xbmc/games/controllers/types/ControllerNode.cpp
+++ b/xbmc/games/controllers/types/ControllerNode.cpp
@@ -14,6 +14,7 @@
#include "games/ports/types/PortNode.h"
#include <algorithm>
+#include <memory>
#include <utility>
using namespace KODI;
@@ -32,7 +33,7 @@ CControllerNode& CControllerNode::operator=(const CControllerNode& rhs)
m_controller = rhs.m_controller;
m_portAddress = rhs.m_portAddress;
m_controllerAddress = rhs.m_controllerAddress;
- m_hub.reset(new CControllerHub(*rhs.m_hub));
+ m_hub = std::make_unique<CControllerHub>(*rhs.m_hub);
}
return *this;
@@ -56,7 +57,7 @@ void CControllerNode::Clear()
m_controller.reset();
m_portAddress.clear();
m_controllerAddress.clear();
- m_hub.reset(new CControllerHub);
+ m_hub = std::make_unique<CControllerHub>();
}
void CControllerNode::SetController(ControllerPtr controller)
@@ -94,7 +95,7 @@ void CControllerNode::SetControllerAddress(std::string controllerAddress)
void CControllerNode::SetHub(CControllerHub hub)
{
- m_hub.reset(new CControllerHub(std::move(hub)));
+ m_hub = std::make_unique<CControllerHub>(std::move(hub));
}
bool CControllerNode::IsControllerAccepted(const std::string& controllerId) const
diff --git a/xbmc/games/controllers/types/ControllerNode.h b/xbmc/games/controllers/types/ControllerNode.h
index ba494cb24d..cf1dc8e5c6 100644
--- a/xbmc/games/controllers/types/ControllerNode.h
+++ b/xbmc/games/controllers/types/ControllerNode.h
@@ -21,6 +21,8 @@ namespace GAME
class CControllerHub;
/*!
+ * \ingroup games
+ *
* \brief Node in the controller tree
*
* The node identifies the controller profile, and optionally the available
diff --git a/xbmc/games/controllers/types/ControllerTree.h b/xbmc/games/controllers/types/ControllerTree.h
index 47e42e9faf..6cbdf4b810 100644
--- a/xbmc/games/controllers/types/ControllerTree.h
+++ b/xbmc/games/controllers/types/ControllerTree.h
@@ -22,9 +22,13 @@ namespace KODI
{
namespace GAME
{
+
/*!
+ * \ingroup games
+ *
* \brief Collection of ports on a console
*/
using CControllerTree = CControllerHub;
+
} // namespace GAME
} // namespace KODI
diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.h b/xbmc/games/controllers/windows/GUIConfigurationWizard.h
index b69badf5c9..a52a2ae4ea 100644
--- a/xbmc/games/controllers/windows/GUIConfigurationWizard.h
+++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.h
@@ -34,6 +34,9 @@ class IActionMap;
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIConfigurationWizard : public IConfigurationWizard,
public JOYSTICK::IButtonMapper,
public KEYBOARD::IKeyboardDriverHandler,
diff --git a/xbmc/games/controllers/windows/GUIControllerList.h b/xbmc/games/controllers/windows/GUIControllerList.h
index 68c3485ec4..539b8ec3eb 100644
--- a/xbmc/games/controllers/windows/GUIControllerList.h
+++ b/xbmc/games/controllers/windows/GUIControllerList.h
@@ -26,6 +26,9 @@ namespace GAME
{
class CGUIControllerWindow;
+/*!
+ * \ingroup games
+ */
class CGUIControllerList : public IControllerList
{
public:
diff --git a/xbmc/games/controllers/windows/GUIControllerWindow.h b/xbmc/games/controllers/windows/GUIControllerWindow.h
index 1913885810..2c15b2c55d 100644
--- a/xbmc/games/controllers/windows/GUIControllerWindow.h
+++ b/xbmc/games/controllers/windows/GUIControllerWindow.h
@@ -22,6 +22,9 @@ class CControllerInstaller;
class IControllerList;
class IFeatureList;
+/*!
+ * \ingroup games
+ */
class CGUIControllerWindow : public CGUIDialog
{
public:
diff --git a/xbmc/games/controllers/windows/GUIFeatureList.h b/xbmc/games/controllers/windows/GUIFeatureList.h
index 087709a669..127f67ded1 100644
--- a/xbmc/games/controllers/windows/GUIFeatureList.h
+++ b/xbmc/games/controllers/windows/GUIFeatureList.h
@@ -24,6 +24,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIFeatureList : public IFeatureList
{
public:
diff --git a/xbmc/games/controllers/windows/IConfigurationWindow.h b/xbmc/games/controllers/windows/IConfigurationWindow.h
index 017adf0f87..ec93bb97b9 100644
--- a/xbmc/games/controllers/windows/IConfigurationWindow.h
+++ b/xbmc/games/controllers/windows/IConfigurationWindow.h
@@ -17,11 +17,20 @@
class CEvent;
+namespace KODI
+{
+namespace GAME
+{
+class CPhysicalFeature;
+
/*!
- * \brief Controller configuration window
+ * \ingroup games
+ *
+ * \brief A list populated by installed controllers for the controller
+ * configuration window
*
* The configuration window presents a list of controllers. Also on the screen
- * is a list of features belonging to that controller.
+ * is a list of features (\ref IFeatureList) belonging to that controller.
*
* The configuration utility reacts to several events:
*
@@ -29,22 +38,12 @@ class CEvent;
* controller's features.
*
* 2) When a feature is selected, the user is prompted for controller input.
- * This initiates a "wizard" that walks the user through the subsequent
- * features.
+ * This initiates a "wizard" (\ref IConfigurationWizard) that walks the
+ * user through the subsequent features.
*
* 3) When the wizard's active feature loses focus, the wizard is cancelled
* and the prompt for input ends.
*/
-
-namespace KODI
-{
-namespace GAME
-{
-class CPhysicalFeature;
-
-/*!
- * \brief A list populated by installed controllers
- */
class IControllerList
{
public:
@@ -52,6 +51,7 @@ public:
/*!
* \brief Initialize the resource
+ *
* \return true if the resource is initialized and can be used
* false if the resource failed to initialize and must not be used
*/
@@ -64,25 +64,30 @@ public:
/*!
* \brief Refresh the contents of the list
+ *
* \param controllerId The controller to focus, or empty to leave focus unchanged
+ *
* \return True if the list was changed
*/
virtual bool Refresh(const std::string& controllerId) = 0;
/*
* \brief The specified controller has been focused
+ *
* \param controllerIndex The index of the controller being focused
*/
virtual void OnFocus(unsigned int controllerIndex) = 0;
/*!
* \brief The specified controller has been selected
+ *
* \param controllerIndex The index of the controller being selected
*/
virtual void OnSelect(unsigned int controllerIndex) = 0;
/*!
* \brief Get the index of the focused controller
+ *
* \return The index of the focused controller, or -1 if no controller has been focused yet
*/
virtual int GetFocusedController() const = 0;
@@ -94,7 +99,13 @@ public:
};
/*!
+ * \ingroup games
+ *
* \brief A list populated by the controller's features
+ *
+ * The feature list is populated by features (\ref IFeatureButton) belonging
+ * to the current controller selected in the controller list
+ * (\ref IControllerList).
*/
class IFeatureList
{
@@ -141,7 +152,9 @@ public:
};
/*!
- * \brief A GUI button in a feature list
+ * \ingroup games
+ *
+ * \brief A GUI button in a feature list (\ref IFeatureList)
*/
class IFeatureButton
{
@@ -216,7 +229,13 @@ public:
};
/*!
+ * \ingroup games
+ *
* \brief A wizard to direct user input
+ *
+ * The wizard is run for the current controller selected in the controller list
+ * (\ref IControllerList). It prompts the user for input for each feature
+ * (\ref IFeatureButton) in the feature list (\ref IFeatureList).
*/
class IConfigurationWizard
{
diff --git a/xbmc/games/dialogs/GUIDialogSelectGameClient.h b/xbmc/games/dialogs/GUIDialogSelectGameClient.h
index c492481df1..8025d31aec 100644
--- a/xbmc/games/dialogs/GUIDialogSelectGameClient.h
+++ b/xbmc/games/dialogs/GUIDialogSelectGameClient.h
@@ -18,6 +18,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIDialogSelectGameClient
{
public:
diff --git a/xbmc/games/dialogs/GUIDialogSelectSavestate.h b/xbmc/games/dialogs/GUIDialogSelectSavestate.h
index e554142375..0563749ca8 100644
--- a/xbmc/games/dialogs/GUIDialogSelectSavestate.h
+++ b/xbmc/games/dialogs/GUIDialogSelectSavestate.h
@@ -16,6 +16,9 @@ namespace GAME
{
class CDialogGameSaves;
+/*!
+ * \ingroup games
+ */
class CGUIDialogSelectSavestate
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameAdvancedSettings.h b/xbmc/games/dialogs/osd/DialogGameAdvancedSettings.h
index 00b669b040..a4daabe2d6 100644
--- a/xbmc/games/dialogs/osd/DialogGameAdvancedSettings.h
+++ b/xbmc/games/dialogs/osd/DialogGameAdvancedSettings.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameAdvancedSettings : public CGUIDialog
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameOSD.h b/xbmc/games/dialogs/osd/DialogGameOSD.h
index 3a8127b609..06dd1694fd 100644
--- a/xbmc/games/dialogs/osd/DialogGameOSD.h
+++ b/xbmc/games/dialogs/osd/DialogGameOSD.h
@@ -18,6 +18,9 @@ namespace GAME
{
class CDialogGameOSDHelp;
+/*!
+ * \ingroup games
+ */
class CDialogGameOSD : public CGUIDialog
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameOSDHelp.cpp b/xbmc/games/dialogs/osd/DialogGameOSDHelp.cpp
index d50d8631d7..b849a0aa2e 100644
--- a/xbmc/games/dialogs/osd/DialogGameOSDHelp.cpp
+++ b/xbmc/games/dialogs/osd/DialogGameOSDHelp.cpp
@@ -17,6 +17,12 @@
using namespace KODI;
using namespace GAME;
+namespace
+{
+constexpr char HELP_COMBO[] =
+ "[B]$FEATURE[select,game.controller.snes] + $FEATURE[x,game.controller.default][/B]";
+}
+
const int CDialogGameOSDHelp::CONTROL_ID_HELP_TEXT = 1101;
CDialogGameOSDHelp::CDialogGameOSDHelp(CDialogGameOSD& dialog) : m_dialog(dialog)
@@ -26,9 +32,8 @@ CDialogGameOSDHelp::CDialogGameOSDHelp(CDialogGameOSD& dialog) : m_dialog(dialog
void CDialogGameOSDHelp::OnInitWindow()
{
// Set help text
- //! @todo Define Select + X combo elsewhere
// "Press {0:s} to open the menu."
- std::string helpText = StringUtils::Format(g_localizeStrings.Get(35235), "Select + X");
+ std::string helpText = StringUtils::Format(g_localizeStrings.Get(35235), HELP_COMBO);
CGUIMessage msg(GUI_MSG_LABEL_SET, WINDOW_DIALOG_GAME_OSD, CONTROL_ID_HELP_TEXT);
msg.SetLabel(helpText);
diff --git a/xbmc/games/dialogs/osd/DialogGameSaves.h b/xbmc/games/dialogs/osd/DialogGameSaves.h
index 3605ee60d0..3183c2cb7d 100644
--- a/xbmc/games/dialogs/osd/DialogGameSaves.h
+++ b/xbmc/games/dialogs/osd/DialogGameSaves.h
@@ -22,6 +22,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameSaves : public CGUIDialog
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameStretchMode.h b/xbmc/games/dialogs/osd/DialogGameStretchMode.h
index 1e05061a59..3870db954e 100644
--- a/xbmc/games/dialogs/osd/DialogGameStretchMode.h
+++ b/xbmc/games/dialogs/osd/DialogGameStretchMode.h
@@ -17,6 +17,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameStretchMode : public CDialogGameVideoSelect
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameVideoFilter.h b/xbmc/games/dialogs/osd/DialogGameVideoFilter.h
index 7ec8c76c59..237720d65d 100644
--- a/xbmc/games/dialogs/osd/DialogGameVideoFilter.h
+++ b/xbmc/games/dialogs/osd/DialogGameVideoFilter.h
@@ -15,6 +15,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameVideoFilter : public CDialogGameVideoSelect
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameVideoRotation.h b/xbmc/games/dialogs/osd/DialogGameVideoRotation.h
index ef454db915..f19a6ee398 100644
--- a/xbmc/games/dialogs/osd/DialogGameVideoRotation.h
+++ b/xbmc/games/dialogs/osd/DialogGameVideoRotation.h
@@ -17,6 +17,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameVideoRotation : public CDialogGameVideoSelect
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameVideoSelect.h b/xbmc/games/dialogs/osd/DialogGameVideoSelect.h
index c14c94e505..836ecd8e0c 100644
--- a/xbmc/games/dialogs/osd/DialogGameVideoSelect.h
+++ b/xbmc/games/dialogs/osd/DialogGameVideoSelect.h
@@ -24,6 +24,9 @@ class CGUIGameVideoHandle;
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameVideoSelect : public CGUIDialog
{
public:
diff --git a/xbmc/games/dialogs/osd/DialogGameVolume.h b/xbmc/games/dialogs/osd/DialogGameVolume.h
index a32339d4bd..679ad82873 100644
--- a/xbmc/games/dialogs/osd/DialogGameVolume.h
+++ b/xbmc/games/dialogs/osd/DialogGameVolume.h
@@ -20,6 +20,9 @@ namespace KODI
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogGameVolume : public CGUIDialogSlider, // GUI interface
public ISliderCallback, // GUI callback
public IGUIVolumeBarCallback, // Volume bar dialog callback
diff --git a/xbmc/games/dialogs/osd/DialogInGameSaves.h b/xbmc/games/dialogs/osd/DialogInGameSaves.h
index e4205ad571..0adc5aa5a6 100644
--- a/xbmc/games/dialogs/osd/DialogInGameSaves.h
+++ b/xbmc/games/dialogs/osd/DialogInGameSaves.h
@@ -18,6 +18,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CDialogInGameSaves : public CDialogGameVideoSelect
{
public:
diff --git a/xbmc/games/ports/guicontrols/GUIActivePortList.h b/xbmc/games/ports/guicontrols/GUIActivePortList.h
index a55e879d2d..20bfcd6877 100644
--- a/xbmc/games/ports/guicontrols/GUIActivePortList.h
+++ b/xbmc/games/ports/guicontrols/GUIActivePortList.h
@@ -27,6 +27,9 @@ namespace GAME
class CController;
class IActivePortList;
+/*!
+ * \ingroup games
+ */
class CGUIActivePortList : public IActivePortList, public Observer
{
public:
diff --git a/xbmc/games/ports/guicontrols/IActivePortList.h b/xbmc/games/ports/guicontrols/IActivePortList.h
index 41526c4f72..88b6ad3463 100644
--- a/xbmc/games/ports/guicontrols/IActivePortList.h
+++ b/xbmc/games/ports/guicontrols/IActivePortList.h
@@ -21,11 +21,16 @@ namespace GAME
*
* \brief A list populated by input ports on a game console
*
+ * In the Player Viewer dialog (<b>`GameAgents`</b> window), this list has
+ * control ID 4.
+ *
* Each port is an item in the list. Ports are represented by the controller
* icon of the connected controller.
*
* Ports are only included in the list if the controller profile provides
* input. For example, Multitaps will be skipped.
+ *
+ * When ports are changed, the player list (\ref IAgentList) is updated.
*/
class IActivePortList
{
diff --git a/xbmc/games/ports/input/PhysicalPort.h b/xbmc/games/ports/input/PhysicalPort.h
index 51dba223c4..d7b32463fb 100644
--- a/xbmc/games/ports/input/PhysicalPort.h
+++ b/xbmc/games/ports/input/PhysicalPort.h
@@ -21,6 +21,9 @@ namespace KODI
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CPhysicalPort
{
public:
diff --git a/xbmc/games/ports/input/PortInput.cpp b/xbmc/games/ports/input/PortInput.cpp
index ccfe3093ee..253567981c 100644
--- a/xbmc/games/ports/input/PortInput.cpp
+++ b/xbmc/games/ports/input/PortInput.cpp
@@ -15,6 +15,8 @@
#include "input/joysticks/keymaps/KeymapHandling.h"
#include "peripherals/devices/Peripheral.h"
+#include <memory>
+
using namespace KODI;
using namespace GAME;
@@ -37,7 +39,7 @@ void CPortInput::RegisterInput(JOYSTICK::IInputProvider* provider)
provider->RegisterInputHandler(this, false);
// Register GUI input
- m_appInput.reset(new JOYSTICK::CKeymapHandling(provider, false, this));
+ m_appInput = std::make_unique<JOYSTICK::CKeymapHandling>(provider, false, this);
}
void CPortInput::UnregisterInput(JOYSTICK::IInputProvider* provider)
diff --git a/xbmc/games/ports/input/PortInput.h b/xbmc/games/ports/input/PortInput.h
index 56a293449e..a5221cd938 100644
--- a/xbmc/games/ports/input/PortInput.h
+++ b/xbmc/games/ports/input/PortInput.h
@@ -25,6 +25,9 @@ namespace GAME
{
class CControllerActivity;
+/*!
+ * \ingroup games
+ */
class CPortInput : public JOYSTICK::IInputHandler, public IKeymapEnvironment
{
public:
diff --git a/xbmc/games/ports/input/PortManager.h b/xbmc/games/ports/input/PortManager.h
index 85dc4255f3..23c26d9ad5 100644
--- a/xbmc/games/ports/input/PortManager.h
+++ b/xbmc/games/ports/input/PortManager.h
@@ -23,6 +23,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CPortManager
{
public:
diff --git a/xbmc/games/ports/types/PortNode.h b/xbmc/games/ports/types/PortNode.h
index 4c5f7404f5..a07e1ee786 100644
--- a/xbmc/games/ports/types/PortNode.h
+++ b/xbmc/games/ports/types/PortNode.h
@@ -21,6 +21,8 @@ namespace GAME
class CPhysicalPort;
/*!
+ * \ingroup games
+ *
* \brief Collection of nodes that can be connected to this port
*/
class CPortNode
diff --git a/xbmc/games/ports/windows/GUIPortDefines.h b/xbmc/games/ports/windows/GUIPortDefines.h
index 5d587e9317..9736a200e8 100644
--- a/xbmc/games/ports/windows/GUIPortDefines.h
+++ b/xbmc/games/ports/windows/GUIPortDefines.h
@@ -21,5 +21,9 @@
// Skin XML file
#define PORT_DIALOG_XML "DialogGameControllers.xml"
-// Allow for two Saturn 6 Player Adapters
+/*!
+ * \ingroup games
+ *
+ * \brief The maximum port count, chosen to allow for two Saturn 6 Player Adapters
+ */
constexpr unsigned int MAX_PORT_COUNT = 12;
diff --git a/xbmc/games/ports/windows/GUIPortList.h b/xbmc/games/ports/windows/GUIPortList.h
index 3fed6b7564..98e394ffcd 100644
--- a/xbmc/games/ports/windows/GUIPortList.h
+++ b/xbmc/games/ports/windows/GUIPortList.h
@@ -27,6 +27,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup game
+ */
class CGUIPortList : public IPortList
{
public:
diff --git a/xbmc/games/ports/windows/GUIPortWindow.h b/xbmc/games/ports/windows/GUIPortWindow.h
index 3b987080f2..c14dc9362a 100644
--- a/xbmc/games/ports/windows/GUIPortWindow.h
+++ b/xbmc/games/ports/windows/GUIPortWindow.h
@@ -19,6 +19,9 @@ namespace GAME
{
class IPortList;
+/*!
+ * \ingroup games
+ */
class CGUIPortWindow : public CGUIDialog
{
public:
diff --git a/xbmc/games/ports/windows/IPortList.h b/xbmc/games/ports/windows/IPortList.h
index e330c6e555..5ef4bb8220 100644
--- a/xbmc/games/ports/windows/IPortList.h
+++ b/xbmc/games/ports/windows/IPortList.h
@@ -10,27 +10,25 @@
#include "games/GameTypes.h"
+namespace KODI
+{
+namespace GAME
+{
/*!
- * \brief Controller port setup window
+ * \ingroup games
+ *
+ * \brief A list populated by controller ports for the port setup window
*
* The port setup window presents a list of ports and their attached
* controllers.
*
* The label2 of each port is the currently-connected controller. The user
* selects from all controllers that the port accepts (as given by the
- * game-addon's topology.xml file).
+ * game-addon's <b>`topology.xml`</b> file).
*
* The controller topology is stored as a generic tree. Here we apply game logic
* to simplify controller selection.
*/
-
-namespace KODI
-{
-namespace GAME
-{
-/*!
- * \brief A list populated by controller ports
- */
class IPortList
{
public:
diff --git a/xbmc/games/tags/GameInfoTag.h b/xbmc/games/tags/GameInfoTag.h
index 00287ad18b..94d0ef3e0c 100644
--- a/xbmc/games/tags/GameInfoTag.h
+++ b/xbmc/games/tags/GameInfoTag.h
@@ -18,6 +18,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGameInfoTag : public IArchivable, public ISerializable, public ISortable
{
public:
diff --git a/xbmc/games/windows/GUIViewStateWindowGames.h b/xbmc/games/windows/GUIViewStateWindowGames.h
index 90ad8213ec..2da9fd6360 100644
--- a/xbmc/games/windows/GUIViewStateWindowGames.h
+++ b/xbmc/games/windows/GUIViewStateWindowGames.h
@@ -14,6 +14,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIViewStateWindowGames : public CGUIViewState
{
public:
diff --git a/xbmc/games/windows/GUIWindowGames.h b/xbmc/games/windows/GUIWindowGames.h
index 2fbabfa676..fd7f833697 100644
--- a/xbmc/games/windows/GUIWindowGames.h
+++ b/xbmc/games/windows/GUIWindowGames.h
@@ -16,6 +16,9 @@ namespace KODI
{
namespace GAME
{
+/*!
+ * \ingroup games
+ */
class CGUIWindowGames : public CGUIMediaWindow
{
public:
diff --git a/xbmc/guilib/GUIBaseContainer.cpp b/xbmc/guilib/GUIBaseContainer.cpp
index 237ced88a8..3db7db9364 100644
--- a/xbmc/guilib/GUIBaseContainer.cpp
+++ b/xbmc/guilib/GUIBaseContainer.cpp
@@ -26,6 +26,8 @@
#include "utils/XBMCTinyXML.h"
#include "utils/log.h"
+#include <memory>
+
#define HOLD_TIME_START 100
#define HOLD_TIME_END 3000
#define SCROLLING_GAP 200U
diff --git a/xbmc/guilib/GUIColorManager.cpp b/xbmc/guilib/GUIColorManager.cpp
index b3ff2dbaa9..0034fedce0 100644
--- a/xbmc/guilib/GUIColorManager.cpp
+++ b/xbmc/guilib/GUIColorManager.cpp
@@ -130,9 +130,8 @@ bool CGUIColorManager::LoadColorsListFromXML(
{
if (xmlColor->FirstChild() && xmlColor->Attribute("name"))
{
- colors.emplace_back(
- std::make_pair(xmlColor->Attribute("name"),
- UTILS::COLOR::MakeColorInfo(xmlColor->FirstChild()->Value())));
+ colors.emplace_back(xmlColor->Attribute("name"),
+ UTILS::COLOR::MakeColorInfo(xmlColor->FirstChild()->Value()));
}
xmlColor = xmlColor->NextSiblingElement("color");
}
diff --git a/xbmc/guilib/GUIComponent.cpp b/xbmc/guilib/GUIComponent.cpp
index e143e7557e..8e908856e3 100644
--- a/xbmc/guilib/GUIComponent.cpp
+++ b/xbmc/guilib/GUIComponent.cpp
@@ -19,15 +19,17 @@
#include "URL.h"
#include "dialogs/GUIDialogYesNo.h"
+#include <memory>
+
CGUIComponent::CGUIComponent()
{
- m_pWindowManager.reset(new CGUIWindowManager());
- m_pTextureManager.reset(new CGUITextureManager());
- m_pLargeTextureManager.reset(new CGUILargeTextureManager());
- m_stereoscopicsManager.reset(new CStereoscopicsManager());
- m_guiInfoManager.reset(new CGUIInfoManager());
- m_guiColorManager.reset(new CGUIColorManager());
- m_guiAudioManager.reset(new CGUIAudioManager());
+ m_pWindowManager = std::make_unique<CGUIWindowManager>();
+ m_pTextureManager = std::make_unique<CGUITextureManager>();
+ m_pLargeTextureManager = std::make_unique<CGUILargeTextureManager>();
+ m_stereoscopicsManager = std::make_unique<CStereoscopicsManager>();
+ m_guiInfoManager = std::make_unique<CGUIInfoManager>();
+ m_guiColorManager = std::make_unique<CGUIColorManager>();
+ m_guiAudioManager = std::make_unique<CGUIAudioManager>();
}
CGUIComponent::~CGUIComponent()
diff --git a/xbmc/guilib/GUIControlFactory.cpp b/xbmc/guilib/GUIControlFactory.cpp
index 5b81a57677..b65fc95f25 100644
--- a/xbmc/guilib/GUIControlFactory.cpp
+++ b/xbmc/guilib/GUIControlFactory.cpp
@@ -106,7 +106,7 @@ static const ControlMapping controls[] = {
{"wraplist", CGUIControl::GUICONTAINER_WRAPLIST},
};
-CGUIControl::GUICONTROLTYPES CGUIControlFactory::TranslateControlType(const std::string &type)
+CGUIControl::GUICONTROLTYPES CGUIControlFactory::TranslateControlType(const std::string& type)
{
for (const ControlMapping& control : controls)
if (StringUtils::EqualsNoCase(type, control.name))
@@ -126,10 +126,15 @@ CGUIControlFactory::CGUIControlFactory(void) = default;
CGUIControlFactory::~CGUIControlFactory(void) = default;
-bool CGUIControlFactory::GetIntRange(const TiXmlNode* pRootNode, const char* strTag, int& iMinValue, int& iMaxValue, int& iIntervalValue)
+bool CGUIControlFactory::GetIntRange(const TiXmlNode* pRootNode,
+ const char* strTag,
+ int& iMinValue,
+ int& iMaxValue,
+ int& iIntervalValue)
{
const TiXmlNode* pNode = pRootNode->FirstChild(strTag);
- if (!pNode || !pNode->FirstChild()) return false;
+ if (!pNode || !pNode->FirstChild())
+ return false;
iMinValue = atoi(pNode->FirstChild()->Value());
const char* maxValue = strchr(pNode->FirstChild()->Value(), ',');
if (maxValue)
@@ -148,10 +153,15 @@ bool CGUIControlFactory::GetIntRange(const TiXmlNode* pRootNode, const char* str
return true;
}
-bool CGUIControlFactory::GetFloatRange(const TiXmlNode* pRootNode, const char* strTag, float& fMinValue, float& fMaxValue, float& fIntervalValue)
+bool CGUIControlFactory::GetFloatRange(const TiXmlNode* pRootNode,
+ const char* strTag,
+ float& fMinValue,
+ float& fMaxValue,
+ float& fIntervalValue)
{
const TiXmlNode* pNode = pRootNode->FirstChild(strTag);
- if (!pNode || !pNode->FirstChild()) return false;
+ if (!pNode || !pNode->FirstChild())
+ return false;
fMinValue = (float)atof(pNode->FirstChild()->Value());
const char* maxValue = strchr(pNode->FirstChild()->Value(), ',');
if (maxValue)
@@ -184,33 +194,50 @@ float CGUIControlFactory::ParsePosition(const char* pos, const float parentSize)
return value;
}
-bool CGUIControlFactory::GetPosition(const TiXmlNode *node, const char* strTag, const float parentSize, float& value)
+bool CGUIControlFactory::GetPosition(const TiXmlNode* node,
+ const char* strTag,
+ const float parentSize,
+ float& value)
{
const TiXmlElement* pNode = node->FirstChildElement(strTag);
- if (!pNode || !pNode->FirstChild()) return false;
+ if (!pNode || !pNode->FirstChild())
+ return false;
value = ParsePosition(pNode->FirstChild()->Value(), parentSize);
return true;
}
-bool CGUIControlFactory::GetDimension(const TiXmlNode *pRootNode, const char* strTag, const float parentSize, float &value, float &min)
+bool CGUIControlFactory::GetDimension(const TiXmlNode* pRootNode,
+ const char* strTag,
+ const float parentSize,
+ float& value,
+ float& min)
{
const TiXmlElement* pNode = pRootNode->FirstChildElement(strTag);
- if (!pNode || !pNode->FirstChild()) return false;
+ if (!pNode || !pNode->FirstChild())
+ return false;
if (0 == StringUtils::CompareNoCase("auto", pNode->FirstChild()->Value(), 4))
{ // auto-width - at least min must be set
value = ParsePosition(pNode->Attribute("max"), parentSize);
min = ParsePosition(pNode->Attribute("min"), parentSize);
- if (!min) min = 1;
+ if (!min)
+ min = 1;
return true;
}
value = ParsePosition(pNode->FirstChild()->Value(), parentSize);
return true;
}
-bool CGUIControlFactory::GetDimensions(const TiXmlNode *node, const char *leftTag, const char *rightTag, const char *centerLeftTag,
- const char *centerRightTag, const char *widthTag, const float parentSize, float &left,
- float &width, float &min_width)
+bool CGUIControlFactory::GetDimensions(const TiXmlNode* node,
+ const char* leftTag,
+ const char* rightTag,
+ const char* centerLeftTag,
+ const char* centerRightTag,
+ const char* widthTag,
+ const float parentSize,
+ float& left,
+ float& width,
+ float& min_width)
{
float center = 0, right = 0;
@@ -236,7 +263,7 @@ bool CGUIControlFactory::GetDimensions(const TiXmlNode *node, const char *leftTa
{
if (hasWidth)
{
- left = center - width/2;
+ left = center - width / 2;
hasLeft = true;
}
else
@@ -276,7 +303,7 @@ bool CGUIControlFactory::GetDimensions(const TiXmlNode *node, const char *leftTa
else if (center > 0 && center < parentSize)
{ // centre given, so fill to edge of parent
width = std::max(0.0f, std::min(parentSize - center, center) * 2);
- left = center - width/2;
+ left = center - width / 2;
hasLeft = true;
hasWidth = true;
}
@@ -290,34 +317,46 @@ bool CGUIControlFactory::GetDimensions(const TiXmlNode *node, const char *leftTa
return hasLeft && hasWidth;
}
-bool CGUIControlFactory::GetAspectRatio(const TiXmlNode* pRootNode, const char* strTag, CAspectRatio &aspect)
+bool CGUIControlFactory::GetAspectRatio(const TiXmlNode* pRootNode,
+ const char* strTag,
+ CAspectRatio& aspect)
{
std::string ratio;
- const TiXmlElement *node = pRootNode->FirstChildElement(strTag);
+ const TiXmlElement* node = pRootNode->FirstChildElement(strTag);
if (!node || !node->FirstChild())
return false;
ratio = node->FirstChild()->Value();
- if (StringUtils::EqualsNoCase(ratio, "keep")) aspect.ratio = CAspectRatio::AR_KEEP;
- else if (StringUtils::EqualsNoCase(ratio, "scale")) aspect.ratio = CAspectRatio::AR_SCALE;
- else if (StringUtils::EqualsNoCase(ratio, "center")) aspect.ratio = CAspectRatio::AR_CENTER;
- else if (StringUtils::EqualsNoCase(ratio, "stretch")) aspect.ratio = CAspectRatio::AR_STRETCH;
-
- const char *attribute = node->Attribute("align");
+ if (StringUtils::EqualsNoCase(ratio, "keep"))
+ aspect.ratio = CAspectRatio::AR_KEEP;
+ else if (StringUtils::EqualsNoCase(ratio, "scale"))
+ aspect.ratio = CAspectRatio::AR_SCALE;
+ else if (StringUtils::EqualsNoCase(ratio, "center"))
+ aspect.ratio = CAspectRatio::AR_CENTER;
+ else if (StringUtils::EqualsNoCase(ratio, "stretch"))
+ aspect.ratio = CAspectRatio::AR_STRETCH;
+
+ const char* attribute = node->Attribute("align");
if (attribute)
{
std::string align(attribute);
- if (StringUtils::EqualsNoCase(align, "center")) aspect.align = ASPECT_ALIGN_CENTER | (aspect.align & ASPECT_ALIGNY_MASK);
- else if (StringUtils::EqualsNoCase(align, "right")) aspect.align = ASPECT_ALIGN_RIGHT | (aspect.align & ASPECT_ALIGNY_MASK);
- else if (StringUtils::EqualsNoCase(align, "left")) aspect.align = ASPECT_ALIGN_LEFT | (aspect.align & ASPECT_ALIGNY_MASK);
+ if (StringUtils::EqualsNoCase(align, "center"))
+ aspect.align = ASPECT_ALIGN_CENTER | (aspect.align & ASPECT_ALIGNY_MASK);
+ else if (StringUtils::EqualsNoCase(align, "right"))
+ aspect.align = ASPECT_ALIGN_RIGHT | (aspect.align & ASPECT_ALIGNY_MASK);
+ else if (StringUtils::EqualsNoCase(align, "left"))
+ aspect.align = ASPECT_ALIGN_LEFT | (aspect.align & ASPECT_ALIGNY_MASK);
}
attribute = node->Attribute("aligny");
if (attribute)
{
std::string align(attribute);
- if (StringUtils::EqualsNoCase(align, "center")) aspect.align = ASPECT_ALIGNY_CENTER | (aspect.align & ASPECT_ALIGN_MASK);
- else if (StringUtils::EqualsNoCase(align, "bottom")) aspect.align = ASPECT_ALIGNY_BOTTOM | (aspect.align & ASPECT_ALIGN_MASK);
- else if (StringUtils::EqualsNoCase(align, "top")) aspect.align = ASPECT_ALIGNY_TOP | (aspect.align & ASPECT_ALIGN_MASK);
+ if (StringUtils::EqualsNoCase(align, "center"))
+ aspect.align = ASPECT_ALIGNY_CENTER | (aspect.align & ASPECT_ALIGN_MASK);
+ else if (StringUtils::EqualsNoCase(align, "bottom"))
+ aspect.align = ASPECT_ALIGNY_BOTTOM | (aspect.align & ASPECT_ALIGN_MASK);
+ else if (StringUtils::EqualsNoCase(align, "top"))
+ aspect.align = ASPECT_ALIGNY_TOP | (aspect.align & ASPECT_ALIGN_MASK);
}
attribute = node->Attribute("scalediffuse");
if (attribute)
@@ -331,7 +370,11 @@ bool CGUIControlFactory::GetAspectRatio(const TiXmlNode* pRootNode, const char*
return true;
}
-bool CGUIControlFactory::GetInfoTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image, GUIINFO::CGUIInfoLabel &info, int parentID)
+bool CGUIControlFactory::GetInfoTexture(const TiXmlNode* pRootNode,
+ const char* strTag,
+ CTextureInfo& image,
+ GUIINFO::CGUIInfoLabel& info,
+ int parentID)
{
GetTexture(pRootNode, strTag, image);
image.filename = "";
@@ -339,11 +382,14 @@ bool CGUIControlFactory::GetInfoTexture(const TiXmlNode* pRootNode, const char*
return true;
}
-bool CGUIControlFactory::GetTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image)
+bool CGUIControlFactory::GetTexture(const TiXmlNode* pRootNode,
+ const char* strTag,
+ CTextureInfo& image)
{
const TiXmlElement* pNode = pRootNode->FirstChildElement(strTag);
- if (!pNode) return false;
- const char *border = pNode->Attribute("border");
+ if (!pNode)
+ return false;
+ const char* border = pNode->Attribute("border");
if (border)
{
GetRectFromString(border, image.border);
@@ -351,22 +397,22 @@ bool CGUIControlFactory::GetTexture(const TiXmlNode* pRootNode, const char* strT
image.m_infill = (!borderinfill || !StringUtils::EqualsNoCase(borderinfill, "false"));
}
image.orientation = 0;
- const char *flipX = pNode->Attribute("flipx");
+ const char* flipX = pNode->Attribute("flipx");
if (flipX && StringUtils::CompareNoCase(flipX, "true") == 0)
image.orientation = 1;
- const char *flipY = pNode->Attribute("flipy");
+ const char* flipY = pNode->Attribute("flipy");
if (flipY && StringUtils::CompareNoCase(flipY, "true") == 0)
image.orientation = 3 - image.orientation; // either 3 or 2
image.diffuse = XMLUtils::GetAttribute(pNode, "diffuse");
image.diffuseColor.Parse(XMLUtils::GetAttribute(pNode, "colordiffuse"), 0);
- const char *background = pNode->Attribute("background");
+ const char* background = pNode->Attribute("background");
if (background && StringUtils::CompareNoCase(background, "true", 4) == 0)
image.useLarge = true;
image.filename = pNode->FirstChild() ? pNode->FirstChild()->Value() : "";
return true;
}
-void CGUIControlFactory::GetRectFromString(const std::string &string, CRect &rect)
+void CGUIControlFactory::GetRectFromString(const std::string& string, CRect& rect)
{
// format is rect="left[,top,right,bottom]"
std::vector<std::string> strRect = StringUtils::Split(string, ',');
@@ -386,22 +432,31 @@ void CGUIControlFactory::GetRectFromString(const std::string &string, CRect &rec
}
}
-bool CGUIControlFactory::GetAlignment(const TiXmlNode* pRootNode, const char* strTag, uint32_t& alignment)
+bool CGUIControlFactory::GetAlignment(const TiXmlNode* pRootNode,
+ const char* strTag,
+ uint32_t& alignment)
{
const TiXmlNode* pNode = pRootNode->FirstChild(strTag);
- if (!pNode || !pNode->FirstChild()) return false;
+ if (!pNode || !pNode->FirstChild())
+ return false;
std::string strAlign = pNode->FirstChild()->Value();
- if (strAlign == "right" || strAlign == "bottom") alignment = XBFONT_RIGHT;
- else if (strAlign == "center") alignment = XBFONT_CENTER_X;
- else if (strAlign == "justify") alignment = XBFONT_JUSTIFIED;
- else alignment = XBFONT_LEFT;
+ if (strAlign == "right" || strAlign == "bottom")
+ alignment = XBFONT_RIGHT;
+ else if (strAlign == "center")
+ alignment = XBFONT_CENTER_X;
+ else if (strAlign == "justify")
+ alignment = XBFONT_JUSTIFIED;
+ else
+ alignment = XBFONT_LEFT;
return true;
}
-bool CGUIControlFactory::GetAlignmentY(const TiXmlNode* pRootNode, const char* strTag, uint32_t& alignment)
+bool CGUIControlFactory::GetAlignmentY(const TiXmlNode* pRootNode,
+ const char* strTag,
+ uint32_t& alignment)
{
- const TiXmlNode* pNode = pRootNode->FirstChild(strTag );
+ const TiXmlNode* pNode = pRootNode->FirstChild(strTag);
if (!pNode || !pNode->FirstChild())
{
return false;
@@ -418,14 +473,17 @@ bool CGUIControlFactory::GetAlignmentY(const TiXmlNode* pRootNode, const char* s
return true;
}
-bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode* control, std::string &condition, std::string &allowHiddenFocus)
+bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode* control,
+ std::string& condition,
+ std::string& allowHiddenFocus)
{
const TiXmlElement* node = control->FirstChildElement("visible");
- if (!node) return false;
+ if (!node)
+ return false;
std::vector<std::string> conditions;
while (node)
{
- const char *hidden = node->Attribute("allowhiddenfocus");
+ const char* hidden = node->Attribute("allowhiddenfocus");
if (hidden)
allowHiddenFocus = hidden;
// add to our condition string
@@ -447,13 +505,16 @@ bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode* control, std:
return true;
}
-bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode *control, std::string &condition)
+bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode* control, std::string& condition)
{
std::string allowHiddenFocus;
return GetConditionalVisibility(control, condition, allowHiddenFocus);
}
-bool CGUIControlFactory::GetAnimations(TiXmlNode *control, const CRect &rect, int context, std::vector<CAnimation> &animations)
+bool CGUIControlFactory::GetAnimations(TiXmlNode* control,
+ const CRect& rect,
+ int context,
+ std::vector<CAnimation>& animations)
{
TiXmlElement* node = control->FirstChildElement("animation");
bool ret = false;
@@ -471,8 +532,8 @@ bool CGUIControlFactory::GetAnimations(TiXmlNode *control, const CRect &rect, in
{ // add the hidden one as well
TiXmlElement hidden(*node);
hidden.FirstChild()->SetValue("hidden");
- const char *start = hidden.Attribute("start");
- const char *end = hidden.Attribute("end");
+ const char* start = hidden.Attribute("start");
+ const char* end = hidden.Attribute("end");
if (start && end)
{
std::string temp = end;
@@ -511,7 +572,7 @@ bool CGUIControlFactory::GetActions(const TiXmlNode* pRootNode,
return actions.HasAnyActions();
}
-bool CGUIControlFactory::GetHitRect(const TiXmlNode *control, CRect &rect, const CRect &parentRect)
+bool CGUIControlFactory::GetHitRect(const TiXmlNode* control, CRect& rect, const CRect& parentRect)
{
const TiXmlElement* node = control->FirstChildElement("hitrect");
if (node)
@@ -531,7 +592,9 @@ bool CGUIControlFactory::GetHitRect(const TiXmlNode *control, CRect &rect, const
return false;
}
-bool CGUIControlFactory::GetScroller(const TiXmlNode *control, const std::string &scrollerTag, CScroller& scroller)
+bool CGUIControlFactory::GetScroller(const TiXmlNode* control,
+ const std::string& scrollerTag,
+ CScroller& scroller)
{
const TiXmlElement* node = control->FirstChildElement(scrollerTag);
if (node)
@@ -559,7 +622,10 @@ bool CGUIControlFactory::GetColor(const TiXmlNode* control,
return false;
}
-bool CGUIControlFactory::GetInfoColor(const TiXmlNode *control, const char *strTag, GUIINFO::CGUIInfoColor &value,int parentID)
+bool CGUIControlFactory::GetInfoColor(const TiXmlNode* control,
+ const char* strTag,
+ GUIINFO::CGUIInfoColor& value,
+ int parentID)
{
const TiXmlElement* node = control->FirstChildElement(strTag);
if (node && node->FirstChild())
@@ -570,7 +636,10 @@ bool CGUIControlFactory::GetInfoColor(const TiXmlNode *control, const char *strT
return false;
}
-void CGUIControlFactory::GetInfoLabel(const TiXmlNode *pControlNode, const std::string &labelTag, GUIINFO::CGUIInfoLabel &infoLabel, int parentID)
+void CGUIControlFactory::GetInfoLabel(const TiXmlNode* pControlNode,
+ const std::string& labelTag,
+ GUIINFO::CGUIInfoLabel& infoLabel,
+ int parentID)
{
std::vector<GUIINFO::CGUIInfoLabel> labels;
GetInfoLabels(pControlNode, labelTag, labels, parentID);
@@ -578,7 +647,9 @@ void CGUIControlFactory::GetInfoLabel(const TiXmlNode *pControlNode, const std::
infoLabel = labels[0];
}
-bool CGUIControlFactory::GetInfoLabelFromElement(const TiXmlElement *element, GUIINFO::CGUIInfoLabel &infoLabel, int parentID)
+bool CGUIControlFactory::GetInfoLabelFromElement(const TiXmlElement* element,
+ GUIINFO::CGUIInfoLabel& infoLabel,
+ int parentID)
{
if (!element || !element->FirstChild())
return false;
@@ -598,7 +669,10 @@ bool CGUIControlFactory::GetInfoLabelFromElement(const TiXmlElement *element, GU
return true;
}
-void CGUIControlFactory::GetInfoLabels(const TiXmlNode *pControlNode, const std::string &labelTag, std::vector<GUIINFO::CGUIInfoLabel> &infoLabels, int parentID)
+void CGUIControlFactory::GetInfoLabels(const TiXmlNode* pControlNode,
+ const std::string& labelTag,
+ std::vector<GUIINFO::CGUIInfoLabel>& infoLabels,
+ int parentID)
{
// we can have the following infolabels:
// 1. <number>1234</number> -> direct number
@@ -612,7 +686,7 @@ void CGUIControlFactory::GetInfoLabels(const TiXmlNode *pControlNode, const std:
infoLabels.emplace_back(label);
return; // done
}
- const TiXmlElement *labelNode = pControlNode->FirstChildElement(labelTag);
+ const TiXmlElement* labelNode = pControlNode->FirstChildElement(labelTag);
while (labelNode)
{
GUIINFO::CGUIInfoLabel label;
@@ -620,7 +694,7 @@ void CGUIControlFactory::GetInfoLabels(const TiXmlNode *pControlNode, const std:
infoLabels.push_back(label);
labelNode = labelNode->NextSiblingElement(labelTag);
}
- const TiXmlNode *infoNode = pControlNode->FirstChild("info");
+ const TiXmlNode* infoNode = pControlNode->FirstChild("info");
if (infoNode)
{ // <info> nodes override <label>'s (backward compatibility)
std::string fallback;
@@ -640,7 +714,7 @@ void CGUIControlFactory::GetInfoLabels(const TiXmlNode *pControlNode, const std:
}
// Convert a string to a GUI label, by translating/parsing the label for localisable strings
-std::string CGUIControlFactory::FilterLabel(const std::string &label)
+std::string CGUIControlFactory::FilterLabel(const std::string& label)
{
std::string viewLabel = label;
if (StringUtils::IsNaturalNumber(viewLabel))
@@ -650,7 +724,9 @@ std::string CGUIControlFactory::FilterLabel(const std::string &label)
return viewLabel;
}
-bool CGUIControlFactory::GetString(const TiXmlNode* pRootNode, const char *strTag, std::string &text)
+bool CGUIControlFactory::GetString(const TiXmlNode* pRootNode,
+ const char* strTag,
+ std::string& text)
{
if (!XMLUtils::GetString(pRootNode, strTag, text))
return false;
@@ -659,10 +735,10 @@ bool CGUIControlFactory::GetString(const TiXmlNode* pRootNode, const char *strTa
return true;
}
-std::string CGUIControlFactory::GetType(const TiXmlElement *pControlNode)
+std::string CGUIControlFactory::GetType(const TiXmlElement* pControlNode)
{
std::string type = XMLUtils::GetAttribute(pControlNode, "type");
- if (type.empty()) // backward compatibility - not desired
+ if (type.empty()) // backward compatibility - not desired
XMLUtils::GetString(pControlNode, "type", type);
return type;
}
@@ -712,7 +788,10 @@ bool CGUIControlFactory::GetMovingSpeedConfig(const TiXmlNode* pRootNode,
return true;
}
-CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlElement* pControlNode, bool insideContainer)
+CGUIControl* CGUIControlFactory::Create(int parentID,
+ const CRect& rect,
+ TiXmlElement* pControlNode,
+ bool insideContainer)
{
// get the control type
std::string strType = GetType(pControlNode);
@@ -729,12 +808,12 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
GUIINFO::CGUIInfoColor colorDiffuse(0xFFFFFFFF);
GUIINFO::CGUIInfoColor colorBox(0xFF000000);
int defaultControl = 0;
- bool defaultAlways = false;
+ bool defaultAlways = false;
std::string strTmp;
int singleInfo = 0;
int singleInfo2 = 0;
std::string strLabel;
- int iUrlSet=0;
+ int iUrlSet = 0;
std::string toggleSelect;
float spinWidth = 16;
@@ -832,7 +911,7 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
CRect hitRect;
CPoint camera;
float stereo = 0.f;
- bool hasCamera = false;
+ bool hasCamera = false;
bool resetOnLabelChange = true;
bool bPassword = false;
std::string visibleCondition;
@@ -844,24 +923,25 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
//
if (!pControlNode->Attribute("id", &id))
- XMLUtils::GetInt(pControlNode, "id", id); // backward compatibility - not desired
+ XMLUtils::GetInt(pControlNode, "id", id); // backward compatibility - not desired
//! @todo Perhaps we should check here whether id is valid for focusable controls
//! such as buttons etc. For labels/fadelabels/images it does not matter
GetAlignment(pControlNode, "align", labelInfo.align);
- if (!GetDimensions(pControlNode, "left", "right", "centerleft", "centerright", "width", rect.Width(), posX, width, minWidth))
+ if (!GetDimensions(pControlNode, "left", "right", "centerleft", "centerright", "width",
+ rect.Width(), posX, width, minWidth))
{ // didn't get 2 dimensions, so test for old <posx> as well
if (GetPosition(pControlNode, "posx", rect.Width(), posX))
{ // <posx> available, so use it along with any hacks we used to support
- if (!insideContainer &&
- type == CGUIControl::GUICONTROL_LABEL &&
+ if (!insideContainer && type == CGUIControl::GUICONTROL_LABEL &&
(labelInfo.align & XBFONT_RIGHT))
posX -= width;
}
if (!width) // no width specified, so compute from parent
width = std::max(rect.Width() - posX, 0.0f);
}
- if (!GetDimensions(pControlNode, "top", "bottom", "centertop", "centerbottom", "height", rect.Height(), posY, height, minHeight))
+ if (!GetDimensions(pControlNode, "top", "bottom", "centertop", "centerbottom", "height",
+ rect.Height(), posY, height, minHeight))
{
GetPosition(pControlNode, "posy", rect.Height(), posY);
if (!height)
@@ -876,18 +956,18 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
GetInfoColor(pControlNode, "hitrectcolor", hitColor, parentID);
- GetActions(pControlNode, "onup", actions[ACTION_MOVE_UP]);
- GetActions(pControlNode, "ondown", actions[ACTION_MOVE_DOWN]);
- GetActions(pControlNode, "onleft", actions[ACTION_MOVE_LEFT]);
+ GetActions(pControlNode, "onup", actions[ACTION_MOVE_UP]);
+ GetActions(pControlNode, "ondown", actions[ACTION_MOVE_DOWN]);
+ GetActions(pControlNode, "onleft", actions[ACTION_MOVE_LEFT]);
GetActions(pControlNode, "onright", actions[ACTION_MOVE_RIGHT]);
- GetActions(pControlNode, "onnext", actions[ACTION_NEXT_CONTROL]);
- GetActions(pControlNode, "onprev", actions[ACTION_PREV_CONTROL]);
- GetActions(pControlNode, "onback", actions[ACTION_NAV_BACK]);
- GetActions(pControlNode, "oninfo", actions[ACTION_SHOW_INFO]);
+ GetActions(pControlNode, "onnext", actions[ACTION_NEXT_CONTROL]);
+ GetActions(pControlNode, "onprev", actions[ACTION_PREV_CONTROL]);
+ GetActions(pControlNode, "onback", actions[ACTION_NAV_BACK]);
+ GetActions(pControlNode, "oninfo", actions[ACTION_SHOW_INFO]);
if (XMLUtils::GetInt(pControlNode, "defaultcontrol", defaultControl))
{
- const char *always = pControlNode->FirstChildElement("defaultcontrol")->Attribute("always");
+ const char* always = pControlNode->FirstChildElement("defaultcontrol")->Attribute("always");
if (always && StringUtils::CompareNoCase(always, "true", 4) == 0)
defaultAlways = true;
}
@@ -910,8 +990,9 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
GetInfoColor(pControlNode, "invalidcolor", labelInfo.invalidColor, parentID);
XMLUtils::GetFloat(pControlNode, "textoffsetx", labelInfo.offsetX);
XMLUtils::GetFloat(pControlNode, "textoffsety", labelInfo.offsetY);
- int angle = 0; // use the negative angle to compensate for our vertically flipped cartesian plane
- if (XMLUtils::GetInt(pControlNode, "angle", angle)) labelInfo.angle = (float)-angle;
+ int angle = 0; // use the negative angle to compensate for our vertically flipped cartesian plane
+ if (XMLUtils::GetInt(pControlNode, "angle", angle))
+ labelInfo.angle = (float)-angle;
std::string strFont, strMonoFont;
if (XMLUtils::GetString(pControlNode, "font", strFont))
labelInfo.font = g_fontManager.GetFont(strFont);
@@ -960,15 +1041,17 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
XMLUtils::GetFloat(pControlNode, "sliderwidth", sliderWidth);
XMLUtils::GetFloat(pControlNode, "sliderheight", sliderHeight);
- if (!GetTexture(pControlNode, "textureradioonfocus", textureRadioOnFocus) || !GetTexture(pControlNode, "textureradioonnofocus", textureRadioOnNoFocus))
+ if (!GetTexture(pControlNode, "textureradioonfocus", textureRadioOnFocus) ||
+ !GetTexture(pControlNode, "textureradioonnofocus", textureRadioOnNoFocus))
{
- GetTexture(pControlNode, "textureradiofocus", textureRadioOnFocus); // backward compatibility
+ GetTexture(pControlNode, "textureradiofocus", textureRadioOnFocus); // backward compatibility
GetTexture(pControlNode, "textureradioon", textureRadioOnFocus);
textureRadioOnNoFocus = textureRadioOnFocus;
}
- if (!GetTexture(pControlNode, "textureradioofffocus", textureRadioOffFocus) || !GetTexture(pControlNode, "textureradiooffnofocus", textureRadioOffNoFocus))
+ if (!GetTexture(pControlNode, "textureradioofffocus", textureRadioOffFocus) ||
+ !GetTexture(pControlNode, "textureradiooffnofocus", textureRadioOffNoFocus))
{
- GetTexture(pControlNode, "textureradionofocus", textureRadioOffFocus); // backward compatibility
+ GetTexture(pControlNode, "textureradionofocus", textureRadioOffFocus); // backward compatibility
GetTexture(pControlNode, "textureradiooff", textureRadioOffFocus);
textureRadioOffNoFocus = textureRadioOffFocus;
}
@@ -996,11 +1079,11 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
{
StringUtils::ToLower(strSubType);
- if ( strSubType == "int")
+ if (strSubType == "int")
iType = SPIN_CONTROL_TYPE_INT;
- else if ( strSubType == "page")
+ else if (strSubType == "page")
iType = SPIN_CONTROL_TYPE_PAGE;
- else if ( strSubType == "float")
+ else if (strSubType == "float")
iType = SPIN_CONTROL_TYPE_FLOAT;
else
iType = SPIN_CONTROL_TYPE_TEXT;
@@ -1034,9 +1117,9 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
GetString(pControlNode, "label2", strLabel2);
XMLUtils::GetBoolean(pControlNode, "wrapmultiline", wrapMultiLine);
- XMLUtils::GetInt(pControlNode,"urlset",iUrlSet);
+ XMLUtils::GetInt(pControlNode, "urlset", iUrlSet);
- if ( XMLUtils::GetString(pControlNode, "orientation", strTmp) )
+ if (XMLUtils::GetString(pControlNode, "orientation", strTmp))
{
StringUtils::ToLower(strTmp);
if (strTmp == "horizontal")
@@ -1050,16 +1133,16 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
if (XMLUtils::GetBoolean(pControlNode, "scroll", alwaysScroll))
scrollValue = alwaysScroll ? CGUIControl::ALWAYS : CGUIControl::NEVER;
- XMLUtils::GetBoolean(pControlNode,"pulseonselect", bPulse);
+ XMLUtils::GetBoolean(pControlNode, "pulseonselect", bPulse);
XMLUtils::GetInt(pControlNode, "timeblocks", timeBlocks);
XMLUtils::GetInt(pControlNode, "rulerunit", rulerUnit);
GetTexture(pControlNode, "progresstexture", textureProgressIndicator);
GetInfoTexture(pControlNode, "imagepath", texture, texturePath, parentID);
- XMLUtils::GetUInt(pControlNode,"timeperimage", timePerImage);
- XMLUtils::GetUInt(pControlNode,"fadetime", fadeTime);
- XMLUtils::GetUInt(pControlNode,"pauseatend", timeToPauseAtEnd);
+ XMLUtils::GetUInt(pControlNode, "timeperimage", timePerImage);
+ XMLUtils::GetUInt(pControlNode, "fadetime", fadeTime);
+ XMLUtils::GetUInt(pControlNode, "pauseatend", timeToPauseAtEnd);
XMLUtils::GetBoolean(pControlNode, "randomize", randomized);
XMLUtils::GetBoolean(pControlNode, "loop", loop);
XMLUtils::GetBoolean(pControlNode, "scrollout", scrollOut);
@@ -1107,7 +1190,7 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
viewType = VIEW_TYPE_WRAP;
viewLabel = g_localizeStrings.Get(541);
}
- TiXmlElement *itemElement = pControlNode->FirstChildElement("viewtype");
+ TiXmlElement* itemElement = pControlNode->FirstChildElement("viewtype");
if (itemElement && itemElement->FirstChild())
{
std::string type = itemElement->FirstChild()->Value();
@@ -1131,12 +1214,12 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
viewType = VIEW_TYPE_INFO;
else if (type == "biginfo")
viewType = VIEW_TYPE_BIG_INFO;
- const char *label = itemElement->Attribute("label");
+ const char* label = itemElement->Attribute("label");
if (label)
viewLabel = GUIINFO::CGUIInfoLabel::GetLabel(FilterLabel(label), INFO::DEFAULT_CONTEXT);
}
- TiXmlElement *cam = pControlNode->FirstChildElement("camera");
+ TiXmlElement* cam = pControlNode->FirstChildElement("camera");
if (cam)
{
hasCamera = true;
@@ -1159,10 +1242,10 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
// Instantiate a new control using the properties gathered above
//
- CGUIControl *control = NULL;
+ CGUIControl* control = NULL;
switch (type)
{
- case CGUIControl::GUICONTROL_GROUP:
+ case CGUIControl::GUICONTROL_GROUP:
{
if (insideContainer)
{
@@ -1170,65 +1253,68 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
}
else
{
- control = new CGUIControlGroup(
- parentID, id, posX, posY, width, height);
+ control = new CGUIControlGroup(parentID, id, posX, posY, width, height);
static_cast<CGUIControlGroup*>(control)->SetDefaultControl(defaultControl, defaultAlways);
static_cast<CGUIControlGroup*>(control)->SetRenderFocusedLast(renderFocusedLast);
}
+ break;
}
- break;
- case CGUIControl::GUICONTROL_GROUPLIST:
+ case CGUIControl::GUICONTROL_GROUPLIST:
{
CScroller scroller;
GetScroller(pControlNode, "scrolltime", scroller);
- control = new CGUIControlGroupList(
- parentID, id, posX, posY, width, height, buttonGap, pageControl, orientation, useControlCoords, labelInfo.align, scroller);
+ control =
+ new CGUIControlGroupList(parentID, id, posX, posY, width, height, buttonGap, pageControl,
+ orientation, useControlCoords, labelInfo.align, scroller);
static_cast<CGUIControlGroup*>(control)->SetDefaultControl(defaultControl, defaultAlways);
static_cast<CGUIControlGroup*>(control)->SetRenderFocusedLast(renderFocusedLast);
static_cast<CGUIControlGroupList*>(control)->SetMinSize(minWidth, minHeight);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_LABEL:
+ case CGUIControl::GUICONTROL_LABEL:
{
static const GUIINFO::CGUIInfoLabel empty;
const GUIINFO::CGUIInfoLabel& content = !infoLabels.empty() ? infoLabels[0] : empty;
if (insideContainer)
{ // inside lists we use CGUIListLabel
- control = new CGUIListLabel(parentID, id, posX, posY, width, height, labelInfo, content, scrollValue);
+ control = new CGUIListLabel(parentID, id, posX, posY, width, height, labelInfo, content,
+ scrollValue);
}
else
{
- control = new CGUILabelControl(
- parentID, id, posX, posY, width, height,
- labelInfo, wrapMultiLine, bHasPath);
+ control = new CGUILabelControl(parentID, id, posX, posY, width, height, labelInfo,
+ wrapMultiLine, bHasPath);
static_cast<CGUILabelControl*>(control)->SetInfo(content);
- static_cast<CGUILabelControl*>(control)->SetWidthControl(minWidth, (scrollValue == CGUIControl::ALWAYS));
+ static_cast<CGUILabelControl*>(control)->SetWidthControl(
+ minWidth, (scrollValue == CGUIControl::ALWAYS));
}
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_EDIT:
+ case CGUIControl::GUICONTROL_EDIT:
{
- control = new CGUIEditControl(
- parentID, id, posX, posY, width, height, textureFocus, textureNoFocus,
- labelInfo, strLabel);
+ control = new CGUIEditControl(parentID, id, posX, posY, width, height, textureFocus,
+ textureNoFocus, labelInfo, strLabel);
GUIINFO::CGUIInfoLabel hint_text;
GetInfoLabel(pControlNode, "hinttext", hint_text, parentID);
static_cast<CGUIEditControl*>(control)->SetHint(hint_text);
if (bPassword)
- static_cast<CGUIEditControl*>(control)->SetInputType(CGUIEditControl::INPUT_TYPE_PASSWORD, 0);
+ static_cast<CGUIEditControl*>(control)->SetInputType(CGUIEditControl::INPUT_TYPE_PASSWORD,
+ 0);
static_cast<CGUIEditControl*>(control)->SetTextChangeActions(textChangeActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_VIDEO:
+ case CGUIControl::GUICONTROL_VIDEO:
{
- control = new CGUIVideoControl(
- parentID, id, posX, posY, width, height);
+ control = new CGUIVideoControl(parentID, id, posX, posY, width, height);
+ break;
}
- break;
- case CGUIControl::GUICONTROL_GAME:
+ case CGUIControl::GUICONTROL_GAME:
{
control = new RETRO::CGUIGameControl(parentID, id, posX, posY, width, height);
@@ -1247,37 +1333,38 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
GUIINFO::CGUIInfoLabel pixels;
GetInfoLabel(pControlNode, "pixels", pixels, parentID);
static_cast<RETRO::CGUIGameControl*>(control)->SetPixels(pixels);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_FADELABEL:
+ case CGUIControl::GUICONTROL_FADELABEL:
{
- control = new CGUIFadeLabelControl(
- parentID, id, posX, posY, width, height,
- labelInfo, scrollOut, timeToPauseAtEnd, resetOnLabelChange, randomized);
+ control =
+ new CGUIFadeLabelControl(parentID, id, posX, posY, width, height, labelInfo, scrollOut,
+ timeToPauseAtEnd, resetOnLabelChange, randomized);
static_cast<CGUIFadeLabelControl*>(control)->SetInfo(infoLabels);
// check whether or not a scroll tag was specified.
if (scrollValue != CGUIControl::FOCUS)
- static_cast<CGUIFadeLabelControl*>(control)->SetScrolling(scrollValue == CGUIControl::ALWAYS);
+ static_cast<CGUIFadeLabelControl*>(control)->SetScrolling(scrollValue ==
+ CGUIControl::ALWAYS);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_RSS:
+ case CGUIControl::GUICONTROL_RSS:
{
- control = new CGUIRSSControl(
- parentID, id, posX, posY, width, height,
- labelInfo, textColor3, headlineColor, strRSSTags);
+ control = new CGUIRSSControl(parentID, id, posX, posY, width, height, labelInfo, textColor3,
+ headlineColor, strRSSTags);
RssUrls::const_iterator iter = CRssManager::GetInstance().GetUrls().find(iUrlSet);
if (iter != CRssManager::GetInstance().GetUrls().end())
static_cast<CGUIRSSControl*>(control)->SetUrlSet(iUrlSet);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_BUTTON:
+ case CGUIControl::GUICONTROL_BUTTON:
{
- control = new CGUIButtonControl(
- parentID, id, posX, posY, width, height,
- textureFocus, textureNoFocus,
- labelInfo, wrapMultiLine);
+ control = new CGUIButtonControl(parentID, id, posX, posY, width, height, textureFocus,
+ textureNoFocus, labelInfo, wrapMultiLine);
CGUIButtonControl* bcontrol = static_cast<CGUIButtonControl*>(control);
bcontrol->SetLabel(strLabel);
@@ -1286,15 +1373,14 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
bcontrol->SetClickActions(clickActions);
bcontrol->SetFocusActions(focusActions);
bcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_TOGGLEBUTTON:
+ case CGUIControl::GUICONTROL_TOGGLEBUTTON:
{
- control = new CGUIToggleButtonControl(
- parentID, id, posX, posY, width, height,
- textureFocus, textureNoFocus,
- textureAltFocus, textureAltNoFocus,
- labelInfo, wrapMultiLine);
+ control = new CGUIToggleButtonControl(parentID, id, posX, posY, width, height, textureFocus,
+ textureNoFocus, textureAltFocus, textureAltNoFocus,
+ labelInfo, wrapMultiLine);
CGUIToggleButtonControl* tcontrol = static_cast<CGUIToggleButtonControl*>(control);
tcontrol->SetLabel(strLabel);
@@ -1305,15 +1391,15 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
tcontrol->SetFocusActions(focusActions);
tcontrol->SetUnFocusActions(unfocusActions);
tcontrol->SetToggleSelect(toggleSelect);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_RADIO:
+ case CGUIControl::GUICONTROL_RADIO:
{
control = new CGUIRadioButtonControl(
- parentID, id, posX, posY, width, height,
- textureFocus, textureNoFocus,
- labelInfo,
- textureRadioOnFocus, textureRadioOnNoFocus, textureRadioOffFocus, textureRadioOffNoFocus, textureRadioOnDisabled, textureRadioOffDisabled);
+ parentID, id, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo,
+ textureRadioOnFocus, textureRadioOnNoFocus, textureRadioOffFocus, textureRadioOffNoFocus,
+ textureRadioOnDisabled, textureRadioOffDisabled);
CGUIRadioButtonControl* rcontrol = static_cast<CGUIRadioButtonControl*>(control);
rcontrol->SetLabel(strLabel);
@@ -1323,15 +1409,14 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
rcontrol->SetClickActions(clickActions);
rcontrol->SetFocusActions(focusActions);
rcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_SPIN:
+ case CGUIControl::GUICONTROL_SPIN:
{
- control = new CGUISpinControl(
- parentID, id, posX, posY, width, height,
- textureUp, textureDown, textureUpFocus, textureDownFocus,
- textureUpDisabled, textureDownDisabled,
- labelInfo, iType);
+ control = new CGUISpinControl(parentID, id, posX, posY, width, height, textureUp, textureDown,
+ textureUpFocus, textureDownFocus, textureUpDisabled,
+ textureDownDisabled, labelInfo, iType);
CGUISpinControl* scontrol = static_cast<CGUISpinControl*>(control);
scontrol->SetReverse(bReverse);
@@ -1352,9 +1437,10 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
scontrol->SetFloatRange(fMin, fMax);
scontrol->SetFloatInterval(fInterval);
}
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_SLIDER:
+ case CGUIControl::GUICONTROL_SLIDER:
{
control = new CGUISliderControl(
parentID, id, posX, posY, width, height, textureBar, textureBarDisabled, textureNib,
@@ -1362,9 +1448,10 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
static_cast<CGUISliderControl*>(control)->SetInfo(singleInfo);
static_cast<CGUISliderControl*>(control)->SetAction(action);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_SETTINGS_SLIDER:
+ case CGUIControl::GUICONTROL_SETTINGS_SLIDER:
{
control = new CGUISettingsSliderControl(
parentID, id, posX, posY, width, height, sliderWidth, sliderHeight, textureFocus,
@@ -1373,62 +1460,64 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
static_cast<CGUISettingsSliderControl*>(control)->SetText(strLabel);
static_cast<CGUISettingsSliderControl*>(control)->SetInfo(singleInfo);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_SCROLLBAR:
+ case CGUIControl::GUICONTROL_SCROLLBAR:
{
- control = new GUIScrollBarControl(
- parentID, id, posX, posY, width, height,
- textureBackground, textureBar, textureBarFocus, textureNib, textureNibFocus, orientation, showOnePage);
+ control = new GUIScrollBarControl(parentID, id, posX, posY, width, height, textureBackground,
+ textureBar, textureBarFocus, textureNib, textureNibFocus,
+ orientation, showOnePage);
+ break;
}
- break;
- case CGUIControl::GUICONTROL_PROGRESS:
+ case CGUIControl::GUICONTROL_PROGRESS:
{
- control = new CGUIProgressControl(
- parentID, id, posX, posY, width, height,
- textureBackground, textureLeft, textureMid, textureRight,
- textureOverlay, bReveal);
+ control =
+ new CGUIProgressControl(parentID, id, posX, posY, width, height, textureBackground,
+ textureLeft, textureMid, textureRight, textureOverlay, bReveal);
static_cast<CGUIProgressControl*>(control)->SetInfo(singleInfo, singleInfo2);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_RANGES:
+ case CGUIControl::GUICONTROL_RANGES:
{
- control = new CGUIRangesControl(
- parentID, id, posX, posY, width, height,
- textureBackground, textureLeft, textureMid, textureRight,
- textureOverlay, singleInfo);
+ control =
+ new CGUIRangesControl(parentID, id, posX, posY, width, height, textureBackground,
+ textureLeft, textureMid, textureRight, textureOverlay, singleInfo);
+ break;
}
- break;
- case CGUIControl::GUICONTROL_IMAGE:
+ case CGUIControl::GUICONTROL_IMAGE:
{
// use a bordered texture if we have <bordersize> or <bordertexture> specified.
if (borderTexture.filename.empty() && borderStr.empty())
- control = new CGUIImage(
- parentID, id, posX, posY, width, height, texture);
+ control = new CGUIImage(parentID, id, posX, posY, width, height, texture);
else
- control = new CGUIBorderedImage(
- parentID, id, posX, posY, width, height, texture, borderTexture, borderSize);
+ control = new CGUIBorderedImage(parentID, id, posX, posY, width, height, texture,
+ borderTexture, borderSize);
CGUIImage* icontrol = static_cast<CGUIImage*>(control);
icontrol->SetInfo(textureFile);
icontrol->SetAspectRatio(aspect);
icontrol->SetCrossFade(fadeTime);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_MULTI_IMAGE:
+ case CGUIControl::GUICONTROL_MULTI_IMAGE:
{
- control = new CGUIMultiImage(
- parentID, id, posX, posY, width, height, texture, timePerImage, fadeTime, randomized, loop, timeToPauseAtEnd);
+ control = new CGUIMultiImage(parentID, id, posX, posY, width, height, texture, timePerImage,
+ fadeTime, randomized, loop, timeToPauseAtEnd);
static_cast<CGUIMultiImage*>(control)->SetInfo(texturePath);
static_cast<CGUIMultiImage*>(control)->SetAspectRatio(aspect);
+
+ break;
}
- break;
- case CGUIControl::GUICONTAINER_LIST:
+ case CGUIControl::GUICONTAINER_LIST:
{
CScroller scroller;
GetScroller(pControlNode, "scrolltime", scroller);
- control = new CGUIListContainer(parentID, id, posX, posY, width, height, orientation, scroller, preloadItems);
+ control = new CGUIListContainer(parentID, id, posX, posY, width, height, orientation,
+ scroller, preloadItems);
CGUIListContainer* lcontrol = static_cast<CGUIListContainer*>(control);
lcontrol->LoadLayout(pControlNode);
lcontrol->LoadListProvider(pControlNode, defaultControl, defaultAlways);
@@ -1439,14 +1528,16 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
lcontrol->SetClickActions(clickActions);
lcontrol->SetFocusActions(focusActions);
lcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTAINER_WRAPLIST:
+ case CGUIControl::GUICONTAINER_WRAPLIST:
{
CScroller scroller;
GetScroller(pControlNode, "scrolltime", scroller);
- control = new CGUIWrappingListContainer(parentID, id, posX, posY, width, height, orientation, scroller, preloadItems, focusPosition);
+ control = new CGUIWrappingListContainer(parentID, id, posX, posY, width, height, orientation,
+ scroller, preloadItems, focusPosition);
CGUIWrappingListContainer* wcontrol = static_cast<CGUIWrappingListContainer*>(control);
wcontrol->LoadLayout(pControlNode);
wcontrol->LoadListProvider(pControlNode, defaultControl, defaultAlways);
@@ -1457,24 +1548,29 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
wcontrol->SetClickActions(clickActions);
wcontrol->SetFocusActions(focusActions);
wcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTAINER_EPGGRID:
+ case CGUIControl::GUICONTAINER_EPGGRID:
{
- CGUIEPGGridContainer *epgGridContainer = new CGUIEPGGridContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, timeBlocks, rulerUnit, textureProgressIndicator);
+ CGUIEPGGridContainer* epgGridContainer =
+ new CGUIEPGGridContainer(parentID, id, posX, posY, width, height, orientation, scrollTime,
+ preloadItems, timeBlocks, rulerUnit, textureProgressIndicator);
control = epgGridContainer;
epgGridContainer->LoadLayout(pControlNode);
epgGridContainer->SetRenderOffset(offset);
epgGridContainer->SetType(viewType, viewLabel);
epgGridContainer->SetPageControl(pageControl);
+
+ break;
}
- break;
- case CGUIControl::GUICONTAINER_FIXEDLIST:
+ case CGUIControl::GUICONTAINER_FIXEDLIST:
{
CScroller scroller;
GetScroller(pControlNode, "scrolltime", scroller);
- control = new CGUIFixedListContainer(parentID, id, posX, posY, width, height, orientation, scroller, preloadItems, focusPosition, iMovementRange);
+ control = new CGUIFixedListContainer(parentID, id, posX, posY, width, height, orientation,
+ scroller, preloadItems, focusPosition, iMovementRange);
CGUIFixedListContainer* fcontrol = static_cast<CGUIFixedListContainer*>(control);
fcontrol->LoadLayout(pControlNode);
fcontrol->LoadListProvider(pControlNode, defaultControl, defaultAlways);
@@ -1485,14 +1581,16 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
fcontrol->SetClickActions(clickActions);
fcontrol->SetFocusActions(focusActions);
fcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTAINER_PANEL:
+ case CGUIControl::GUICONTAINER_PANEL:
{
CScroller scroller;
GetScroller(pControlNode, "scrolltime", scroller);
- control = new CGUIPanelContainer(parentID, id, posX, posY, width, height, orientation, scroller, preloadItems);
+ control = new CGUIPanelContainer(parentID, id, posX, posY, width, height, orientation,
+ scroller, preloadItems);
CGUIPanelContainer* pcontrol = static_cast<CGUIPanelContainer*>(control);
pcontrol->LoadLayout(pControlNode);
pcontrol->LoadListProvider(pControlNode, defaultControl, defaultAlways);
@@ -1503,18 +1601,18 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
pcontrol->SetClickActions(clickActions);
pcontrol->SetFocusActions(focusActions);
pcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_TEXTBOX:
+ case CGUIControl::GUICONTROL_TEXTBOX:
{
if (!strMonoFont.empty())
{
labelInfoMono = labelInfo;
labelInfoMono.font = g_fontManager.GetFont(strMonoFont);
}
- control = new CGUITextBox(
- parentID, id, posX, posY, width, height,
- labelInfo, scrollTime, strMonoFont.empty() ? nullptr : &labelInfoMono);
+ control = new CGUITextBox(parentID, id, posX, posY, width, height, labelInfo, scrollTime,
+ strMonoFont.empty() ? nullptr : &labelInfoMono);
CGUITextBox* tcontrol = static_cast<CGUITextBox*>(control);
@@ -1523,117 +1621,124 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
tcontrol->SetInfo(infoLabels[0]);
tcontrol->SetAutoScrolling(pControlNode);
tcontrol->SetMinHeight(minHeight);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_MOVER:
+ case CGUIControl::GUICONTROL_MOVER:
{
control = new CGUIMoverControl(parentID, id, posX, posY, width, height, textureFocus,
textureNoFocus, movingSpeedCfg);
+ break;
}
- break;
- case CGUIControl::GUICONTROL_RESIZE:
+ case CGUIControl::GUICONTROL_RESIZE:
{
control = new CGUIResizeControl(parentID, id, posX, posY, width, height, textureFocus,
textureNoFocus, movingSpeedCfg);
+ break;
}
- break;
- case CGUIControl::GUICONTROL_SPINEX:
+ case CGUIControl::GUICONTROL_SPINEX:
{
- control = new CGUISpinControlEx(
- parentID, id, posX, posY, width, height, spinWidth, spinHeight,
- labelInfo, textureFocus, textureNoFocus, textureUp, textureDown, textureUpFocus, textureDownFocus,
- textureUpDisabled, textureDownDisabled, labelInfo, iType);
+ control = new CGUISpinControlEx(parentID, id, posX, posY, width, height, spinWidth,
+ spinHeight, labelInfo, textureFocus, textureNoFocus,
+ textureUp, textureDown, textureUpFocus, textureDownFocus,
+ textureUpDisabled, textureDownDisabled, labelInfo, iType);
CGUISpinControlEx* scontrol = static_cast<CGUISpinControlEx*>(control);
scontrol->SetSpinPosition(spinPosX);
scontrol->SetText(strLabel);
scontrol->SetReverse(bReverse);
+
+ break;
}
- break;
- case CGUIControl::GUICONTROL_VISUALISATION:
- control = new CGUIVisualisationControl(parentID, id, posX, posY, width, height);
- break;
- case CGUIControl::GUICONTROL_RENDERADDON:
- control = new CGUIRenderingControl(parentID, id, posX, posY, width, height);
- break;
- case CGUIControl::GUICONTROL_GAMECONTROLLER:
- {
- control = new GAME::CGUIGameController(parentID, id, posX, posY, width, height, texture);
+ case CGUIControl::GUICONTROL_VISUALISATION:
+ {
+ control = new CGUIVisualisationControl(parentID, id, posX, posY, width, height);
+ break;
+ }
+ case CGUIControl::GUICONTROL_RENDERADDON:
+ {
+ control = new CGUIRenderingControl(parentID, id, posX, posY, width, height);
+ break;
+ }
+ case CGUIControl::GUICONTROL_GAMECONTROLLER:
+ {
+ control = new GAME::CGUIGameController(parentID, id, posX, posY, width, height, texture);
- GAME::CGUIGameController* gcontrol = static_cast<GAME::CGUIGameController*>(control);
+ GAME::CGUIGameController* gcontrol = static_cast<GAME::CGUIGameController*>(control);
- // Set texture
- gcontrol->SetInfo(textureFile);
+ // Set texture
+ gcontrol->SetInfo(textureFile);
- // Set aspect ratio
- gcontrol->SetAspectRatio(aspect);
+ // Set aspect ratio
+ gcontrol->SetAspectRatio(aspect);
- // Set controller ID
- GUIINFO::CGUIInfoLabel controllerId;
- GetInfoLabel(pControlNode, "controllerid", controllerId, parentID);
- gcontrol->SetControllerID(controllerId);
+ // Set controller ID
+ GUIINFO::CGUIInfoLabel controllerId;
+ GetInfoLabel(pControlNode, "controllerid", controllerId, parentID);
+ gcontrol->SetControllerID(controllerId);
- // Set controller address
- GUIINFO::CGUIInfoLabel controllerAddress;
- GetInfoLabel(pControlNode, "controlleraddress", controllerAddress, parentID);
- gcontrol->SetControllerAddress(controllerAddress);
+ // Set controller address
+ GUIINFO::CGUIInfoLabel controllerAddress;
+ GetInfoLabel(pControlNode, "controlleraddress", controllerAddress, parentID);
+ gcontrol->SetControllerAddress(controllerAddress);
- // Set controller diffuse color
- GUIINFO::CGUIInfoColor controllerDiffuse(0xFFFFFFFF);
- GetInfoColor(pControlNode, "controllerdiffuse", controllerDiffuse, parentID);
- gcontrol->SetControllerDiffuse(controllerDiffuse);
+ // Set controller diffuse color
+ GUIINFO::CGUIInfoColor controllerDiffuse(0xFFFFFFFF);
+ GetInfoColor(pControlNode, "controllerdiffuse", controllerDiffuse, parentID);
+ gcontrol->SetControllerDiffuse(controllerDiffuse);
- // Set port address
- GUIINFO::CGUIInfoLabel portAddress;
- GetInfoLabel(pControlNode, "portaddress", portAddress, parentID);
- gcontrol->SetPortAddress(portAddress);
+ // Set port address
+ GUIINFO::CGUIInfoLabel portAddress;
+ GetInfoLabel(pControlNode, "portaddress", portAddress, parentID);
+ gcontrol->SetPortAddress(portAddress);
- // Set peripheral location
- GUIINFO::CGUIInfoLabel peripheralLocation;
- GetInfoLabel(pControlNode, "peripherallocation", peripheralLocation, parentID);
- gcontrol->SetPeripheralLocation(peripheralLocation);
+ // Set peripheral location
+ GUIINFO::CGUIInfoLabel peripheralLocation;
+ GetInfoLabel(pControlNode, "peripherallocation", peripheralLocation, parentID);
+ gcontrol->SetPeripheralLocation(peripheralLocation);
- break;
- }
- case CGUIControl::GUICONTROL_GAMECONTROLLERLIST:
- {
- CScroller scroller;
- GetScroller(pControlNode, "scrolltime", scroller);
+ break;
+ }
+ case CGUIControl::GUICONTROL_GAMECONTROLLERLIST:
+ {
+ CScroller scroller;
+ GetScroller(pControlNode, "scrolltime", scroller);
- control = new GAME::CGUIGameControllerList(parentID, id, posX, posY, width, height, orientation,
- labelInfo.align, scroller);
+ control = new GAME::CGUIGameControllerList(parentID, id, posX, posY, width, height,
+ orientation, labelInfo.align, scroller);
- GAME::CGUIGameControllerList* lcontrol = static_cast<GAME::CGUIGameControllerList*>(control);
+ GAME::CGUIGameControllerList* lcontrol = static_cast<GAME::CGUIGameControllerList*>(control);
- lcontrol->LoadLayout(pControlNode);
- lcontrol->LoadListProvider(pControlNode, defaultControl, defaultAlways);
- lcontrol->SetType(viewType, viewLabel);
- lcontrol->SetPageControl(pageControl);
- lcontrol->SetRenderOffset(offset);
- lcontrol->SetAutoScrolling(pControlNode);
- lcontrol->SetClickActions(clickActions);
- lcontrol->SetFocusActions(focusActions);
- lcontrol->SetUnFocusActions(unfocusActions);
+ lcontrol->LoadLayout(pControlNode);
+ lcontrol->LoadListProvider(pControlNode, defaultControl, defaultAlways);
+ lcontrol->SetType(viewType, viewLabel);
+ lcontrol->SetPageControl(pageControl);
+ lcontrol->SetRenderOffset(offset);
+ lcontrol->SetAutoScrolling(pControlNode);
+ lcontrol->SetClickActions(clickActions);
+ lcontrol->SetFocusActions(focusActions);
+ lcontrol->SetUnFocusActions(unfocusActions);
- break;
- }
- case CGUIControl::GUICONTROL_COLORBUTTON:
- {
- control = new CGUIColorButtonControl(parentID, id, posX, posY, width, height, textureFocus,
- textureNoFocus, labelInfo, textureColorMask,
- textureColorDisabledMask);
-
- CGUIColorButtonControl* rcontrol = static_cast<CGUIColorButtonControl*>(control);
- rcontrol->SetLabel(strLabel);
- rcontrol->SetImageBoxColor(colorBox);
- rcontrol->SetColorDimensions(colorPosX, colorPosY, colorWidth, colorHeight);
- rcontrol->SetClickActions(clickActions);
- rcontrol->SetFocusActions(focusActions);
- rcontrol->SetUnFocusActions(unfocusActions);
- }
- break;
- default:
- break;
+ break;
+ }
+ case CGUIControl::GUICONTROL_COLORBUTTON:
+ {
+ control = new CGUIColorButtonControl(parentID, id, posX, posY, width, height, textureFocus,
+ textureNoFocus, labelInfo, textureColorMask,
+ textureColorDisabledMask);
+
+ CGUIColorButtonControl* rcontrol = static_cast<CGUIColorButtonControl*>(control);
+ rcontrol->SetLabel(strLabel);
+ rcontrol->SetImageBoxColor(colorBox);
+ rcontrol->SetColorDimensions(colorPosX, colorPosY, colorWidth, colorHeight);
+ rcontrol->SetClickActions(clickActions);
+ rcontrol->SetFocusActions(focusActions);
+ rcontrol->SetUnFocusActions(unfocusActions);
+
+ break;
+ }
+ default:
+ break;
}
// things that apply to all controls
diff --git a/xbmc/guilib/GUIControlFactory.h b/xbmc/guilib/GUIControlFactory.h
index 9cdf56c07a..22b571c70e 100644
--- a/xbmc/guilib/GUIControlFactory.h
+++ b/xbmc/guilib/GUIControlFactory.h
@@ -31,10 +31,10 @@ namespace GUILIB
{
namespace GUIINFO
{
- class CGUIInfoLabel;
-}
-}
+class CGUIInfoLabel;
}
+} // namespace GUILIB
+} // namespace KODI
/*!
\ingroup controls
@@ -45,13 +45,16 @@ class CGUIControlFactory
public:
CGUIControlFactory(void);
virtual ~CGUIControlFactory(void);
- CGUIControl* Create(int parentID, const CRect &rect, TiXmlElement* pControlNode, bool insideContainer = false);
+ CGUIControl* Create(int parentID,
+ const CRect& rect,
+ TiXmlElement* pControlNode,
+ bool insideContainer = false);
/*! \brief translate from control name to control type
\param type name of the control
\return type of control
*/
- static CGUIControl::GUICONTROLTYPES TranslateControlType(const std::string &type);
+ static CGUIControl::GUICONTROLTYPES TranslateControlType(const std::string& type);
/*! \brief translate from control type to control name
\param type type of the control
@@ -59,12 +62,21 @@ public:
*/
static std::string TranslateControlType(CGUIControl::GUICONTROLTYPES type);
- static bool GetAspectRatio(const TiXmlNode* pRootNode, const char* strTag, CAspectRatio &aspectRatio);
- static bool GetInfoTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image, KODI::GUILIB::GUIINFO::CGUIInfoLabel &info, int parentID);
- static bool GetTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image);
+ static bool GetAspectRatio(const TiXmlNode* pRootNode,
+ const char* strTag,
+ CAspectRatio& aspectRatio);
+ static bool GetInfoTexture(const TiXmlNode* pRootNode,
+ const char* strTag,
+ CTextureInfo& image,
+ KODI::GUILIB::GUIINFO::CGUIInfoLabel& info,
+ int parentID);
+ static bool GetTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo& image);
static bool GetAlignment(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwAlignment);
static bool GetAlignmentY(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwAlignment);
- static bool GetAnimations(TiXmlNode *control, const CRect &rect, int context, std::vector<CAnimation> &animation);
+ static bool GetAnimations(TiXmlNode* control,
+ const CRect& rect,
+ int context,
+ std::vector<CAnimation>& animation);
/*! \brief Create an info label from an XML element
Processes XML elements of the form
@@ -78,7 +90,7 @@ public:
\return true if a valid info label was read, false otherwise
*/
- /*! \brief Parse a position string
+ /*! \brief Parse a position string
Handles strings of the form
### number of pixels
###r number of pixels measured from the right
@@ -89,26 +101,50 @@ public:
*/
static float ParsePosition(const char* pos, const float parentSize);
- static bool GetInfoLabelFromElement(const TiXmlElement *element, KODI::GUILIB::GUIINFO::CGUIInfoLabel &infoLabel, int parentID);
- static void GetInfoLabel(const TiXmlNode *pControlNode, const std::string &labelTag, KODI::GUILIB::GUIINFO::CGUIInfoLabel &infoLabel, int parentID);
- static void GetInfoLabels(const TiXmlNode *pControlNode, const std::string &labelTag, std::vector<KODI::GUILIB::GUIINFO::CGUIInfoLabel> &infoLabels, int parentID);
+ static bool GetInfoLabelFromElement(const TiXmlElement* element,
+ KODI::GUILIB::GUIINFO::CGUIInfoLabel& infoLabel,
+ int parentID);
+ static void GetInfoLabel(const TiXmlNode* pControlNode,
+ const std::string& labelTag,
+ KODI::GUILIB::GUIINFO::CGUIInfoLabel& infoLabel,
+ int parentID);
+ static void GetInfoLabels(const TiXmlNode* pControlNode,
+ const std::string& labelTag,
+ std::vector<KODI::GUILIB::GUIINFO::CGUIInfoLabel>& infoLabels,
+ int parentID);
static bool GetColor(const TiXmlNode* pRootNode, const char* strTag, UTILS::COLOR::Color& value);
- static bool GetInfoColor(const TiXmlNode* pRootNode, const char* strTag, KODI::GUILIB::GUIINFO::CGUIInfoColor &value, int parentID);
- static std::string FilterLabel(const std::string &label);
- static bool GetConditionalVisibility(const TiXmlNode* control, std::string &condition);
+ static bool GetInfoColor(const TiXmlNode* pRootNode,
+ const char* strTag,
+ KODI::GUILIB::GUIINFO::CGUIInfoColor& value,
+ int parentID);
+ static std::string FilterLabel(const std::string& label);
+ static bool GetConditionalVisibility(const TiXmlNode* control, std::string& condition);
static bool GetActions(const TiXmlNode* pRootNode, const char* strTag, CGUIAction& actions);
- static void GetRectFromString(const std::string &string, CRect &rect);
- static bool GetHitRect(const TiXmlNode* pRootNode, CRect &rect, const CRect &parentRect);
- static bool GetScroller(const TiXmlNode *pControlNode, const std::string &scrollerTag, CScroller& scroller);
+ static void GetRectFromString(const std::string& string, CRect& rect);
+ static bool GetHitRect(const TiXmlNode* pRootNode, CRect& rect, const CRect& parentRect);
+ static bool GetScroller(const TiXmlNode* pControlNode,
+ const std::string& scrollerTag,
+ CScroller& scroller);
+
private:
- static std::string GetType(const TiXmlElement *pControlNode);
+ static std::string GetType(const TiXmlElement* pControlNode);
static bool GetMovingSpeedConfig(const TiXmlNode* pRootNode,
const char* strTag,
UTILS::MOVING_SPEED::MapEventConfig& movingSpeedCfg);
- static bool GetConditionalVisibility(const TiXmlNode* control, std::string &condition, std::string &allowHiddenFocus);
+ static bool GetConditionalVisibility(const TiXmlNode* control,
+ std::string& condition,
+ std::string& allowHiddenFocus);
bool GetString(const TiXmlNode* pRootNode, const char* strTag, std::string& strString);
- static bool GetFloatRange(const TiXmlNode* pRootNode, const char* strTag, float& iMinValue, float& iMaxValue, float& iIntervalValue);
- static bool GetIntRange(const TiXmlNode* pRootNode, const char* strTag, int& iMinValue, int& iMaxValue, int& iIntervalValue);
+ static bool GetFloatRange(const TiXmlNode* pRootNode,
+ const char* strTag,
+ float& iMinValue,
+ float& iMaxValue,
+ float& iIntervalValue);
+ static bool GetIntRange(const TiXmlNode* pRootNode,
+ const char* strTag,
+ int& iMinValue,
+ int& iMaxValue,
+ int& iIntervalValue);
/*! \brief Get the value of a position tag from XML
Handles both absolute and relative values.
@@ -118,7 +154,10 @@ private:
\param value [out] the returned value.
\sa ParsePosition, GetDimension, GetDimensions.
*/
- static bool GetPosition(const TiXmlNode *node, const char* tag, const float parentSize, float& value);
+ static bool GetPosition(const TiXmlNode* node,
+ const char* tag,
+ const float parentSize,
+ float& value);
/*! \brief grab a dimension out of the XML
@@ -134,7 +173,8 @@ private:
\return true if we found and read the tag.
\sa GetPosition, GetDimensions, ParsePosition.
*/
- static bool GetDimension(const TiXmlNode *node, const char* strTag, const float parentSize, float &value, float &min);
+ static bool GetDimension(
+ const TiXmlNode* node, const char* strTag, const float parentSize, float& value, float& min);
/*! \brief Retrieve the dimensions for a control.
@@ -153,8 +193,14 @@ private:
\return true if we can successfully derive the position and size, false otherwise.
\sa GetDimension, GetPosition, ParsePosition.
*/
- static bool GetDimensions(const TiXmlNode *node, const char *leftTag, const char *rightTag, const char *centerLeftTag,
- const char *centerRightTag, const char *widthTag, const float parentSize, float &left,
- float &width, float &min_width);
+ static bool GetDimensions(const TiXmlNode* node,
+ const char* leftTag,
+ const char* rightTag,
+ const char* centerLeftTag,
+ const char* centerRightTag,
+ const char* widthTag,
+ const float parentSize,
+ float& left,
+ float& width,
+ float& min_width);
};
-
diff --git a/xbmc/guilib/GUIDialog.cpp b/xbmc/guilib/GUIDialog.cpp
index a3d0e02fca..70c35148f7 100644
--- a/xbmc/guilib/GUIDialog.cpp
+++ b/xbmc/guilib/GUIDialog.cpp
@@ -114,7 +114,7 @@ void CGUIDialog::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregi
// if we were running but now we're not, mark us dirty
if (!m_active && m_wasRunning)
- dirtyregions.push_back(CDirtyRegion(m_renderRegion));
+ dirtyregions.emplace_back(m_renderRegion);
if (m_active)
CGUIWindow::DoProcess(currentTime, dirtyregions);
diff --git a/xbmc/guilib/GUIEditControl.cpp b/xbmc/guilib/GUIEditControl.cpp
index 27cdb9b623..7d5a643021 100644
--- a/xbmc/guilib/GUIEditControl.cpp
+++ b/xbmc/guilib/GUIEditControl.cpp
@@ -222,6 +222,18 @@ bool CGUIEditControl::OnAction(const CAction &action)
return CGUIButtonControl::OnAction(action);
}
}
+ else if (action.GetID() == ACTION_KEYBOARD_COMPOSING_KEY)
+ {
+ ComposingCursorAppendChar(action.GetUnicode());
+ }
+ else if (action.GetID() == ACTION_KEYBOARD_COMPOSING_KEY_CANCELLED)
+ {
+ CancelKeyComposition(action.GetUnicode());
+ }
+ else if (action.GetID() == ACTION_KEYBOARD_COMPOSING_KEY_FINISHED)
+ {
+ ResetCursor();
+ }
else if (action.GetID() == KEY_UNICODE)
{
// input from the keyboard
@@ -606,11 +618,18 @@ bool CGUIEditControl::SetStyledText(const std::wstring &text)
}
// show the cursor
- uint32_t ch = L'|' | style;
- if ((++m_cursorBlink % 64) > 32)
- ch |= (3 << 16);
- styled.insert(styled.begin() + m_cursorPos, ch);
-
+ unsigned int posChar = m_cursorPos;
+ for (const uint32_t& cursorChar : m_cursorChars)
+ {
+ uint32_t ch = cursorChar | style;
+ if (m_cursorBlinkEnabled)
+ {
+ if ((++m_cursorBlink % 64) > 32)
+ ch |= (3 << 16);
+ }
+ styled.insert(styled.begin() + posChar, ch);
+ posChar++;
+ }
return m_label2.SetStyledText(styled, colors);
}
@@ -779,3 +798,62 @@ std::string CGUIEditControl::GetDescriptionByIndex(int index) const
return "";
}
+
+void CGUIEditControl::ComposingCursorAppendChar(std::uint32_t deadUnicodeKey)
+{
+ std::uint32_t ch;
+ if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5 ||
+ m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW)
+ {
+ ch = '*';
+ }
+ else
+ {
+ ch = deadUnicodeKey;
+ }
+
+ if (IsComposingKey())
+ {
+ m_cursorChars.emplace_back(ch);
+ m_cursorCharsBuffer.emplace_back(deadUnicodeKey);
+ }
+ else
+ {
+ m_cursorChars = {ch};
+ m_cursorCharsBuffer.emplace_back(deadUnicodeKey);
+ }
+ m_cursorBlinkEnabled = false;
+}
+
+void CGUIEditControl::CancelKeyComposition(std::uint32_t deadUnicodeKey)
+{
+ // sequence cancelled and reverted...
+ if (deadUnicodeKey == XBMCK_BACKSPACE)
+ {
+ ResetCursor();
+ }
+ // sequence cancelled and replay...
+ else
+ {
+ ClearMD5();
+ m_edit.clear();
+ for (const uint32_t& cursorChar : m_cursorCharsBuffer)
+ {
+ m_text2.insert(m_text2.begin() + m_cursorPos++, cursorChar);
+ }
+ UpdateText();
+ ResetCursor();
+ }
+}
+
+void CGUIEditControl::ResetCursor()
+{
+ m_cursorChars = {'|'};
+ m_cursorCharsBuffer.clear();
+ m_cursorBlinkEnabled = true;
+}
+
+bool CGUIEditControl::IsComposingKey() const
+{
+ return !m_cursorBlinkEnabled;
+}
diff --git a/xbmc/guilib/GUIEditControl.h b/xbmc/guilib/GUIEditControl.h
index 0c23890ec1..718a0798ea 100644
--- a/xbmc/guilib/GUIEditControl.h
+++ b/xbmc/guilib/GUIEditControl.h
@@ -102,6 +102,22 @@ protected:
*/
bool ClearMD5();
+ /*! \brief Append a given char to the composing cursor
+ * \param deadUnicodeKey - the unicode key that started the composing sequence
+ */
+ void ComposingCursorAppendChar(std::uint32_t deadUnicodeKey);
+ /*! \brief Reset the cursor aspect to normal input (i.e. not composing a key)
+ */
+ void ResetCursor();
+ /*! \brief Cancel the key composition
+ * \param deadUnicodeKey - the unicode key that ended/cancelled the composing sequence
+ */
+ void CancelKeyComposition(std::uint32_t deadUnicodeKey);
+ /*! \brief Check if the control is composing a key
+ * \return true if a key is being composed, false otherwise
+ */
+ bool IsComposingKey() const;
+
std::wstring m_text2;
std::string m_text;
KODI::GUILIB::GUIINFO::CGUIInfoLabel m_hintInfo;
@@ -109,7 +125,12 @@ protected:
CRect m_clipRect; ///< clipping rect for the second label
unsigned int m_cursorPos;
+ bool m_cursorBlinkEnabled{true};
unsigned int m_cursorBlink;
+ // visible cursor chars
+ std::vector<std::uint32_t> m_cursorChars{'|'};
+ // cursor char buffer
+ std::vector<std::uint32_t> m_cursorCharsBuffer{};
std::string m_inputHeading;
INPUT_TYPE m_inputType;
diff --git a/xbmc/guilib/GUIRangesControl.cpp b/xbmc/guilib/GUIRangesControl.cpp
index 6abfbd1f77..8c8c2f0dc5 100644
--- a/xbmc/guilib/GUIRangesControl.cpp
+++ b/xbmc/guilib/GUIRangesControl.cpp
@@ -260,8 +260,8 @@ void CGUIRangesControl::SetRanges(const std::vector<std::pair<float, float>>& ra
{
ClearRanges();
for (const auto& range : ranges)
- m_ranges.emplace_back(CGUIRange(m_posX, m_posY, m_width, m_height,
- m_guiLowerTextureInfo, m_guiFillTextureInfo, m_guiUpperTextureInfo, range));
+ m_ranges.emplace_back(m_posX, m_posY, m_width, m_height, m_guiLowerTextureInfo,
+ m_guiFillTextureInfo, m_guiUpperTextureInfo, range);
for (auto& range : m_ranges)
range.AllocResources(); // note: we need to alloc the instance actually inserted into the vector; hence the second loop.
@@ -399,7 +399,7 @@ void CGUIRangesControl::UpdateInfo(const CGUIListItem* item /* = nullptr */)
++it;
if (first <= second)
- ranges.emplace_back(std::make_pair(first, second));
+ ranges.emplace_back(first, second);
else
CLog::Log(LOGERROR, "CGUIRangesControl::UpdateInfo - malformed ranges csv string (end element must be larger or equal than start element)");
}
diff --git a/xbmc/guilib/GUIStaticItem.cpp b/xbmc/guilib/GUIStaticItem.cpp
index 97cd7545d7..2ff69a5362 100644
--- a/xbmc/guilib/GUIStaticItem.cpp
+++ b/xbmc/guilib/GUIStaticItem.cpp
@@ -37,10 +37,14 @@ CGUIStaticItem::CGUIStaticItem(const TiXmlElement *item, int parentID) : CFileIt
SetLabel2(label2.GetLabel(parentID));
SetArt("thumb", thumb.GetLabel(parentID, true));
SetArt("icon", icon.GetLabel(parentID, true));
- if (!label.IsConstant()) m_info.push_back(std::make_pair(label, "label"));
- if (!label2.IsConstant()) m_info.push_back(std::make_pair(label2, "label2"));
- if (!thumb.IsConstant()) m_info.push_back(std::make_pair(thumb, "thumb"));
- if (!icon.IsConstant()) m_info.push_back(std::make_pair(icon, "icon"));
+ if (!label.IsConstant())
+ m_info.emplace_back(label, "label");
+ if (!label2.IsConstant())
+ m_info.emplace_back(label2, "label2");
+ if (!thumb.IsConstant())
+ m_info.emplace_back(thumb, "thumb");
+ if (!icon.IsConstant())
+ m_info.emplace_back(icon, "icon");
m_iprogramCount = id ? atoi(id) : 0;
// add any properties
const TiXmlElement *property = item->FirstChildElement("property");
@@ -52,7 +56,7 @@ CGUIStaticItem::CGUIStaticItem(const TiXmlElement *item, int parentID) : CFileIt
{
SetProperty(name, prop.GetLabel(parentID, true).c_str());
if (!prop.IsConstant())
- m_info.push_back(std::make_pair(prop, name));
+ m_info.emplace_back(prop, name);
}
property = property->NextSiblingElement("property");
}
diff --git a/xbmc/guilib/GUIVisualisationControl.cpp b/xbmc/guilib/GUIVisualisationControl.cpp
index 0399edde0c..fb02bc9f18 100644
--- a/xbmc/guilib/GUIVisualisationControl.cpp
+++ b/xbmc/guilib/GUIVisualisationControl.cpp
@@ -31,6 +31,8 @@
#include "utils/URIUtils.h"
#include "utils/log.h"
+#include <memory>
+
namespace
{
constexpr unsigned int MAX_AUDIO_BUFFERS = 16;
@@ -386,7 +388,7 @@ bool CGUIVisualisationControl::InitVisualization()
if (y + h > context.GetHeight())
h = context.GetHeight() - y;
- m_instance.reset(new KODI::ADDONS::CVisualization(addonBase, x, y, w, h));
+ m_instance = std::make_unique<KODI::ADDONS::CVisualization>(addonBase, x, y, w, h);
CreateBuffers();
m_alreadyStarted = false;
diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp
index a8da102614..3104ce1be8 100644
--- a/xbmc/guilib/GUIWindowManager.cpp
+++ b/xbmc/guilib/GUIWindowManager.cpp
@@ -22,7 +22,6 @@
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
#include "events/windows/GUIWindowEventLog.h"
-#include "favourites/GUIDialogFavourites.h"
#include "favourites/GUIWindowFavourites.h"
#include "input/actions/Action.h"
#include "input/actions/ActionIDs.h"
@@ -50,6 +49,7 @@
#include "utils/log.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
#include "video/dialogs/GUIDialogVideoOSD.h"
+#include "video/dialogs/GUIDialogVideoVersion.h"
#include "video/windows/GUIWindowFullScreen.h"
#include "video/windows/GUIWindowVideoNav.h"
#include "video/windows/GUIWindowVideoPlaylist.h"
@@ -229,7 +229,6 @@ void CGUIWindowManager::CreateWindows()
Add(new CGUIDialogNetworkSetup);
Add(new CGUIDialogMediaSource);
Add(new CGUIDialogProfileSettings);
- Add(new CGUIDialogFavourites);
Add(new CGUIDialogSongInfo);
Add(new CGUIDialogSmartPlaylistEditor);
Add(new CGUIDialogSmartPlaylistRule);
@@ -293,6 +292,8 @@ void CGUIWindowManager::CreateWindows()
Add(new CGUIDialogMusicInfo);
Add(new CGUIDialogOK);
Add(new CGUIDialogVideoInfo);
+ Add(new CGUIDialogVideoVersion(WINDOW_DIALOG_VIDEO_VERSION));
+ Add(new CGUIDialogVideoVersion(WINDOW_DIALOG_VIDEO_VERSION_SELECT));
Add(new CGUIDialogTextViewer);
Add(new CGUIWindowFullScreen);
Add(new CGUIWindowVisualisation);
@@ -333,6 +334,8 @@ bool CGUIWindowManager::DestroyWindows()
DestroyWindow(WINDOW_MUSIC_NAV);
DestroyWindow(WINDOW_DIALOG_MUSIC_INFO);
DestroyWindow(WINDOW_DIALOG_VIDEO_INFO);
+ DestroyWindow(WINDOW_DIALOG_VIDEO_VERSION);
+ DestroyWindow(WINDOW_DIALOG_VIDEO_VERSION_SELECT);
DestroyWindow(WINDOW_VIDEO_PLAYLIST);
DestroyWindow(WINDOW_VIDEO_NAV);
DestroyWindow(WINDOW_FILES);
@@ -364,7 +367,6 @@ bool CGUIWindowManager::DestroyWindows()
DestroyWindow(WINDOW_DIALOG_CONTENT_SETTINGS);
DestroyWindow(WINDOW_DIALOG_INFOPROVIDER_SETTINGS);
DestroyWindow(WINDOW_DIALOG_LIBEXPORT_SETTINGS);
- DestroyWindow(WINDOW_DIALOG_FAVOURITES);
DestroyWindow(WINDOW_DIALOG_SONG_INFO);
DestroyWindow(WINDOW_DIALOG_SMART_PLAYLIST_EDITOR);
DestroyWindow(WINDOW_DIALOG_SMART_PLAYLIST_RULE);
@@ -1521,7 +1523,7 @@ void CGUIWindowManager::SendThreadMessage(CGUIMessage& message, int window /*= 0
std::unique_lock<CCriticalSection> lock(m_critSection);
CGUIMessage* msg = new CGUIMessage(message);
- m_vecThreadMessages.emplace_back(std::pair<CGUIMessage*, int>(msg,window));
+ m_vecThreadMessages.emplace_back(msg, window);
}
void CGUIWindowManager::DispatchThreadMessages()
diff --git a/xbmc/guilib/WindowIDs.dox b/xbmc/guilib/WindowIDs.dox
index 68fdc7dc08..5efe215b6c 100644
--- a/xbmc/guilib/WindowIDs.dox
+++ b/xbmc/guilib/WindowIDs.dox
@@ -56,7 +56,6 @@ This page shows the window names, the window definition, the window ID and the s
| LockSettings | WINDOW_DIALOG_LOCK_SETTINGS | 10131 | DialogSettings.xml | |
| ContentSettings | WINDOW_DIALOG_CONTENT_SETTINGS | 10132 | DialogSettings.xml | |
| LibexportSettings | WINDOW_DIALOG_LIBEXPORT_SETTINGS | 10133 | DialogSettings.xml | |
-| Favourites | WINDOW_DIALOG_FAVOURITES | 10134 | DialogFavourites.xml | @deprecated Dialog **Favourites** is deprecated and is scheduled for removal in v21.<p></p> @skinning_v20 Deprecated. Please use **FavouritesBrowser** window instead. <p></p> |
| SongInformation | WINDOW_DIALOG_SONG_INFO | 10135 | DialogMusicInfo.xml | |
| SmartPlaylistEditor | WINDOW_DIALOG_SMART_PLAYLIST_EDITOR | 10136 | SmartPlaylistEditor.xml | |
| SmartPlaylistRule | WINDOW_DIALOG_SMART_PLAYLIST_RULE | 10137 | SmartPlaylistRule.xml | |
@@ -68,7 +67,7 @@ This page shows the window names, the window definition, the window ID and the s
| AddonInformation | WINDOW_DIALOG_ADDON_INFO | 10146 | DialogAddonInfo.xml | |
| TextViewer | WINDOW_DIALOG_TEXT_VIEWER | 10147 | DialogTextViewer.xml | |
| | WINDOW_DIALOG_PLAY_EJECT | 10148 | DialogConfirm.xml | |
-| | WINDOW_DIALOG_PERIPHERALS | 10149 | DialogSelect.xml | |
+| Peripherals | WINDOW_DIALOG_PERIPHERALS | 10149 | DialogSelect.xml | @skinning_v21 The "Peripherals" alias has been added for this window<p></p> |
| PeripheralSettings | WINDOW_DIALOG_PERIPHERAL_SETTINGS | 10150 | DialogSettings.xml | |
| ExtendedProgressDialog | WINDOW_DIALOG_EXT_PROGRESS | 10151 | DialogExtendedProgressBar.xml | |
| MediaFilter | WINDOW_DIALOG_MEDIA_FILTER | 10152 | DialogSettings.xml | |
@@ -113,23 +112,29 @@ This page shows the window names, the window definition, the window ID and the s
| FullscreenRadioPreview | WINDOW_FULLSCREEN_RADIO_PREVIEW | 10803 | None (shortcut to fullscreenradio) | |
| FullscreenLivetvInput | WINDOW_FULLSCREEN_LIVETV_INPUT | 10804 | None (shortcut to fullscreenlivetv) | |
| FullscreenRadioInput | WINDOW_FULLSCREEN_RADIO_INPUT | 10805 | None (shortcut to fullscreenradio) | |
-| GameControllers | WINDOW_DIALOG_GAME_CONTROLLERS | 10820 | DialogGameControllers.xml | |
-| Games | WINDOW_GAMES | 10821 | MyGames.xml | |
-| GameOSD | WINDOW_DIALOG_GAME_OSD | 10822 | GameOSD.xml | |
-| GameVideoFilter | WINDOW_DIALOG_GAME_VIDEO_FILTER | 10823 | DialogSelect.xml | |
-| GameStretchMode | WINDOW_DIALOG_GAME_STRETCH_MODE | 10824 | DialogSelect.xml | |
-| GameVolume | WINDOW_DIALOG_GAME_VOLUME | 10825 | DialogVolumeBar.xml | |
-| GameAdvancedSettings | WINDOW_DIALOG_GAME_ADVANCED_SETTINGS | 10826 | DialogAddonSettings.xml | |
-| GameVideoRotation | WINDOW_DIALOG_GAME_VIDEO_ROTATION | 10827 | DialogSelect.xml | |
+| GameControllers | WINDOW_DIALOG_GAME_CONTROLLERS | 10820 | DialogGameControllers.xml | @skinning_v17 **New window**<p></p> |
+| Games | WINDOW_GAMES | 10821 | MyGames.xml | @skinning_v18 **New window**<p></p> |
+| GameOSD | WINDOW_DIALOG_GAME_OSD | 10822 | GameOSD.xml | @skinning_v18 **New window**<p></p> |
+| GameVideoFilter | WINDOW_DIALOG_GAME_VIDEO_FILTER | 10823 | DialogSelect.xml | @skinning_v18 **New window**<p></p> |
+| GameStretchMode | WINDOW_DIALOG_GAME_STRETCH_MODE | 10824 | DialogSelect.xml | @skinning_v18 **New window**<p></p> |
+| GameVolume | WINDOW_DIALOG_GAME_VOLUME | 10825 | DialogSlider.xml | @skinning_v18 **New window** See https://github.com/xbmc/xbmc/pull/12765<p></p> |
+| GameAdvancedSettings | WINDOW_DIALOG_GAME_ADVANCED_SETTINGS | 10826 | DialogAddonSettings.xml | @skinning_v18 **New window**<p></p> |
+| GameVideoRotation | WINDOW_DIALOG_GAME_VIDEO_ROTATION | 10827 | DialogSelect.xml | @skinning_v18 **New window**<p></p> |
+| GamePorts | WINDOW_DIALOG_GAME_PORTS | 10828 | DialogGameControllers.xml | @skinning_v20 **New window** See https://github.com/xbmc/xbmc/pull/20505<p></p> |
+| InGameSaves | WINDOW_DIALOG_IN_GAME_SAVES | 10829 | DialogSelect.xml | @skinning_v20 **New window** Part of the GSoC 2020 Saved Game Manager<p></p> |
+| GameSaves | WINDOW_DIALOG_GAME_SAVES | 10830 | DialogSelect.xml | @skinning_v20 **New window** Part of the GSoC 2020 Saved Game Manager<p></p> |
+| GameAgents | WINDOW_DIALOG_GAME_AGENTS | 10831 | DialogGameControllers.xml | @skinning_v21 **New window** See https://github.com/xbmc/xbmc/pull/23548<p></p> |
| Custom Skin Windows | - | - | custom*.xml | |
| SelectDialog | WINDOW_DIALOG_SELECT | 12000 | DialogSelect.xml | |
| MusicInformation | WINDOW_DIALOG_MUSIC_INFO | 12001 | DialogMusicInfo.xml | |
| OKDialog | WINDOW_DIALOG_OK | 12002 | DialogConfirm.xml | |
| MovieInformation | WINDOW_DIALOG_VIDEO_INFO | 12003 | DialogVideoInfo.xml | |
+| VideoVersion | WINDOW_DIALOG_VIDEO_VERSION | 12004 | DialogVideoVersion.xml | @skinning_v21 **New window** VideoVersion |
| FullscreenVideo | WINDOW_FULLSCREEN_VIDEO | 12005 | VideoFullScreen.xml | |
| Visualisation | WINDOW_VISUALISATION | 12006 | MusicVisualisation.xml | |
| Slideshow | WINDOW_SLIDESHOW | 12007 | SlideShow.xml | |
| DialogColorPicker | WINDOW_DIALOG_COLOR_PICKER | 12008 | DialogColorPicker.xml | |
+| VideoVersionPlay | WINDOW_DIALOG_VIDEO_VERSION_PLAY | 12009 | DialogVideoVersion.xml | @skinning_v21 **New window** VideoVersionPlay |
| Weather | WINDOW_WEATHER | 12600 | MyWeather.xml | |
| Screensaver | WINDOW_SCREENSAVER | 12900 | none | |
| VideoOSD | WINDOW_DIALOG_VIDEO_OSD | 12901 | VideoOSD.xml | |
@@ -141,5 +146,10 @@ This page shows the window names, the window definition, the window ID and the s
| Startup | WINDOW_STARTUP_ANIM | 12999 | Startup.xml | |
| FavouritesBrowser | WINDOW_FAVOURITES | 10060 | MyFavourites.xml | @skinning_v20 **New window** FavouritesBrowser, replaces the old Favourites dialog.<p></p> |
+\section window_ids_rm Additional revision history
+
+\subsection modules_rm_windowids_v21 Kodi v21 (Omega)
+@skinning_v21 **[Removed Windows]** The following windows have been removed:
+- **Favourites** - Window Favourites (DialogFavourites.xml with id 10134) has been removed. Please use **FavouritesBrowser** instead.
*/
diff --git a/xbmc/guilib/WindowIDs.h b/xbmc/guilib/WindowIDs.h
index d08354d112..3f70bd8fc3 100644
--- a/xbmc/guilib/WindowIDs.h
+++ b/xbmc/guilib/WindowIDs.h
@@ -72,7 +72,6 @@
#define WINDOW_DIALOG_LOCK_SETTINGS 10131
#define WINDOW_DIALOG_CONTENT_SETTINGS 10132
#define WINDOW_DIALOG_LIBEXPORT_SETTINGS 10133
-#define WINDOW_DIALOG_FAVOURITES 10134
#define WINDOW_DIALOG_SONG_INFO 10135
#define WINDOW_DIALOG_SMART_PLAYLIST_EDITOR 10136
#define WINDOW_DIALOG_SMART_PLAYLIST_RULE 10137
@@ -165,10 +164,12 @@
#define WINDOW_DIALOG_MUSIC_INFO 12001
#define WINDOW_DIALOG_OK 12002
#define WINDOW_DIALOG_VIDEO_INFO 12003
+#define WINDOW_DIALOG_VIDEO_VERSION 12004
#define WINDOW_FULLSCREEN_VIDEO 12005
#define WINDOW_VISUALISATION 12006
#define WINDOW_SLIDESHOW 12007
#define WINDOW_DIALOG_COLOR_PICKER 12008
+#define WINDOW_DIALOG_VIDEO_VERSION_SELECT 12009
#define WINDOW_WEATHER 12600
#define WINDOW_SCREENSAVER 12900
#define WINDOW_DIALOG_VIDEO_OSD 12901
diff --git a/xbmc/guilib/_Controls.dox b/xbmc/guilib/_Controls.dox
index c9947c3479..fe60088fc7 100644
--- a/xbmc/guilib/_Controls.dox
+++ b/xbmc/guilib/_Controls.dox
@@ -13,6 +13,8 @@ manual will explain each and every control in detail.
- \subpage Fade_Label_Control - used to show multiple pieces of text in the same position, by fading from one to the other.
- \subpage Fixed_List_Container - used for a list of items with a fixed focus. Same as the \ref Wrap_List_Container "Wrap List Container" except it doesn't wrap.
- \subpage Game_Control - used to display the currently playing game, with optional effects, whilst in the GUI.
+- \subpage Game_Controller - used to display a game controller, with optional effects
+- \subpage Game_Controller_List - used to give special behavior to a list of Game Controller controls
- \subpage Group_Control - used to group controls together.
- \subpage Group_List_Control - special case of the group control that forms a scrolling list of controls.
- \subpage Image_Control - used to show an image.
diff --git a/xbmc/guilib/guiinfo/GUIInfoLabel.cpp b/xbmc/guilib/guiinfo/GUIInfoLabel.cpp
index 07ca5a4c23..9973b44a94 100644
--- a/xbmc/guilib/guiinfo/GUIInfoLabel.cpp
+++ b/xbmc/guilib/guiinfo/GUIInfoLabel.cpp
@@ -10,7 +10,9 @@
#include "FileItem.h"
#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
#include "addons/Skin.h"
+#include "games/GameServices.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIListItem.h"
#include "guilib/LocalizeStrings.h"
@@ -230,6 +232,16 @@ std::string AddonReplacer(const std::string &str)
return g_localizeStrings.GetAddonString(addonid, stringid);
}
+std::string ControllerFeatureReplacer(const std::string& str)
+{
+ // assumes "feature name,controller ID"
+ const size_t length = str.find(',');
+ const std::string featureName = str.substr(0, length);
+ const std::string controllerId = str.substr(length + 1);
+
+ return CServiceBroker::GetGameServices().TranslateFeature(controllerId, featureName);
+}
+
std::string NumberReplacer(const std::string &str)
{
return str;
@@ -249,6 +261,12 @@ std::string CGUIInfoLabel::ReplaceAddonStrings(std::string &&label)
return std::move(label);
}
+std::string CGUIInfoLabel::ReplaceControllerStrings(std::string&& label)
+{
+ ReplaceSpecialKeywordReferences(label, "FEATURE", ControllerFeatureReplacer);
+ return std::move(label);
+}
+
enum EINFOFORMAT { NONE = 0, FORMATINFO, FORMATESCINFO, FORMATVAR, FORMATESCVAR };
typedef struct
@@ -272,7 +290,9 @@ void CGUIInfoLabel::Parse(const std::string& label,
std::string work = ReplaceLocalize(label);
// Step 2: Replace all $ADDON[id number] with the real string
work = ReplaceAddonStrings(std::move(work));
- // Step 3: Find all $INFO[info,prefix,postfix] blocks
+ // Step 3: Replace all game controller strings with the real string
+ work = ReplaceControllerStrings(std::move(work));
+ // Step 4: Find all $INFO[info,prefix,postfix] blocks
EINFOFORMAT format;
do
{
diff --git a/xbmc/guilib/guiinfo/GUIInfoLabel.h b/xbmc/guilib/guiinfo/GUIInfoLabel.h
index 3bb4452e9f..fd5da50154 100644
--- a/xbmc/guilib/guiinfo/GUIInfoLabel.h
+++ b/xbmc/guilib/guiinfo/GUIInfoLabel.h
@@ -92,6 +92,16 @@ public:
*/
static std::string ReplaceAddonStrings(std::string &&label);
+ /*!
+ * \brief Replaces instances of $FEATURE[feature name, controller ID] with
+ * the appropriate localized controller string
+ *
+ * \param label The text to replace
+ *
+ * \return text with any controller strings filled in
+ */
+ static std::string ReplaceControllerStrings(std::string&& label);
+
typedef std::function<std::string(const std::string&)> StringReplacerFunc;
/*!
diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h
index 428171c150..0c481dc324 100644
--- a/xbmc/guilib/guiinfo/GUIInfoLabels.h
+++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h
@@ -564,6 +564,11 @@
#define SYSTEM_BUILD_VERSION_CODE 1007
#define SYSTEM_BUILD_VERSION_GIT 1008
+static constexpr unsigned int SYSTEM_LOCALE_TIMEZONECOUNTRY = 1009;
+static constexpr unsigned int SYSTEM_LOCALE_TIMEZONE = 1010;
+static constexpr unsigned int SYSTEM_LOCALE_REGION = 1011;
+static constexpr unsigned int SYSTEM_LOCALE = 1012;
+
#define PVR_CONDITIONS_START 1100
#define PVR_IS_RECORDING (PVR_CONDITIONS_START)
#define PVR_HAS_TIMER (PVR_CONDITIONS_START + 1)
@@ -966,6 +971,8 @@
#define LISTITEM_PARENTAL_RATING_CODE (LISTITEM_START + 210)
#define LISTITEM_VIDEO_WIDTH (LISTITEM_START + 211)
#define LISTITEM_VIDEO_HEIGHT (LISTITEM_START + 212)
+#define LISTITEM_HASVIDEOVERSIONS (LISTITEM_START + 213)
+#define LISTITEM_ISVIDEOEXTRAS (LISTITEM_START + 214)
#define LISTITEM_END (LISTITEM_START + 2500)
diff --git a/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp b/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
index fc0f471746..f7aceadb7f 100644
--- a/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
@@ -245,7 +245,7 @@ bool CLibraryGUIInfo::GetBool(bool& value, const CGUIListItem *gitem, int contex
{
artistcount = db.GetArtistCountForRole(strRole);
db.Close();
- m_libraryRoleCounts.emplace_back(std::make_pair(strRole, artistcount));
+ m_libraryRoleCounts.emplace_back(strRole, artistcount);
}
}
value = artistcount > 0;
diff --git a/xbmc/guilib/guiinfo/PicturesGUIInfo.cpp b/xbmc/guilib/guiinfo/PicturesGUIInfo.cpp
index 6115517fdb..ff6b9e2877 100644
--- a/xbmc/guilib/guiinfo/PicturesGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/PicturesGUIInfo.cpp
@@ -22,6 +22,7 @@
#include "utils/log.h"
#include <map>
+#include <memory>
using namespace KODI::GUILIB::GUIINFO;
@@ -99,7 +100,7 @@ void CPicturesGUIInfo::SetCurrentSlide(CFileItem *item)
if (!tag->Loaded()) // If picture metadata has not been loaded yet, load it now
tag->Load(item->GetPath());
}
- m_currentSlide.reset(new CFileItem(*item));
+ m_currentSlide = std::make_unique<CFileItem>(*item);
}
else if (m_currentSlide)
{
diff --git a/xbmc/guilib/guiinfo/PlayerGUIInfo.cpp b/xbmc/guilib/guiinfo/PlayerGUIInfo.cpp
index dfb9455aa2..08a8a6abff 100644
--- a/xbmc/guilib/guiinfo/PlayerGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/PlayerGUIInfo.cpp
@@ -33,6 +33,7 @@
#include <charconv>
#include <cmath>
+#include <memory>
using namespace KODI::GUILIB::GUIINFO;
@@ -147,7 +148,7 @@ bool CPlayerGUIInfo::InitCurrentItem(CFileItem *item)
if (item && m_appPlayer->IsPlaying())
{
CLog::Log(LOGDEBUG, "CPlayerGUIInfo::InitCurrentItem({})", CURL::GetRedacted(item->GetPath()));
- m_currentItem.reset(new CFileItem(*item));
+ m_currentItem = std::make_unique<CFileItem>(*item);
}
else
{
@@ -666,7 +667,7 @@ std::vector<std::pair<float, float>> CPlayerGUIInfo::GetEditList(const CDataCach
{
float editStart = edit.start * 100.0f / duration;
float editEnd = edit.end * 100.0f / duration;
- ranges.emplace_back(std::make_pair(editStart, editEnd));
+ ranges.emplace_back(editStart, editEnd);
}
return ranges;
}
@@ -682,7 +683,7 @@ std::vector<std::pair<float, float>> CPlayerGUIInfo::GetCuts(const CDataCacheCor
{
float marker = cut * 100.0f / duration;
if (marker != 0)
- ranges.emplace_back(std::make_pair(lastMarker, marker));
+ ranges.emplace_back(lastMarker, marker);
lastMarker = marker;
}
@@ -700,7 +701,7 @@ std::vector<std::pair<float, float>> CPlayerGUIInfo::GetSceneMarkers(const CData
{
float marker = scene * 100.0f / duration;
if (marker != 0)
- ranges.emplace_back(std::make_pair(lastMarker, marker));
+ ranges.emplace_back(lastMarker, marker);
lastMarker = marker;
}
@@ -718,7 +719,7 @@ std::vector<std::pair<float, float>> CPlayerGUIInfo::GetChapters(const CDataCach
{
float marker = chapter.second * 1000 * 100.0f / duration;
if (marker != 0)
- ranges.emplace_back(std::make_pair(lastMarker, marker));
+ ranges.emplace_back(lastMarker, marker);
lastMarker = marker;
}
diff --git a/xbmc/guilib/guiinfo/SystemGUIInfo.cpp b/xbmc/guilib/guiinfo/SystemGUIInfo.cpp
index d6a428fab6..9a05031e0e 100644
--- a/xbmc/guilib/guiinfo/SystemGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/SystemGUIInfo.cpp
@@ -326,8 +326,33 @@ bool CSystemGUIInfo::GetLabel(std::string& value, const CFileItem *item, int con
return true;
}
- }
+ case SYSTEM_LOCALE_TIMEZONECOUNTRY:
+ {
+ value = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_LOCALE_TIMEZONECOUNTRY);
+ return true;
+ }
+
+ case SYSTEM_LOCALE_TIMEZONE:
+ {
+ value = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_LOCALE_TIMEZONE);
+ return true;
+ }
+
+ case SYSTEM_LOCALE_REGION:
+ {
+ value = g_langInfo.GetCurrentRegion();
+ return true;
+ }
+
+ case SYSTEM_LOCALE:
+ {
+ value = g_langInfo.GetRegionLocale();
+ return true;
+ }
+ }
return false;
}
diff --git a/xbmc/guilib/guiinfo/VideoGUIInfo.cpp b/xbmc/guilib/guiinfo/VideoGUIInfo.cpp
index 0d23d63aa5..62c162f245 100644
--- a/xbmc/guilib/guiinfo/VideoGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/VideoGUIInfo.cpp
@@ -765,6 +765,12 @@ bool CVideoGUIInfo::GetBool(bool& value, const CGUIListItem *gitem, int contextW
case LISTITEM_IS_COLLECTION:
value = tag->m_type == MediaTypeVideoCollection;
return true;
+ case LISTITEM_HASVIDEOVERSIONS:
+ value = tag->m_hasVideoVersions;
+ return true;
+ case LISTITEM_ISVIDEOEXTRAS:
+ value = tag->IsVideoExtras();
+ return true;
}
}
diff --git a/xbmc/imagefiles/SpecialImageLoaderFactory.cpp b/xbmc/imagefiles/SpecialImageLoaderFactory.cpp
index c143ad1ef4..8673031797 100644
--- a/xbmc/imagefiles/SpecialImageLoaderFactory.cpp
+++ b/xbmc/imagefiles/SpecialImageLoaderFactory.cpp
@@ -11,6 +11,7 @@
#include "guilib/Texture.h"
#include "music/MusicEmbeddedImageFileLoader.h"
#include "pictures/PictureFolderImageFileLoader.h"
+#include "pvr/PVRChannelGroupImageFileLoader.h"
#include "video/VideoChapterImageFileLoader.h"
#include "video/VideoEmbeddedImageFileLoader.h"
#include "video/VideoGeneratedImageFileLoader.h"
@@ -24,6 +25,7 @@ CSpecialImageLoaderFactory::CSpecialImageLoaderFactory()
m_specialImageLoaders[2] = std::make_unique<VIDEO::CVideoGeneratedImageFileLoader>();
m_specialImageLoaders[3] = std::make_unique<CPictureFolderImageFileLoader>();
m_specialImageLoaders[4] = std::make_unique<VIDEO::CVideoChapterImageFileLoader>();
+ m_specialImageLoaders[5] = std::make_unique<PVR::CPVRChannelGroupImageFileLoader>();
}
std::unique_ptr<CTexture> CSpecialImageLoaderFactory::Load(const std::string& specialType,
diff --git a/xbmc/imagefiles/SpecialImageLoaderFactory.h b/xbmc/imagefiles/SpecialImageLoaderFactory.h
index e5bb7b6259..c2fae52086 100644
--- a/xbmc/imagefiles/SpecialImageLoaderFactory.h
+++ b/xbmc/imagefiles/SpecialImageLoaderFactory.h
@@ -29,6 +29,6 @@ public:
unsigned int preferredHeight) const;
private:
- std::array<std::unique_ptr<ISpecialImageFileLoader>, 5> m_specialImageLoaders{};
+ std::array<std::unique_ptr<ISpecialImageFileLoader>, 6> m_specialImageLoaders{};
};
} // namespace IMAGE_FILES
diff --git a/xbmc/input/IRTranslator.cpp b/xbmc/input/IRTranslator.cpp
index e115352b7e..1c4be47431 100644
--- a/xbmc/input/IRTranslator.cpp
+++ b/xbmc/input/IRTranslator.cpp
@@ -19,6 +19,7 @@
#include "utils/log.h"
#include <cstring>
+#include <memory>
#include <stdlib.h>
#include <vector>
@@ -106,7 +107,7 @@ void CIRTranslator::MapRemote(tinyxml2::XMLNode* pRemote, const std::string& szD
auto it = m_irRemotesMap.find(szDevice);
if (it == m_irRemotesMap.end())
- m_irRemotesMap[szDevice].reset(new IRButtonMap);
+ m_irRemotesMap[szDevice] = std::make_shared<IRButtonMap>();
const std::shared_ptr<IRButtonMap>& buttons = m_irRemotesMap[szDevice];
@@ -116,7 +117,7 @@ void CIRTranslator::MapRemote(tinyxml2::XMLNode* pRemote, const std::string& szD
if (!pButton->NoChildren())
{
if (std::strcmp(pButton->Value(), "altname") == 0)
- remoteNames.push_back(pButton->FirstChild()->Value());
+ remoteNames.emplace_back(pButton->FirstChild()->Value());
else
(*buttons)[pButton->FirstChild()->Value()] = pButton->Value();
}
diff --git a/xbmc/input/InputManager.cpp b/xbmc/input/InputManager.cpp
index 571b2599ab..30f76c1b89 100644
--- a/xbmc/input/InputManager.cpp
+++ b/xbmc/input/InputManager.cpp
@@ -43,6 +43,7 @@
#include <algorithm>
#include <math.h>
#include <mutex>
+#include <unordered_map>
using EVENTSERVER::CEventServer;
@@ -50,6 +51,14 @@ using namespace KODI;
const std::string CInputManager::SETTING_INPUT_ENABLE_CONTROLLER = "input.enablejoystick";
+namespace
+{
+const std::unordered_map<uint8_t, int> keyComposeactionEventMap = {
+ {XBMC_KEYCOMPOSING_COMPOSING, ACTION_KEYBOARD_COMPOSING_KEY},
+ {XBMC_KEYCOMPOSING_CANCELLED, ACTION_KEYBOARD_COMPOSING_KEY_CANCELLED},
+ {XBMC_KEYCOMPOSING_FINISHED, ACTION_KEYBOARD_COMPOSING_KEY_FINISHED}};
+}
+
CInputManager::CInputManager()
: m_keymapEnvironment(new CKeymapEnvironment),
m_buttonTranslator(new CButtonTranslator),
@@ -230,7 +239,7 @@ bool CInputManager::ProcessEventServer(int windowId, float frameTime)
CLog::Log(LOGDEBUG, "EventServer: key {} translated to action {}", wKeyID, actionName);
- return ExecuteInputAction(CAction(actionID, fAmount, 0.0f, actionName));
+ return ExecuteInputAction(CAction(actionID, fAmount, 0.0f, actionName, 0, wKeyID));
}
else
{
@@ -349,6 +358,15 @@ bool CInputManager::OnEvent(XBMC_Event& newEvent)
m_Keyboard.ProcessKeyUp();
OnKeyUp(m_Keyboard.TranslateKey(newEvent.key.keysym));
break;
+ case XBMC_KEYCOMPOSING_COMPOSING:
+ case XBMC_KEYCOMPOSING_CANCELLED:
+ case XBMC_KEYCOMPOSING_FINISHED:
+ {
+ const CAction action = CAction(keyComposeactionEventMap.find(newEvent.type)->second,
+ static_cast<wchar_t>(newEvent.key.keysym.unicode));
+ ExecuteInputAction(action);
+ break;
+ }
case XBMC_MOUSEBUTTONDOWN:
case XBMC_MOUSEBUTTONUP:
case XBMC_MOUSEMOTION:
diff --git a/xbmc/input/JoystickMapper.cpp b/xbmc/input/JoystickMapper.cpp
index 4e1337ab5f..72145e2972 100644
--- a/xbmc/input/JoystickMapper.cpp
+++ b/xbmc/input/JoystickMapper.cpp
@@ -16,6 +16,7 @@
#include "utils/StringUtils.h"
#include <algorithm>
+#include <memory>
#include <sstream>
#include <utility>
@@ -43,7 +44,7 @@ void CJoystickMapper::MapActions(int windowID, const tinyxml2::XMLNode* pDevice)
// Create/overwrite keymap
auto& keymap = m_joystickKeymaps[controllerId];
if (!keymap)
- keymap.reset(new CWindowKeymap(controllerId));
+ keymap = std::make_shared<CWindowKeymap>(controllerId);
const auto* pButton = pDevice->FirstChildElement();
while (pButton != nullptr)
diff --git a/xbmc/input/WindowTranslator.cpp b/xbmc/input/WindowTranslator.cpp
index a6b7b3b86d..c1bbd68da7 100644
--- a/xbmc/input/WindowTranslator.cpp
+++ b/xbmc/input/WindowTranslator.cpp
@@ -78,7 +78,6 @@ const CWindowTranslator::WindowMapByName CWindowTranslator::WindowMappingByName
{"virtualkeyboard", WINDOW_DIALOG_KEYBOARD},
{"volumebar", WINDOW_DIALOG_VOLUME_BAR},
{"submenu", WINDOW_DIALOG_SUB_MENU},
- {"favourites", WINDOW_DIALOG_FAVOURITES},
{"contextmenu", WINDOW_DIALOG_CONTEXT_MENU},
{"notification", WINDOW_DIALOG_KAI_TOAST},
{"numericinput", WINDOW_DIALOG_NUMERIC},
@@ -120,6 +119,8 @@ const CWindowTranslator::WindowMapByName CWindowTranslator::WindowMappingByName
{"musicinformation", WINDOW_DIALOG_MUSIC_INFO},
{"okdialog", WINDOW_DIALOG_OK},
{"movieinformation", WINDOW_DIALOG_VIDEO_INFO},
+ {"videoversion", WINDOW_DIALOG_VIDEO_VERSION},
+ {"videoversionselect", WINDOW_DIALOG_VIDEO_VERSION_SELECT},
{"textviewer", WINDOW_DIALOG_TEXT_VIEWER},
{"fullscreenvideo", WINDOW_FULLSCREEN_VIDEO},
{"dialogcolorpicker", WINDOW_DIALOG_COLOR_PICKER},
@@ -154,6 +155,7 @@ const CWindowTranslator::WindowMapByName CWindowTranslator::WindowMappingByName
{"splash", WINDOW_SPLASH},
{"startwindow", WINDOW_START},
{"startup", WINDOW_STARTUP_ANIM},
+ {"peripherals", WINDOW_DIALOG_PERIPHERALS},
{"peripheralsettings", WINDOW_DIALOG_PERIPHERAL_SETTINGS},
{"extendedprogressdialog", WINDOW_DIALOG_EXT_PROGRESS},
{"mediafilter", WINDOW_DIALOG_MEDIA_FILTER},
diff --git a/xbmc/input/XBMC_keysym.h b/xbmc/input/XBMC_keysym.h
index a417c0c414..7a23dabc33 100644
--- a/xbmc/input/XBMC_keysym.h
+++ b/xbmc/input/XBMC_keysym.h
@@ -133,6 +133,11 @@ typedef enum
XBMCK_MEDIA_REWIND = 0xBA,
XBMCK_MEDIA_FASTFORWARD = 0xBB,
+ // This key is not present on standard US keyboard layouts. For European
+ // layouts it's usually located to the right of left-shift key, with '\' as
+ // its main function.
+ XBMCK_OEM_102 = 0xE2,
+
// Numeric keypad
XBMCK_KP0 = 0x100,
XBMCK_KP1 = 0x101,
@@ -223,6 +228,49 @@ typedef enum
// Add any other keys here
+ /* Dead keys */
+ XBMCK_GRAVE = 0x0060,
+ XBMCK_ACUTE = 0x00B4,
+ XBMCK_CIRCUMFLEX = 0x005E,
+ XBMCK_PERISPOMENI = 0x1FC0,
+ XBMCK_MACRON = 0x00AF,
+ XBMCK_BREVE = 0x02D8,
+ XBMCK_ABOVEDOT = 0x02D9,
+ XBMCK_DIAERESIS = 0x00A8,
+ XBMCK_ABOVERING = 0x02DA,
+ XBMCK_DOUBLEACUTE = 0x030B,
+ XBMCK_CARON = 0x030C,
+ XBMCK_CEDILLA = 0x0327,
+ XBMCK_OGONEK = 0x0328,
+ XBMCK_IOTA = 0x0345,
+ XBMCK_VOICESOUND = 0x3099,
+ XBMCK_SEMIVOICESOUND = 0x309A,
+ XBMCK_BELOWDOT = 0x0323,
+ XBMCK_HOOK = 0x0309,
+ XBMCK_HORN = 0x031B,
+ XBMCK_STROKE = 0x0335,
+ XBMCK_ABOVECOMMA = 0x0313,
+ XBMCK_ABOVEREVERSEDCOMMA = 0x0314,
+ XBMCK_DOUBLEGRAVE = 0x30F,
+ XBMCK_BELOWRING = 0x325,
+ XBMCK_BELOWMACRON = 0x331,
+ XBMCK_BELOWCIRCUMFLEX = 0x32D,
+ XBMCK_BELOWTILDE = 0x330,
+ XBMCK_BELOWBREVE = 0x32e,
+ XBMCK_BELOWDIAERESIS = 0x324,
+ XBMCK_INVERTEDBREVE = 0x32f,
+ XBMCK_BELOWCOMMA = 0x326,
+ XBMCK_LOWLINE = 0x332,
+ XBMCK_ABOVEVERTICALLINE = 0x30D,
+ XBMCK_BELOWVERTICALLINE = 0x329,
+ XBMCK_LONGSOLIDUSOVERLAY = 0x338,
+ XBMCK_DEAD_A = 0x363,
+ XBMCK_DEAD_E = 0x364,
+ XBMCK_DEAD_I = 0x365,
+ XBMCK_DEAD_O = 0x366,
+ XBMCK_DEAD_U = 0x367,
+ XBMCK_SCHWA = 0x1DEA,
+
/* Media keys */
XBMCK_STOP = 337,
XBMCK_RECORD = 338,
diff --git a/xbmc/input/actions/Action.cpp b/xbmc/input/actions/Action.cpp
index 8449e42029..bc85cb2686 100644
--- a/xbmc/input/actions/Action.cpp
+++ b/xbmc/input/actions/Action.cpp
@@ -20,14 +20,15 @@ CAction::CAction(int actionID,
float amount1 /* = 1.0f */,
float amount2 /* = 0.0f */,
const std::string& name /* = "" */,
- unsigned int holdTime /*= 0*/)
+ unsigned int holdTime /*= 0*/,
+ unsigned int buttonCode /*= 0*/)
: m_name(name)
{
m_id = actionID;
m_amount[0] = amount1;
m_amount[1] = amount2;
m_repeat = 0;
- m_buttonCode = 0;
+ m_buttonCode = buttonCode;
m_unicode = 0;
m_holdTime = holdTime;
}
diff --git a/xbmc/input/actions/Action.h b/xbmc/input/actions/Action.h
index f1d417370a..dad49fa5b0 100644
--- a/xbmc/input/actions/Action.h
+++ b/xbmc/input/actions/Action.h
@@ -27,7 +27,8 @@ public:
float amount1 = 1.0f,
float amount2 = 0.0f,
const std::string& name = "",
- unsigned int holdTime = 0);
+ unsigned int holdTime = 0,
+ unsigned int buttonCode = 0);
CAction(int actionID, wchar_t unicode);
CAction(int actionID,
unsigned int state,
diff --git a/xbmc/input/actions/ActionIDs.h b/xbmc/input/actions/ActionIDs.h
index 8d4e14ee4a..f302647902 100644
--- a/xbmc/input/actions/ActionIDs.h
+++ b/xbmc/input/actions/ActionIDs.h
@@ -447,6 +447,13 @@ constexpr const int ACTION_CYCLE_TONEMAP_METHOD = 261; //!< Switch to next tonem
//! Show debug info for video (source format, metadata, shaders, render flags and output format)
constexpr const int ACTION_PLAYER_DEBUG_VIDEO = 262;
+//! Keyboard is composing a key (sequence started by a dead key press)
+constexpr const int ACTION_KEYBOARD_COMPOSING_KEY = 263;
+//! Keyboard has canceled the key composition
+constexpr const int ACTION_KEYBOARD_COMPOSING_KEY_CANCELLED = 264;
+//! Keyboard has finishing the key composition
+constexpr const int ACTION_KEYBOARD_COMPOSING_KEY_FINISHED = 265;
+
// Voice actions
constexpr const int ACTION_VOICE_RECOGNIZE = 300;
diff --git a/xbmc/input/joysticks/generic/ButtonMapping.cpp b/xbmc/input/joysticks/generic/ButtonMapping.cpp
index 8ecfb83c23..6b4cd5cfa9 100644
--- a/xbmc/input/joysticks/generic/ButtonMapping.cpp
+++ b/xbmc/input/joysticks/generic/ButtonMapping.cpp
@@ -25,6 +25,7 @@
#include <algorithm>
#include <assert.h>
#include <cmath>
+#include <memory>
using namespace KODI;
using namespace JOYSTICK;
@@ -571,7 +572,7 @@ CMouseButtonDetector& CButtonMapping::GetMouseButton(MOUSE::BUTTON_ID buttonInde
CPointerDetector& CButtonMapping::GetPointer()
{
if (!m_pointer)
- m_pointer.reset(new CPointerDetector(this));
+ m_pointer = std::make_unique<CPointerDetector>(this);
return *m_pointer;
}
diff --git a/xbmc/input/joysticks/keymaps/KeymapHandler.cpp b/xbmc/input/joysticks/keymaps/KeymapHandler.cpp
index 24e2a0b300..51f5e5f076 100644
--- a/xbmc/input/joysticks/keymaps/KeymapHandler.cpp
+++ b/xbmc/input/joysticks/keymaps/KeymapHandler.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <assert.h>
#include <cmath>
+#include <memory>
#include <utility>
using namespace KODI;
@@ -33,7 +34,7 @@ CKeymapHandler::CKeymapHandler(IActionListener* actionHandler, const IKeymap* ke
assert(m_keymap != nullptr);
if (m_keymap->Environment()->UseEasterEgg())
- m_easterEgg.reset(new CJoystickEasterEgg(ControllerID()));
+ m_easterEgg = std::make_unique<CJoystickEasterEgg>(ControllerID());
}
bool CKeymapHandler::HotkeysPressed(const std::set<std::string>& keyNames) const
diff --git a/xbmc/interfaces/AnnouncementManager.cpp b/xbmc/interfaces/AnnouncementManager.cpp
index 2f5101bf71..2718adf0d5 100644
--- a/xbmc/interfaces/AnnouncementManager.cpp
+++ b/xbmc/interfaces/AnnouncementManager.cpp
@@ -19,6 +19,7 @@
#include "utils/log.h"
#include "video/VideoDatabase.h"
+#include <memory>
#include <mutex>
#include <stdio.h>
@@ -135,7 +136,7 @@ void CAnnouncementManager::Announce(AnnouncementFlag flag,
announcement.data = data;
if (item != nullptr)
- announcement.item = CFileItemPtr(new CFileItem(*item));
+ announcement.item = std::make_shared<CFileItem>(*item);
{
std::unique_lock<CCriticalSection> lock(m_queueCritSection);
diff --git a/xbmc/interfaces/builtins/PlayerBuiltins.cpp b/xbmc/interfaces/builtins/PlayerBuiltins.cpp
index bbd66d72f0..25e6f9c226 100644
--- a/xbmc/interfaces/builtins/PlayerBuiltins.cpp
+++ b/xbmc/interfaces/builtins/PlayerBuiltins.cpp
@@ -428,8 +428,17 @@ void GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList&
VIDEO_UTILS::GetItemsForPlayList(item, queuedItems);
else if (MUSIC_UTILS::IsItemPlayable(*item))
MUSIC_UTILS::GetItemsForPlayList(item, queuedItems);
- else
- CLog::LogF(LOGERROR, "Unable to get playlist items for {}", item->GetPath());
+}
+
+PLAYLIST::Id GetPlayListId(const CFileItem& item)
+{
+ PLAYLIST::Id playlistId{PLAYLIST::TYPE_NONE};
+ if (item.IsVideo())
+ playlistId = PLAYLIST::TYPE_VIDEO;
+ else if (item.IsAudio())
+ playlistId = PLAYLIST::TYPE_MUSIC;
+
+ return playlistId;
}
int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
@@ -504,19 +513,19 @@ int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
if (askToResume)
{
- const VIDEO::GUILIB::SelectAction action =
+ const VIDEO::GUILIB::Action action =
VIDEO::GUILIB::CVideoSelectActionProcessorBase::ChoosePlayOrResume(item);
- if (action == VIDEO::GUILIB::SELECT_ACTION_RESUME)
+ if (action == VIDEO::GUILIB::ACTION_RESUME)
{
item.SetStartOffset(STARTOFFSET_RESUME);
}
- else if (action != VIDEO::GUILIB::SELECT_ACTION_PLAY)
+ else if (action != VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING)
{
// The Resume dialog was closed without any choice
return false;
}
- item.SetProperty("check_resume", false);
}
+ item.SetProperty("check_resume", false);
if (item.IsStack())
{
@@ -562,71 +571,75 @@ int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
}
}
- auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
-
- // Play vs. Queue (+Play)
- if (forcePlay)
- {
- playlistPlayer.ClearPlaylist(playlistId);
- playlistPlayer.Reset();
- playlistPlayer.Add(playlistId, items);
- playlistPlayer.SetCurrentPlaylist(playlistId);
- playlistPlayer.Play(playOffset, "");
- }
- else
+ if (!items.IsEmpty())
{
- const int oldSize = playlistPlayer.GetPlaylist(playlistId).size();
+ auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
- const auto appPlayer = components.GetComponent<CApplicationPlayer>();
- if (playNext)
+ // Play vs. Queue (+Play)
+ if (forcePlay)
{
- if (appPlayer->IsPlaying())
- playlistPlayer.Insert(playlistId, items, playlistPlayer.GetCurrentSong() + 1);
- else
- playlistPlayer.Add(playlistId, items);
+ playlistPlayer.ClearPlaylist(playlistId);
+ playlistPlayer.Reset();
+ playlistPlayer.Add(playlistId, items);
+ playlistPlayer.SetCurrentPlaylist(playlistId);
+ playlistPlayer.Play(playOffset, "");
}
else
{
- playlistPlayer.Add(playlistId, items);
- }
+ const int oldSize = playlistPlayer.GetPlaylist(playlistId).size();
- if (items.Size() && !appPlayer->IsPlaying())
- {
- playlistPlayer.SetCurrentPlaylist(playlistId);
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (playNext)
+ {
+ if (appPlayer->IsPlaying())
+ playlistPlayer.Insert(playlistId, items, playlistPlayer.GetCurrentSong() + 1);
+ else
+ playlistPlayer.Add(playlistId, items);
+ }
+ else
+ {
+ playlistPlayer.Add(playlistId, items);
+ }
- if (containsMusic)
+ if (!appPlayer->IsPlaying())
{
- // video does not auto play on queue like music
- playlistPlayer.Play(hasPlayOffset ? playOffset : oldSize, "");
+ playlistPlayer.SetCurrentPlaylist(playlistId);
+
+ if (containsMusic)
+ {
+ // video does not auto play on queue like music
+ playlistPlayer.Play(hasPlayOffset ? playOffset : oldSize, "");
+ }
}
}
}
-
+ else
+ {
+ CLog::LogF(LOGERROR, "Unable to {} item '{}'", forcePlay ? "play" : "queue",
+ item.GetPath());
+ }
return 0;
}
}
if (forcePlay)
{
- if (item.HasVideoInfoTag() && item.GetStartOffset() == STARTOFFSET_RESUME)
- {
- const CBookmark bookmark = item.GetVideoInfoTag()->GetResumePoint();
- if (bookmark.IsSet())
- item.SetStartOffset(CUtil::ConvertSecsToMilliSecs(bookmark.timeInSeconds));
- }
-
- if ((item.IsAudio() || item.IsVideo()) && !item.IsSmartPlayList())
+ if ((item.IsAudio() || item.IsVideo()) && !item.IsSmartPlayList() && !item.IsPVR())
{
if (!item.HasProperty("playlist_type_hint"))
- item.SetProperty("playlist_type_hint", PLAYLIST::TYPE_MUSIC);
+ item.SetProperty("playlist_type_hint", GetPlayListId(item));
CServiceBroker::GetPlaylistPlayer().Play(std::make_shared<CFileItem>(item), "");
}
else
{
- g_application.PlayMedia(item, "", PLAYLIST::TYPE_NONE);
+ g_application.PlayMedia(item, "", GetPlayListId(item));
}
}
+ else
+ {
+ CLog::LogF(LOGERROR, "Unable to {} item '{}'", forcePlay ? "play" : "queue", item.GetPath());
+ }
return 0;
}
diff --git a/xbmc/interfaces/generic/ScriptInvocationManager.cpp b/xbmc/interfaces/generic/ScriptInvocationManager.cpp
index 9daae0f69f..a3ea29c368 100644
--- a/xbmc/interfaces/generic/ScriptInvocationManager.cpp
+++ b/xbmc/interfaces/generic/ScriptInvocationManager.cpp
@@ -18,6 +18,7 @@
#include "utils/log.h"
#include <cerrno>
+#include <memory>
#include <mutex>
#include <utility>
#include <vector>
@@ -268,8 +269,7 @@ int CScriptInvocationManager::ExecuteAsync(
return invokerThread->GetId();
}
- m_lastInvokerThread =
- CLanguageInvokerThreadPtr(new CLanguageInvokerThread(languageInvoker, this, reuseable));
+ m_lastInvokerThread = std::make_shared<CLanguageInvokerThread>(languageInvoker, this, reuseable);
if (m_lastInvokerThread == NULL)
return -1;
diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.cpp b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
index 813c4c85c4..3d6fa79d79 100644
--- a/xbmc/interfaces/json-rpc/AudioLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
@@ -29,6 +29,8 @@
#include "utils/URIUtils.h"
#include "utils/Variant.h"
+#include <memory>
+
using namespace MUSIC_INFO;
using namespace JSONRPC;
using namespace XFILE;
@@ -495,7 +497,7 @@ JSONRPC_STATUS CAudioLibrary::GetSongDetails(const std::string &method, ITranspo
return InvalidParams;
CFileItemList items;
- CFileItemPtr item = CFileItemPtr(new CFileItem(song));
+ CFileItemPtr item = std::make_shared<CFileItem>(song);
FillItemArtistIDs(song.GetArtistIDArray(), item);
items.Add(item);
@@ -1151,7 +1153,7 @@ bool CAudioLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemL
CSong song;
if (musicdatabase.GetSong(songID, song))
{
- list.Add(CFileItemPtr(new CFileItem(song)));
+ list.Add(std::make_shared<CFileItem>(song));
success = true;
}
}
@@ -1190,7 +1192,7 @@ void CAudioLibrary::FillAlbumItem(const CAlbum& album,
const std::string& path,
std::shared_ptr<CFileItem>& item)
{
- item = CFileItemPtr(new CFileItem(path, album));
+ item = std::make_shared<CFileItem>(path, album);
// Add album artistIds as separate property as not part of CMusicInfoTag
std::vector<int> artistids = album.GetArtistIDArray();
FillItemArtistIDs(artistids, item);
diff --git a/xbmc/interfaces/json-rpc/FileItemHandler.cpp b/xbmc/interfaces/json-rpc/FileItemHandler.cpp
index 9d649d346d..07426447e1 100644
--- a/xbmc/interfaces/json-rpc/FileItemHandler.cpp
+++ b/xbmc/interfaces/json-rpc/FileItemHandler.cpp
@@ -37,6 +37,7 @@
#include "video/VideoThumbLoader.h"
#include <map>
+#include <memory>
#include <string.h>
using namespace MUSIC_INFO;
@@ -524,7 +525,7 @@ bool CFileItemHandler::FillFileItemList(const CVariant &parameterObject, CFileIt
if (!added)
{
- CFileItemPtr item = CFileItemPtr(new CFileItem(file, false));
+ CFileItemPtr item = std::make_shared<CFileItem>(file, false);
if (item->IsPicture())
{
CPictureInfoTag picture;
diff --git a/xbmc/interfaces/json-rpc/FileOperations.cpp b/xbmc/interfaces/json-rpc/FileOperations.cpp
index 6329a7867c..4658e99353 100644
--- a/xbmc/interfaces/json-rpc/FileOperations.cpp
+++ b/xbmc/interfaces/json-rpc/FileOperations.cpp
@@ -26,6 +26,8 @@
#include "utils/Variant.h"
#include "video/VideoDatabase.h"
+#include <memory>
+
using namespace XFILE;
using namespace JSONRPC;
@@ -44,7 +46,7 @@ JSONRPC_STATUS CFileOperations::GetRootDirectory(const std::string &method, ITra
if (sources->at(i).m_iHasLock == LOCK_STATE_LOCKED)
continue;
- items.Add(CFileItemPtr(new CFileItem(sources->at(i))));
+ items.Add(std::make_shared<CFileItem>(sources->at(i)));
}
for (unsigned int i = 0; i < (unsigned int)items.Size(); i++)
@@ -184,7 +186,7 @@ JSONRPC_STATUS CFileOperations::GetFileDetails(const std::string &method, ITrans
if (CDirectory::GetDirectory(path, items, "", DIR_FLAG_DEFAULTS) && items.Contains(file))
item = items.Get(file);
else
- item = CFileItemPtr(new CFileItem(file, false));
+ item = std::make_shared<CFileItem>(file, false);
if (!URIUtils::IsUPnP(file))
FillFileItem(item, item, parameterObject["media"].asString(), parameterObject);
diff --git a/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp b/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp
index da100b24cb..3a9cd037ad 100644
--- a/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp
+++ b/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp
@@ -30,6 +30,8 @@
#include "utils/StringUtils.h"
#include "utils/log.h"
+#include <memory>
+
using namespace JSONRPC;
std::map<std::string, CVariant> CJSONServiceDescription::m_notifications = std::map<std::string, CVariant>();
@@ -422,7 +424,7 @@ bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /*
// of the current property into it, parse it
// recursively and add its default value
// to the current type's default value
- JSONSchemaTypeDefinitionPtr propertyType = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr propertyType = std::make_shared<JSONSchemaTypeDefinition>();
propertyType->name = itr->first;
if (!propertyType->Parse(itr->second))
{
@@ -435,7 +437,7 @@ bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /*
}
hasAdditionalProperties = true;
- additionalProperties = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ additionalProperties = std::make_shared<JSONSchemaTypeDefinition>();
if (value.isMember("additionalProperties"))
{
if (value["additionalProperties"].isBoolean())
@@ -484,7 +486,7 @@ bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /*
// If it is an object, there is only one schema for it
if (value["additionalItems"].isObject())
{
- JSONSchemaTypeDefinitionPtr additionalItem = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr additionalItem = std::make_shared<JSONSchemaTypeDefinition>();
if (additionalItem->Parse(value["additionalItems"]))
additionalItems.push_back(additionalItem);
else
@@ -499,7 +501,7 @@ bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /*
{
for (unsigned int itemIndex = 0; itemIndex < value["additionalItems"].size(); itemIndex++)
{
- JSONSchemaTypeDefinitionPtr additionalItem = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr additionalItem = std::make_shared<JSONSchemaTypeDefinition>();
if (additionalItem->Parse(value["additionalItems"][itemIndex]))
additionalItems.push_back(additionalItem);
@@ -527,7 +529,7 @@ bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /*
{
if (value["items"].isObject())
{
- JSONSchemaTypeDefinitionPtr item = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr item = std::make_shared<JSONSchemaTypeDefinition>();
if (!item->Parse(value["items"]))
{
CLog::Log(LOGDEBUG, "Invalid item definition in \"items\" for type {}", name);
@@ -542,7 +544,7 @@ bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /*
{
for (CVariant::const_iterator_array itemItr = value["items"].begin_array(); itemItr != value["items"].end_array(); ++itemItr)
{
- JSONSchemaTypeDefinitionPtr item = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr item = std::make_shared<JSONSchemaTypeDefinition>();
if (!item->Parse(*itemItr))
{
CLog::Log(LOGDEBUG, "Invalid item definition in \"items\" array for type {}", name);
@@ -1335,7 +1337,7 @@ bool JsonRpcMethod::Parse(const CVariant &value)
// Parse the parameter and add it to the list
// of defined parameters
- JSONSchemaTypeDefinitionPtr param = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr param = std::make_shared<JSONSchemaTypeDefinition>();
if (!parseParameter(parameter, param))
{
missingReference = param->missingReference;
@@ -1615,7 +1617,7 @@ bool CJSONServiceDescription::AddType(const std::string &jsonType)
// Make sure the "id" attribute is correctly populated
descriptionObject[typeName]["id"] = typeName;
- JSONSchemaTypeDefinitionPtr globalType = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr globalType = std::make_shared<JSONSchemaTypeDefinition>();
globalType->name = typeName;
globalType->ID = typeName;
CJSONServiceDescription::addReferenceTypeDefinition(globalType);
@@ -1704,7 +1706,7 @@ bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector
values.size() == 0)
return false;
- JSONSchemaTypeDefinitionPtr definition = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr definition = std::make_shared<JSONSchemaTypeDefinition>();
definition->ID = name;
std::vector<CVariant::VariantType> types;
@@ -1994,7 +1996,7 @@ bool CJSONServiceDescription::parseJSONSchemaType(const CVariant &value, std::ve
// to handle a union type
for (unsigned int typeIndex = 0; typeIndex < value.size(); typeIndex++)
{
- JSONSchemaTypeDefinitionPtr definition = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
+ JSONSchemaTypeDefinitionPtr definition = std::make_shared<JSONSchemaTypeDefinition>();
// If the type is a string try to parse it
if (value[typeIndex].isString())
definition->type = StringToSchemaValueType(value[typeIndex].asString());
diff --git a/xbmc/interfaces/json-rpc/PVROperations.cpp b/xbmc/interfaces/json-rpc/PVROperations.cpp
index 3611ad6d66..0ec809481c 100644
--- a/xbmc/interfaces/json-rpc/PVROperations.cpp
+++ b/xbmc/interfaces/json-rpc/PVROperations.cpp
@@ -27,6 +27,8 @@
#include "pvr/timers/PVRTimers.h"
#include "utils/Variant.h"
+#include <memory>
+
using namespace JSONRPC;
using namespace PVR;
using namespace KODI::MESSAGING;
@@ -217,7 +219,8 @@ JSONRPC_STATUS CPVROperations::GetBroadcastDetails(const std::string &method, IT
if (!epgTag)
return InvalidParams;
- HandleFileItem("broadcastid", false, "broadcastdetails", CFileItemPtr(new CFileItem(epgTag)), parameterObject, parameterObject["properties"], result, false);
+ HandleFileItem("broadcastid", false, "broadcastdetails", std::make_shared<CFileItem>(epgTag),
+ parameterObject, parameterObject["properties"], result, false);
return OK;
}
@@ -231,7 +234,7 @@ JSONRPC_STATUS CPVROperations::GetBroadcastIsPlayable(const std::string& method,
if (!CServiceBroker::GetPVRManager().IsStarted())
return FailedToExecute;
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
parameterObject["broadcastid"].asInteger());
@@ -334,7 +337,11 @@ JSONRPC_STATUS CPVROperations::GetPropertyValue(const std::string &property, CVa
return OK;
}
-void CPVROperations::FillChannelGroupDetails(const std::shared_ptr<CPVRChannelGroup> &channelGroup, const CVariant &parameterObject, CVariant &result, bool append /* = false */)
+void CPVROperations::FillChannelGroupDetails(
+ const std::shared_ptr<const CPVRChannelGroup>& channelGroup,
+ const CVariant& parameterObject,
+ CVariant& result,
+ bool append /* = false */)
{
if (channelGroup == NULL)
return;
@@ -396,7 +403,8 @@ JSONRPC_STATUS CPVROperations::GetTimerDetails(const std::string &method, ITrans
if (!timer)
return InvalidParams;
- HandleFileItem("timerid", false, "timerdetails", CFileItemPtr(new CFileItem(timer)), parameterObject, parameterObject["properties"], result, false);
+ HandleFileItem("timerid", false, "timerdetails", std::make_shared<CFileItem>(timer),
+ parameterObject, parameterObject["properties"], result, false);
return OK;
}
diff --git a/xbmc/interfaces/json-rpc/PVROperations.h b/xbmc/interfaces/json-rpc/PVROperations.h
index 08aa703411..6ead6cd01a 100644
--- a/xbmc/interfaces/json-rpc/PVROperations.h
+++ b/xbmc/interfaces/json-rpc/PVROperations.h
@@ -53,6 +53,10 @@ namespace JSONRPC
private:
static JSONRPC_STATUS GetPropertyValue(const std::string &property, CVariant &result);
- static void FillChannelGroupDetails(const std::shared_ptr<PVR::CPVRChannelGroup> &channelGroup, const CVariant &parameterObject, CVariant &result, bool append = false);
+ static void FillChannelGroupDetails(
+ const std::shared_ptr<const PVR::CPVRChannelGroup>& channelGroup,
+ const CVariant& parameterObject,
+ CVariant& result,
+ bool append = false);
};
}
diff --git a/xbmc/interfaces/json-rpc/PlayerOperations.cpp b/xbmc/interfaces/json-rpc/PlayerOperations.cpp
index 237032b34a..b0d9a956ab 100644
--- a/xbmc/interfaces/json-rpc/PlayerOperations.cpp
+++ b/xbmc/interfaces/json-rpc/PlayerOperations.cpp
@@ -47,6 +47,7 @@
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "utils/MathUtils.h"
+#include "utils/URIUtils.h"
#include "utils/Variant.h"
#include "video/VideoDatabase.h"
@@ -835,11 +836,13 @@ JSONRPC_STATUS CPlayerOperations::Open(const std::string &method, ITransportLaye
}
else if (parameterObject["item"].isMember("channelid"))
{
- const std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
+ const std::shared_ptr<const CPVRChannelGroupsContainer> channelGroupContainer =
+ CServiceBroker::GetPVRManager().ChannelGroups();
if (!channelGroupContainer)
return FailedToExecute;
- const std::shared_ptr<CPVRChannel> channel = channelGroupContainer->GetChannelById(static_cast<int>(parameterObject["item"]["channelid"].asInteger()));
+ const std::shared_ptr<const CPVRChannel> channel = channelGroupContainer->GetChannelById(
+ static_cast<int>(parameterObject["item"]["channelid"].asInteger()));
if (!channel)
return InvalidParams;
@@ -856,7 +859,8 @@ JSONRPC_STATUS CPlayerOperations::Open(const std::string &method, ITransportLaye
}
else if (parameterObject["item"].isMember("recordingid"))
{
- const std::shared_ptr<CPVRRecordings> recordingsContainer = CServiceBroker::GetPVRManager().Recordings();
+ const std::shared_ptr<const CPVRRecordings> recordingsContainer =
+ CServiceBroker::GetPVRManager().Recordings();
if (!recordingsContainer)
return FailedToExecute;
@@ -897,6 +901,12 @@ JSONRPC_STATUS CPlayerOperations::Open(const std::string &method, ITransportLaye
return StartSlideshow("", false, optionShuffled.isBoolean() && optionShuffled.asBoolean());
}
+ else if (list.Size() == 1 && (URIUtils::IsPVRChannel(list[0]->GetPath()) ||
+ URIUtils::IsPVRRecording(list[0]->GetPath())))
+ {
+ if (!CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayMedia(*list[0]))
+ return FailedToExecute;
+ }
else
{
std::string playername;
@@ -2141,17 +2151,19 @@ double CPlayerOperations::ParseTimeInSeconds(const CVariant &time)
bool CPlayerOperations::IsPVRChannel()
{
- const std::shared_ptr<CPVRPlaybackState> state = CServiceBroker::GetPVRManager().PlaybackState();
+ const std::shared_ptr<const CPVRPlaybackState> state =
+ CServiceBroker::GetPVRManager().PlaybackState();
return state->IsPlayingTV() || state->IsPlayingRadio();
}
std::shared_ptr<CPVREpgInfoTag> CPlayerOperations::GetCurrentEpg()
{
- const std::shared_ptr<CPVRPlaybackState> state = CServiceBroker::GetPVRManager().PlaybackState();
+ const std::shared_ptr<const CPVRPlaybackState> state =
+ CServiceBroker::GetPVRManager().PlaybackState();
if (!state->IsPlayingTV() && !state->IsPlayingRadio())
return {};
- const std::shared_ptr<CPVRChannel> currentChannel = state->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> currentChannel = state->GetPlayingChannel();
if (!currentChannel)
return {};
diff --git a/xbmc/interfaces/json-rpc/TextureOperations.cpp b/xbmc/interfaces/json-rpc/TextureOperations.cpp
index 326093b53e..d6ffcc8761 100644
--- a/xbmc/interfaces/json-rpc/TextureOperations.cpp
+++ b/xbmc/interfaces/json-rpc/TextureOperations.cpp
@@ -14,6 +14,8 @@
#include "TextureDatabase.h"
#include "utils/Variant.h"
+#include <algorithm>
+
using namespace JSONRPC;
JSONRPC_STATUS CTextureOperations::GetTextures(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
diff --git a/xbmc/interfaces/json-rpc/VideoLibrary.cpp b/xbmc/interfaces/json-rpc/VideoLibrary.cpp
index db152cd634..f98c84552e 100644
--- a/xbmc/interfaces/json-rpc/VideoLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/VideoLibrary.cpp
@@ -22,6 +22,8 @@
#include "video/VideoDbUrl.h"
#include "video/VideoLibraryQueue.h"
+#include <memory>
+
using namespace JSONRPC;
JSONRPC_STATUS CVideoLibrary::GetMovies(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
@@ -93,7 +95,8 @@ JSONRPC_STATUS CVideoLibrary::GetMovieDetails(const std::string &method, ITransp
if (!videodatabase.GetMovieInfo("", infos, id, RequiresAdditionalDetails(MediaTypeMovie, parameterObject)) || infos.m_iDbId <= 0)
return InvalidParams;
- HandleFileItem("movieid", true, "moviedetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
+ HandleFileItem("movieid", true, "moviedetails", std::make_shared<CFileItem>(infos),
+ parameterObject, parameterObject["properties"], result, false);
return OK;
}
@@ -124,7 +127,8 @@ JSONRPC_STATUS CVideoLibrary::GetMovieSetDetails(const std::string &method, ITra
if (!videodatabase.GetSetInfo(id, infos) || infos.m_iDbId <= 0)
return InvalidParams;
- HandleFileItem("setid", false, "setdetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
+ HandleFileItem("setid", false, "setdetails", std::make_shared<CFileItem>(infos), parameterObject,
+ parameterObject["properties"], result, false);
// Get movies from the set
CFileItemList items;
@@ -227,7 +231,7 @@ JSONRPC_STATUS CVideoLibrary::GetSeasonDetails(const std::string &method, ITrans
infos.m_iDbId <= 0 || infos.m_iIdShow <= 0)
return InvalidParams;
- CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
+ CFileItemPtr pItem = std::make_shared<CFileItem>(infos);
HandleFileItem("seasonid", false, "seasondetails", pItem, parameterObject, parameterObject["properties"], result, false);
return OK;
}
@@ -301,7 +305,7 @@ JSONRPC_STATUS CVideoLibrary::GetEpisodeDetails(const std::string &method, ITran
if (!videodatabase.GetEpisodeInfo("", infos, id, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject)) || infos.m_iDbId <= 0)
return InvalidParams;
- CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
+ CFileItemPtr pItem = std::make_shared<CFileItem>(infos);
// We need to set the correct base path to get the valid fanart
int tvshowid = infos.m_iIdShow;
if (tvshowid <= 0)
@@ -374,7 +378,8 @@ JSONRPC_STATUS CVideoLibrary::GetMusicVideoDetails(const std::string &method, IT
if (!videodatabase.GetMusicVideoInfo("", infos, id, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject)) || infos.m_iDbId <= 0)
return InvalidParams;
- HandleFileItem("musicvideoid", true, "musicvideodetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
+ HandleFileItem("musicvideoid", true, "musicvideodetails", std::make_shared<CFileItem>(infos),
+ parameterObject, parameterObject["properties"], result, false);
return OK;
}
@@ -861,7 +866,8 @@ JSONRPC_STATUS CVideoLibrary::RefreshMovie(const std::string &method, ITransport
bool ignoreNfo = parameterObject["ignorenfo"].asBoolean();
std::string searchTitle = parameterObject["title"].asString();
- CVideoLibraryQueue::GetInstance().RefreshItem(CFileItemPtr(new CFileItem(infos)), ignoreNfo, true, false, searchTitle);
+ CVideoLibraryQueue::GetInstance().RefreshItem(std::make_shared<CFileItem>(infos), ignoreNfo, true,
+ false, searchTitle);
return ACK;
}
@@ -901,7 +907,7 @@ JSONRPC_STATUS CVideoLibrary::RefreshEpisode(const std::string &method, ITranspo
if (!videodatabase.GetEpisodeInfo("", infos, id) || infos.m_iDbId <= 0)
return InvalidParams;
- CFileItemPtr item = CFileItemPtr(new CFileItem(infos));
+ CFileItemPtr item = std::make_shared<CFileItem>(infos);
// We need to set the correct base path to get the valid fanart
int tvshowid = infos.m_iIdShow;
if (tvshowid <= 0)
@@ -928,7 +934,8 @@ JSONRPC_STATUS CVideoLibrary::RefreshMusicVideo(const std::string &method, ITran
bool ignoreNfo = parameterObject["ignorenfo"].asBoolean();
std::string searchTitle = parameterObject["title"].asString();
- CVideoLibraryQueue::GetInstance().RefreshItem(CFileItemPtr(new CFileItem(infos)), ignoreNfo, true, false, searchTitle);
+ CVideoLibraryQueue::GetInstance().RefreshItem(std::make_shared<CFileItem>(infos), ignoreNfo, true,
+ false, searchTitle);
return ACK;
}
@@ -1063,7 +1070,7 @@ bool CVideoLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemL
videodatabase.GetMovieInfo("", details, movieID);
if (!details.IsEmpty())
{
- list.Add(CFileItemPtr(new CFileItem(details)));
+ list.Add(std::make_shared<CFileItem>(details));
success = true;
}
}
@@ -1072,7 +1079,7 @@ bool CVideoLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemL
CVideoInfoTag details;
if (videodatabase.GetEpisodeInfo("", details, episodeID) && !details.IsEmpty())
{
- list.Add(CFileItemPtr(new CFileItem(details)));
+ list.Add(std::make_shared<CFileItem>(details));
success = true;
}
}
@@ -1082,7 +1089,7 @@ bool CVideoLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemL
videodatabase.GetMusicVideoInfo("", details, musicVideoID);
if (!details.IsEmpty())
{
- list.Add(CFileItemPtr(new CFileItem(details)));
+ list.Add(std::make_shared<CFileItem>(details));
success = true;
}
}
diff --git a/xbmc/interfaces/json-rpc/schema/version.txt b/xbmc/interfaces/json-rpc/schema/version.txt
index 998f113d61..652ab24d06 100644
--- a/xbmc/interfaces/json-rpc/schema/version.txt
+++ b/xbmc/interfaces/json-rpc/schema/version.txt
@@ -1 +1 @@
-JSONRPC_VERSION 13.3.0
+JSONRPC_VERSION 13.3.1
diff --git a/xbmc/interfaces/legacy/Dialog.cpp b/xbmc/interfaces/legacy/Dialog.cpp
index 6dd00f179b..55812f9099 100644
--- a/xbmc/interfaces/legacy/Dialog.cpp
+++ b/xbmc/interfaces/legacy/Dialog.cpp
@@ -33,6 +33,8 @@
#include "utils/log.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
+#include <memory>
+
using namespace KODI::MESSAGING;
#define ACTIVE_WINDOW CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()
@@ -171,7 +173,7 @@ namespace XBMCAddon
pDialog->Open();
if (pDialog->IsConfirmed())
- return std::unique_ptr<std::vector<int>>(new std::vector<int>(pDialog->GetSelectedItems()));
+ return std::make_unique<std::vector<int>>(pDialog->GetSelectedItems());
else
return std::unique_ptr<std::vector<int>>();
}
diff --git a/xbmc/interfaces/legacy/InfoTagMusic.cpp b/xbmc/interfaces/legacy/InfoTagMusic.cpp
index 3baf7d5092..300fce848d 100644
--- a/xbmc/interfaces/legacy/InfoTagMusic.cpp
+++ b/xbmc/interfaces/legacy/InfoTagMusic.cpp
@@ -179,6 +179,11 @@ namespace XBMCAddon
return infoTag->GetMusicBrainzAlbumArtistID();
}
+ String InfoTagMusic::getSongVideoURL()
+ {
+ return infoTag->GetSongVideoURL();
+ }
+
void InfoTagMusic::setDbId(int dbId, const String& type)
{
XBMCAddonUtils::GuiLock lock(languageHook, offscreen);
@@ -330,6 +335,12 @@ namespace XBMCAddon
setCommentRaw(infoTag, comment);
}
+ void InfoTagMusic::setSongVideoURL(const String& songVideoURL)
+ {
+ XBMCAddonUtils::GuiLock lock(languageHook, offscreen);
+ setSongVideoURLRaw(infoTag, songVideoURL);
+ }
+
void InfoTagMusic::setDbIdRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int dbId, const String& type)
{
infoTag->SetDatabaseId(dbId, type);
@@ -463,6 +474,12 @@ namespace XBMCAddon
{
infoTag->SetComment(comment);
}
+
+ void InfoTagMusic::setSongVideoURLRaw(MUSIC_INFO::CMusicInfoTag* infoTag,
+ const String& songVideoURL)
+ {
+ infoTag->SetSongVideoURL(songVideoURL);
+ }
}
}
diff --git a/xbmc/interfaces/legacy/InfoTagMusic.h b/xbmc/interfaces/legacy/InfoTagMusic.h
index 835a705fb6..91eaf2c82e 100644
--- a/xbmc/interfaces/legacy/InfoTagMusic.h
+++ b/xbmc/interfaces/legacy/InfoTagMusic.h
@@ -572,6 +572,23 @@ namespace XBMCAddon
#ifdef DOXYGEN_SHOULD_USE_THIS
///
/// \ingroup python_InfoTagMusic
+ /// @brief \python_func{ getSongVideoURL() }
+ /// Returns the URL to a video of the song from the music tag as a string (if present).
+ ///
+ /// @return [string] URL to a video of the song.
+ ///
+ ///
+ ///-----------------------------------------------------------------------
+ /// @python_v21 New function added.
+ ///
+ getSongVideoURL();
+#else
+ String getSongVideoURL();
+#endif
+
+#ifdef DOXYGEN_SHOULD_USE_THIS
+ ///
+ /// \ingroup python_InfoTagMusic
/// @brief \python_func{ setDbId(dbId, type) }
/// Set the database identifier of the music item.
///
@@ -995,6 +1012,23 @@ namespace XBMCAddon
void setComment(const String& comment);
#endif
+#ifdef DOXYGEN_SHOULD_USE_THIS
+ ///
+ /// \ingroup python_InfoTagMusic
+ /// @brief \python_func{ setSongVideoURL(songVideoURL) }
+ /// Set the URL of the song to point to a video.
+ ///
+ /// @param songVideoURL string - URL to a video of the song.
+ ///
+ ///
+ ///-----------------------------------------------------------------------
+ /// @python_v21 New function added.
+ ///
+ setSongVideoURL(...);
+#else
+ void setSongVideoURL(const String& songVideoURL);
+#endif
+
#ifndef SWIG
static void setDbIdRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int dbId, const String& type);
static void setURLRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& url);
@@ -1027,6 +1061,8 @@ namespace XBMCAddon
static void setMusicBrainzAlbumArtistIDRaw(
MUSIC_INFO::CMusicInfoTag* infoTag, const std::vector<String>& musicBrainzAlbumArtistID);
static void setCommentRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& comment);
+ static void setSongVideoURLRaw(MUSIC_INFO::CMusicInfoTag* infoTag,
+ const String& songVideoURL);
#endif
};
//@}
diff --git a/xbmc/interfaces/legacy/InfoTagVideo.cpp b/xbmc/interfaces/legacy/InfoTagVideo.cpp
index 8bc5fd1264..27156d8310 100644
--- a/xbmc/interfaces/legacy/InfoTagVideo.cpp
+++ b/xbmc/interfaces/legacy/InfoTagVideo.cpp
@@ -584,6 +584,11 @@ namespace XBMCAddon
setTagsRaw(infoTag, std::move(tags));
}
+ void InfoTagVideo::setVideoVersion(const String& videoVersion)
+ {
+ setVideoVersionRaw(infoTag, videoVersion);
+ }
+
void InfoTagVideo::setProductionCode(const String& productionCode)
{
XBMCAddonUtils::GuiLock lock(languageHook, offscreen);
@@ -936,6 +941,11 @@ namespace XBMCAddon
infoTag->SetTags(std::move(tags));
}
+ void InfoTagVideo::setVideoVersionRaw(CVideoInfoTag* infoTag, const String& videoVersion)
+ {
+ infoTag->SetVideoVersion(videoVersion);
+ }
+
void InfoTagVideo::setProductionCodeRaw(CVideoInfoTag* infoTag, const String& productionCode)
{
infoTag->SetProductionCode(productionCode);
diff --git a/xbmc/interfaces/legacy/InfoTagVideo.h b/xbmc/interfaces/legacy/InfoTagVideo.h
index 91f925eb60..34933e0ac8 100644
--- a/xbmc/interfaces/legacy/InfoTagVideo.h
+++ b/xbmc/interfaces/legacy/InfoTagVideo.h
@@ -2300,6 +2300,23 @@ namespace XBMCAddon
#ifdef DOXYGEN_SHOULD_USE_THIS
///
/// \ingroup python_InfoTagVideo
+ /// @brief \python_func{ setVideoVersion(videoVersion) }
+ /// Set the video version of the item.
+ ///
+ /// @param videoVersion string - Video version.
+ ///
+ ///
+ ///-----------------------------------------------------------------------
+ /// @python_v21 New function added.
+ ///
+ setVideoVersion(...);
+#else
+ void setVideoVersion(const String& videoVersion);
+#endif
+
+#ifdef DOXYGEN_SHOULD_USE_THIS
+ ///
+ /// \ingroup python_InfoTagVideo
/// @brief \python_func{ setProductionCode(const String& productioncode) }
/// Set the production code of the video item.
///
@@ -2734,6 +2751,7 @@ namespace XBMCAddon
static void setSetRaw(CVideoInfoTag* infoTag, const String& set);
static void setSetOverviewRaw(CVideoInfoTag* infoTag, const String& setOverview);
static void setTagsRaw(CVideoInfoTag* infoTag, std::vector<String> tags);
+ static void setVideoVersionRaw(CVideoInfoTag* infoTag, const String& videoVersion);
static void setProductionCodeRaw(CVideoInfoTag* infoTag, const String& productionCode);
static void setFirstAiredRaw(CVideoInfoTag* infoTag, const String& firstAired);
static void setLastPlayedRaw(CVideoInfoTag* infoTag, const String& lastPlayed);
diff --git a/xbmc/interfaces/legacy/ListItem.cpp b/xbmc/interfaces/legacy/ListItem.cpp
index 64410ad22a..0bb08e6d1b 100644
--- a/xbmc/interfaces/legacy/ListItem.cpp
+++ b/xbmc/interfaces/legacy/ListItem.cpp
@@ -23,6 +23,7 @@
#include "video/VideoInfoTag.h"
#include <cstdlib>
+#include <memory>
#include <sstream>
#include <utility>
@@ -39,7 +40,7 @@ namespace XBMCAddon
item.reset();
// create CFileItem
- item.reset(new CFileItem());
+ item = std::make_shared<CFileItem>();
if (!item) // not sure if this is really possible
return;
@@ -508,6 +509,8 @@ namespace XBMCAddon
InfoTagVideo::setSetOverviewRaw(videotag, value);
else if (key == "tag")
InfoTagVideo::setTagsRaw(videotag, getVideoStringArray(alt, key, value));
+ else if (key == "videoversion")
+ InfoTagVideo::setVideoVersionRaw(videotag, value);
else if (key == "imdbnumber")
InfoTagVideo::setIMDBNumberRaw(videotag, value);
else if (key == "code")
diff --git a/xbmc/interfaces/legacy/ListItem.h b/xbmc/interfaces/legacy/ListItem.h
index 82513e0f45..0a734aa51f 100644
--- a/xbmc/interfaces/legacy/ListItem.h
+++ b/xbmc/interfaces/legacy/ListItem.h
@@ -22,6 +22,7 @@
#include "commons/Exception.h"
#include <map>
+#include <memory>
#include <utility>
#include <vector>
@@ -110,7 +111,7 @@ namespace XBMCAddon
static inline AddonClass::Ref<ListItem> fromString(const String& str)
{
AddonClass::Ref<ListItem> ret = AddonClass::Ref<ListItem>(new ListItem());
- ret->item.reset(new CFileItem(str));
+ ret->item = std::make_shared<CFileItem>(str);
return ret;
}
#endif
@@ -710,6 +711,7 @@ namespace XBMCAddon
/// | set | string (Batman Collection) - name of the collection
/// | setoverview | string (All Batman movies) - overview of the collection
/// | tag | string (cult) or list of strings (["cult", "documentary", "best movies"]) - movie tag
+ /// | videoversion | string (Video version)
/// | imdbnumber | string (tt0110293) - IMDb code
/// | code | string (101) - Production code
/// | aired | string (2008-12-07)
@@ -780,6 +782,7 @@ namespace XBMCAddon
/// Added labels **dbid** (music), **setoverview**, **tag**, **sortepisode**, **sortseason**, **episodeguide**, **showlink**.
/// Extended labels **genre**, **country**, **director**, **studio**, **writer**, **tag**, **credits** to also use a list of strings.
/// @python_v20 Partially deprecated. Use explicit setters in **InfoTagVideo**, **InfoTagMusic**, **InfoTagPicture** or **InfoTagGame** instead.
+ /// @python_v21 Added videoversion infolabel for movies
///
/// **Example:**
/// ~~~~~~~~~~~~~{.py}
diff --git a/xbmc/interfaces/legacy/ModuleXbmc.cpp b/xbmc/interfaces/legacy/ModuleXbmc.cpp
index d2db55df8b..290d575359 100644
--- a/xbmc/interfaces/legacy/ModuleXbmc.cpp
+++ b/xbmc/interfaces/legacy/ModuleXbmc.cpp
@@ -446,11 +446,14 @@ namespace XBMCAddon
else
{
StringUtils::Replace(result, "H", "%H");
+ StringUtils::Replace(result, "hh", "%I");
StringUtils::Replace(result, "h", "%I");
- StringUtils::Replace(result, "mm", "%M");
- StringUtils::Replace(result, "ss", "%S");
- StringUtils::Replace(result, "xx", "%p");
}
+ StringUtils::Replace(result, "mm", "%M");
+ StringUtils::Replace(result, "m", "%M");
+ StringUtils::Replace(result, "ss", "%S");
+ StringUtils::Replace(result, "s", "%S");
+ StringUtils::Replace(result, "xx", "%p");
}
else if (StringUtils::CompareNoCase(id, "meridiem") == 0)
{
diff --git a/xbmc/interfaces/legacy/ModuleXbmc.h b/xbmc/interfaces/legacy/ModuleXbmc.h
index 0720d8a80f..738557a0d1 100644
--- a/xbmc/interfaces/legacy/ModuleXbmc.h
+++ b/xbmc/interfaces/legacy/ModuleXbmc.h
@@ -649,9 +649,11 @@ namespace XBMCAddon
/// @param id string - id of setting to return
/// @return Region setting
///
- /// @note choices are (dateshort, datelong, time, meridiem, tempunit, speedunit)
- /// You can use the above as keywords for arguments.
+ /// @note choices are (dateshort, datelong, time, meridiem, tempunit, speedunit,
+ /// datelongraw, dateshortraw, timeraw)
+ /// You can use the above as keywords for arguments.
///
+ /// @warning an empty string is returned if the provided Id is not supported
///
/// ------------------------------------------------------------------------
///
diff --git a/xbmc/interfaces/legacy/Window.cpp b/xbmc/interfaces/legacy/Window.cpp
index 100803ead3..d26bcde2ed 100644
--- a/xbmc/interfaces/legacy/Window.cpp
+++ b/xbmc/interfaces/legacy/Window.cpp
@@ -550,21 +550,25 @@ namespace XBMCAddon
throw WindowException("Control does not exist in window");
}
+ CGUIMessage msg(GUI_MSG_REMOVE_CONTROL, 0, 0);
+ msg.SetPointer(pControl->pGUIControl);
+ CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, iWindowId, wait);
+
// delete control from vecControls in window object
- std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
+ std::vector<AddonClass::Ref<Control>>::iterator it = vecControls.begin();
while (it != vecControls.end())
{
AddonClass::Ref<Control> control = (*it);
if (control->iControlId == pControl->iControlId)
{
it = vecControls.erase(it);
- } else ++it;
+ }
+ else
+ {
+ ++it;
+ }
}
- CGUIMessage msg(GUI_MSG_REMOVE_CONTROL, 0, 0);
- msg.SetPointer(pControl->pGUIControl);
- CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, iWindowId, wait);
-
// initialize control to zero
pControl->pGUIControl = NULL;
pControl->iControlId = 0;
diff --git a/xbmc/interfaces/python/ContextItemAddonInvoker.cpp b/xbmc/interfaces/python/ContextItemAddonInvoker.cpp
index 734193b15c..be529ffbea 100644
--- a/xbmc/interfaces/python/ContextItemAddonInvoker.cpp
+++ b/xbmc/interfaces/python/ContextItemAddonInvoker.cpp
@@ -12,14 +12,14 @@
#include "interfaces/python/swig.h"
#include "utils/log.h"
+#include <memory>
+
#include <Python.h>
#include <osdefs.h>
-
-CContextItemAddonInvoker::CContextItemAddonInvoker(
- ILanguageInvocationHandler *invocationHandler,
- const CFileItemPtr& item)
- : CAddonPythonInvoker(invocationHandler), m_item(CFileItemPtr(new CFileItem(*item.get())))
+CContextItemAddonInvoker::CContextItemAddonInvoker(ILanguageInvocationHandler* invocationHandler,
+ const CFileItemPtr& item)
+ : CAddonPythonInvoker(invocationHandler), m_item(std::make_shared<CFileItem>(*item.get()))
{
}
diff --git a/xbmc/interfaces/python/typemaps/python.buffer.intm b/xbmc/interfaces/python/typemaps/python.buffer.intm
index f074b2bad2..e47a460688 100644
--- a/xbmc/interfaces/python/typemaps/python.buffer.intm
+++ b/xbmc/interfaces/python/typemaps/python.buffer.intm
@@ -33,4 +33,6 @@
${api}.flip(); // prepare the buffer for reading from
}
else
- throw XBMCAddon::WrongTypeException("argument \"%s\" for \"%s\" must be a string, bytes or a bytearray", "${api}", "${method.@name}"); \ No newline at end of file
+ {
+ throw XBMCAddon::WrongTypeException("argument \"%s\" for \"%s\" must be a string, bytes or a bytearray", "${api}", "${method.@name}");
+ } \ No newline at end of file
diff --git a/xbmc/listproviders/DirectoryProvider.cpp b/xbmc/listproviders/DirectoryProvider.cpp
index d54a8c7fea..2811ec1882 100644
--- a/xbmc/listproviders/DirectoryProvider.cpp
+++ b/xbmc/listproviders/DirectoryProvider.cpp
@@ -12,33 +12,32 @@
#include "FileItem.h"
#include "ServiceBroker.h"
#include "addons/AddonManager.h"
-#include "addons/gui/GUIDialogAddonInfo.h"
#include "favourites/FavouritesService.h"
-#include "favourites/FavouritesURL.h"
#include "filesystem/Directory.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
#include "interfaces/AnnouncementManager.h"
#include "music/MusicThumbLoader.h"
-#include "music/dialogs/GUIDialogMusicInfo.h"
#include "pictures/PictureThumbLoader.h"
#include "pvr/PVRManager.h"
#include "pvr/PVRThumbLoader.h"
-#include "pvr/guilib/PVRGUIActionsUtils.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "utils/ExecString.h"
#include "utils/JobManager.h"
#include "utils/PlayerUtils.h"
#include "utils/SortUtils.h"
+#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/Variant.h"
#include "utils/XMLUtils.h"
+#include "utils/guilib/GUIContentUtils.h"
#include "utils/log.h"
#include "video/VideoInfoTag.h"
#include "video/VideoThumbLoader.h"
-#include "video/dialogs/GUIDialogVideoInfo.h"
+#include "video/VideoUtils.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
#include "video/guilib/VideoSelectActionProcessor.h"
#include <memory>
@@ -467,10 +466,15 @@ bool ExecuteAction(const std::string& execute)
return false;
}
+bool ExecuteAction(const CExecString& execute)
+{
+ return ExecuteAction(execute.GetExecString());
+}
+
class CVideoSelectActionProcessor : public VIDEO::GUILIB::CVideoSelectActionProcessorBase
{
public:
- CVideoSelectActionProcessor(CDirectoryProvider& provider, CFileItem& item)
+ CVideoSelectActionProcessor(CDirectoryProvider& provider, const std::shared_ptr<CFileItem>& item)
: CVideoSelectActionProcessorBase(item), m_provider(provider)
{
}
@@ -479,103 +483,134 @@ protected:
bool OnPlayPartSelected(unsigned int part) override
{
// part numbers are 1-based
- BuildAndExecAction("PlayMedia", StringUtils::Format("playoffset={}", part - 1));
+ ExecuteAction({"PlayMedia", *m_item, StringUtils::Format("playoffset={}", part - 1)});
return true;
}
bool OnResumeSelected() override
{
- BuildAndExecAction("PlayMedia", "resume");
+ ExecuteAction({"PlayMedia", *m_item, "resume"});
return true;
}
bool OnPlaySelected() override
{
- BuildAndExecAction("PlayMedia", "noresume");
+ ExecuteAction({"PlayMedia", *m_item, "noresume"});
return true;
}
bool OnQueueSelected() override
{
- BuildAndExecAction("QueueMedia", "");
+ ExecuteAction({"QueueMedia", *m_item, ""});
return true;
}
bool OnInfoSelected() override
{
- m_provider.OnInfo(std::make_shared<CFileItem>(m_item));
+ m_provider.OnInfo(m_item);
return true;
}
bool OnMoreSelected() override
{
- m_provider.OnContextMenu(std::make_shared<CFileItem>(m_item));
+ m_provider.OnContextMenu(m_item);
return true;
}
private:
- void BuildAndExecAction(const std::string& method, const std::string& param)
+ CDirectoryProvider& m_provider;
+};
+
+class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
+{
+public:
+ explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item)
+ : CVideoPlayActionProcessorBase(item)
{
- std::vector<std::string> params{StringUtils::Paramify(m_item.GetPath())};
- if (!param.empty())
- params.emplace_back(param);
+ }
- const CExecString es{method, params};
- ExecuteAction(es.GetExecString());
+protected:
+ bool OnResumeSelected() override
+ {
+ ExecuteAction({"PlayMedia", *m_item, "resume"});
+ return true;
}
- CDirectoryProvider& m_provider;
+ bool OnPlaySelected() override
+ {
+ ExecuteAction({"PlayMedia", *m_item, "noresume"});
+ return true;
+ }
};
} // namespace
bool CDirectoryProvider::OnClick(const CGUIListItemPtr& item)
{
- CFileItem fileItem(*std::static_pointer_cast<CFileItem>(item));
- if (fileItem.HasVideoInfoTag())
+ CFileItem targetItem{*std::static_pointer_cast<CFileItem>(item)};
+
+ if (targetItem.IsFavourite())
{
- CVideoSelectActionProcessor proc{*this, fileItem};
- if (proc.Process())
+ const auto target{CServiceBroker::GetFavouritesService().ResolveFavourite(targetItem)};
+ if (!target)
+ return false;
+
+ targetItem = *target;
+ }
+
+ const CExecString exec{targetItem, GetTarget(targetItem)};
+ const bool isPlayMedia{exec.GetFunction() == "playmedia"};
+
+ // video select action setting is for files only, except exec func is playmedia...
+ if (targetItem.HasVideoInfoTag() && (!targetItem.m_bIsFolder || isPlayMedia))
+ {
+ CVideoSelectActionProcessor proc{*this, std::make_shared<CFileItem>(targetItem)};
+ if (proc.ProcessDefaultAction())
return true;
}
+ // exec the execute string for the original (!) item
+ CFileItem fileItem{*std::static_pointer_cast<CFileItem>(item)};
+
if (fileItem.HasProperty("node.target_url"))
fileItem.SetPath(fileItem.GetProperty("node.target_url").asString());
- // grab and execute the execute string
- return ExecuteAction(CExecString(fileItem, GetTarget(fileItem)).GetExecString());
+ return ExecuteAction({fileItem, GetTarget(fileItem)});
}
bool CDirectoryProvider::OnPlay(const CGUIListItemPtr& item)
{
- CFileItem fileItem(*std::static_pointer_cast<CFileItem>(item));
+ CFileItem targetItem{*std::static_pointer_cast<CFileItem>(item)};
- if (fileItem.IsFavourite())
+ if (targetItem.IsFavourite())
{
- // Resolve the favourite
- const CFavouritesURL url(fileItem.GetPath());
- if (url.IsValid())
- {
- // If action is playmedia, just play it
- if (url.GetAction() == CFavouritesURL::Action::PLAY_MEDIA)
- return ExecuteAction(url.GetExecString());
+ const auto target{CServiceBroker::GetFavouritesService().ResolveFavourite(targetItem)};
+ if (!target)
+ return false;
- CFileItem targetItem(url.GetTarget(), url.IsDir());
- fileItem = targetItem;
- }
+ targetItem = *target;
+ }
+
+ // video play action setting is for files and folders...
+ if (targetItem.HasVideoInfoTag() ||
+ (targetItem.m_bIsFolder && VIDEO_UTILS::IsItemPlayable(targetItem)))
+ {
+ CVideoPlayActionProcessor proc{std::make_shared<CFileItem>(targetItem)};
+ if (proc.ProcessDefaultAction())
+ return true;
}
- if (CPlayerUtils::IsItemPlayable(fileItem))
+ if (CPlayerUtils::IsItemPlayable(targetItem))
{
- CExecString exec(fileItem, {});
+ const CExecString exec{targetItem, GetTarget(targetItem)};
if (exec.GetFunction() == "playmedia")
{
- return ExecuteAction(exec.GetExecString());
+ // exec as is
+ return ExecuteAction(exec);
}
else
{
- // build and execute a playmedia execute string
- exec = CExecString("PlayMedia", {StringUtils::Paramify(fileItem.GetPath())});
- return ExecuteAction(exec.GetExecString());
+ // build a playmedia execute string for given target and exec this
+ return ExecuteAction({"PlayMedia", targetItem, ""});
}
}
return true;
@@ -583,32 +618,11 @@ bool CDirectoryProvider::OnPlay(const CGUIListItemPtr& item)
bool CDirectoryProvider::OnInfo(const std::shared_ptr<CFileItem>& fileItem)
{
- if (fileItem->HasAddonInfo())
- {
- return CGUIDialogAddonInfo::ShowForItem(fileItem);
- }
- else if (fileItem->IsPVR())
- {
- return CServiceBroker::GetPVRManager().Get<PVR::GUI::Utils>().OnInfo(*fileItem);
- }
- else if (fileItem->HasVideoInfoTag())
- {
- auto mediaType = fileItem->GetVideoInfoTag()->m_type;
- if (mediaType == MediaTypeMovie || mediaType == MediaTypeTvShow ||
- mediaType == MediaTypeSeason || mediaType == MediaTypeEpisode ||
- mediaType == MediaTypeVideo || mediaType == MediaTypeVideoCollection ||
- mediaType == MediaTypeMusicVideo)
- {
- CGUIDialogVideoInfo::ShowFor(*fileItem);
- return true;
- }
- }
- else if (fileItem->HasMusicInfoTag())
- {
- CGUIDialogMusicInfo::ShowFor(fileItem.get());
- return true;
- }
- return false;
+ const auto targetItem{fileItem->IsFavourite()
+ ? CServiceBroker::GetFavouritesService().ResolveFavourite(*fileItem)
+ : fileItem};
+
+ return UTILS::GUILIB::CGUIContentUtils::ShowInfoForItem(*targetItem);
}
bool CDirectoryProvider::OnInfo(const CGUIListItemPtr& item)
@@ -623,7 +637,7 @@ bool CDirectoryProvider::OnContextMenu(const std::shared_ptr<CFileItem>& fileIte
if (!target.empty())
fileItem->SetProperty("targetwindow", target);
- return CONTEXTMENU::ShowFor(fileItem);
+ return CONTEXTMENU::ShowFor(fileItem, CContextMenuManager::MAIN);
}
bool CDirectoryProvider::OnContextMenu(const CGUIListItemPtr& item)
diff --git a/xbmc/media/MediaType.cpp b/xbmc/media/MediaType.cpp
index 86484fe038..8318aa55d1 100644
--- a/xbmc/media/MediaType.cpp
+++ b/xbmc/media/MediaType.cpp
@@ -29,6 +29,7 @@ static std::map<std::string, CMediaTypes::MediaTypeInfo> fillDefaultMediaTypes()
mediaTypes.insert(std::make_pair(MediaTypeTvShow, CMediaTypes::MediaTypeInfo(MediaTypeTvShow, MediaTypeTvShow "s", true, 36902, 36903, 36902, 36903)));
mediaTypes.insert(std::make_pair(MediaTypeSeason, CMediaTypes::MediaTypeInfo(MediaTypeSeason, MediaTypeSeason "s", true, 36904, 36905, 20373, 33054)));
mediaTypes.insert(std::make_pair(MediaTypeEpisode, CMediaTypes::MediaTypeInfo(MediaTypeEpisode, MediaTypeEpisode "s", false, 36906, 36907, 20359, 20360)));
+ mediaTypes.insert(std::make_pair(MediaTypeVideoVersion, CMediaTypes::MediaTypeInfo(MediaTypeVideoVersion, MediaTypeVideoVersion "s", false, 40010, 40011, 40012, 40013)));
// clang-format on
return mediaTypes;
diff --git a/xbmc/media/MediaType.h b/xbmc/media/MediaType.h
index a237720ff3..2fe00acef4 100644
--- a/xbmc/media/MediaType.h
+++ b/xbmc/media/MediaType.h
@@ -25,6 +25,14 @@ using MediaType = std::string;
#define MediaTypeTvShow "tvshow"
#define MediaTypeSeason "season"
#define MediaTypeEpisode "episode"
+#define MediaTypeVideoVersion "videoversion"
+
+constexpr const char* MediaTypeVideoCollections = "sets";
+constexpr const char* MediaTypeMusicVideos = "musicvideos";
+constexpr const char* MediaTypeMovies = "movies";
+constexpr const char* MediaTypeTvShows = "tvshows";
+constexpr const char* MediaTypeSeasons = "seasons";
+constexpr const char* MediaTypeEpisodes = "episodes";
class CMediaTypes
{
diff --git a/xbmc/messaging/ApplicationMessenger.cpp b/xbmc/messaging/ApplicationMessenger.cpp
index dfacac9e1e..d9eab6147a 100644
--- a/xbmc/messaging/ApplicationMessenger.cpp
+++ b/xbmc/messaging/ApplicationMessenger.cpp
@@ -96,7 +96,7 @@ int CApplicationMessenger::SendMsg(ThreadMessage&& message, bool wait)
// forever!
if (m_guiThreadId != CThread::GetCurrentThreadId())
{
- message.waitEvent.reset(new CEvent(true));
+ message.waitEvent = std::make_shared<CEvent>(true);
waitEvent = message.waitEvent;
result = message.result;
}
diff --git a/xbmc/music/ContextMenus.cpp b/xbmc/music/ContextMenus.cpp
index 76c5c69334..9d146083bc 100644
--- a/xbmc/music/ContextMenus.cpp
+++ b/xbmc/music/ContextMenus.cpp
@@ -11,6 +11,7 @@
#include "FileItem.h"
#include "GUIUserMessages.h"
#include "ServiceBroker.h"
+#include "cores/playercorefactory/PlayerCoreFactory.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIWindowManager.h"
#include "music/MusicUtils.h"
@@ -43,24 +44,26 @@ bool CMusicInfo::Execute(const std::shared_ptr<CFileItem>& item) const
bool CMusicBrowse::IsVisible(const CFileItem& item) const
{
- if (item.IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
- return false; // handled by CMediaWindow
-
- return item.m_bIsFolder && MUSIC_UTILS::IsItemPlayable(item);
+ return ((item.m_bIsFolder || item.IsFileFolder(EFILEFOLDER_MASK_ONBROWSE)) &&
+ MUSIC_UTILS::IsItemPlayable(item));
}
bool CMusicBrowse::Execute(const std::shared_ptr<CFileItem>& item) const
{
+ // For file directory browsing, we need item's dyn path, for everything else the path.
+ const std::string path{item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE) ? item->GetDynPath()
+ : item->GetPath()};
+
auto& windowMgr = CServiceBroker::GetGUI()->GetWindowManager();
if (windowMgr.GetActiveWindow() == WINDOW_MUSIC_NAV)
{
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, WINDOW_MUSIC_NAV, 0, GUI_MSG_UPDATE);
- msg.SetStringParam(item->GetPath());
+ msg.SetStringParam(path);
windowMgr.SendMessage(msg);
}
else
{
- windowMgr.ActivateWindow(WINDOW_MUSIC_NAV, {item->GetPath(), "return"});
+ windowMgr.ActivateWindow(WINDOW_MUSIC_NAV, {path, "return"});
}
return true;
}
@@ -70,20 +73,67 @@ bool CMusicPlay::IsVisible(const CFileItem& item) const
return MUSIC_UTILS::IsItemPlayable(item);
}
-bool CMusicPlay::Execute(const std::shared_ptr<CFileItem>& item) const
+namespace
+{
+void Play(const std::shared_ptr<CFileItem>& item, const std::string& player)
{
item->SetProperty("playlist_type_hint", PLAYLIST::TYPE_MUSIC);
const ContentUtils::PlayMode mode = item->GetProperty("CheckAutoPlayNextItem").asBoolean()
? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
: ContentUtils::PlayMode::PLAY_ONLY_THIS;
- MUSIC_UTILS::PlayItem(item, mode);
+ MUSIC_UTILS::PlayItem(item, player, mode);
+}
+
+std::vector<std::string> GetPlayers(const CPlayerCoreFactory& playerCoreFactory,
+ const CFileItem& item)
+{
+ std::vector<std::string> players;
+ playerCoreFactory.GetPlayers(item, players);
+ return players;
+}
+
+bool CanQueue(const CFileItem& item)
+{
+ if (!item.CanQueue())
+ return false;
+
+ const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
+ if (windowId == WINDOW_MUSIC_PLAYLIST)
+ return false; // Already queued
+
return true;
}
+} // unnamed namespace
+
+bool CMusicPlay::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ Play(item, "");
+ return true;
+}
+
+bool CMusicPlayUsing::IsVisible(const CFileItem& item) const
+{
+ const CPlayerCoreFactory& playerCoreFactory{CServiceBroker::GetPlayerCoreFactory()};
+ return (GetPlayers(playerCoreFactory, item).size() > 1) && MUSIC_UTILS::IsItemPlayable(item);
+}
+
+bool CMusicPlayUsing::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ const CPlayerCoreFactory& playerCoreFactory{CServiceBroker::GetPlayerCoreFactory()};
+ const std::vector<std::string> players{GetPlayers(playerCoreFactory, *item)};
+ const std::string player{playerCoreFactory.SelectPlayerDialog(players)};
+ if (!player.empty())
+ {
+ Play(item, player);
+ return true;
+ }
+ return false;
+}
bool CMusicPlayNext::IsVisible(const CFileItem& item) const
{
- if (!item.CanQueue())
+ if (!CanQueue(item))
return false;
return MUSIC_UTILS::IsItemPlayable(item);
@@ -97,7 +147,7 @@ bool CMusicPlayNext::Execute(const std::shared_ptr<CFileItem>& item) const
bool CMusicQueue::IsVisible(const CFileItem& item) const
{
- if (!item.CanQueue())
+ if (!CanQueue(item))
return false;
return MUSIC_UTILS::IsItemPlayable(item);
diff --git a/xbmc/music/ContextMenus.h b/xbmc/music/ContextMenus.h
index 73275923fc..d96da33213 100644
--- a/xbmc/music/ContextMenus.h
+++ b/xbmc/music/ContextMenus.h
@@ -57,6 +57,13 @@ struct CMusicPlay : CStaticContextMenuAction
bool Execute(const std::shared_ptr<CFileItem>& item) const override;
};
+struct CMusicPlayUsing : CStaticContextMenuAction
+{
+ CMusicPlayUsing() : CStaticContextMenuAction(15213) {} // Play using...
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& _item) const override;
+};
+
struct CMusicPlayNext : CStaticContextMenuAction
{
CMusicPlayNext() : CStaticContextMenuAction(10008) {} // Play next
diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp
index dd54a6379a..171e5a4e8d 100644
--- a/xbmc/music/MusicDatabase.cpp
+++ b/xbmc/music/MusicDatabase.cpp
@@ -12021,7 +12021,8 @@ void CMusicDatabase::ExportToXML(const CLibExportSettings& settings,
CLog::Log(LOGERROR, "CMusicDatabase::{}: Album nfo export failed! ('{}')",
__FUNCTION__, nfoFile);
CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
- g_localizeStrings.Get(20302), nfoFile);
+ g_localizeStrings.Get(20302),
+ CURL::GetRedacted(nfoFile));
iFailCount++;
}
}
@@ -12165,7 +12166,8 @@ void CMusicDatabase::ExportToXML(const CLibExportSettings& settings,
CLog::Log(LOGERROR, "CMusicDatabase::{}: Artist nfo export failed! ('{}')",
__FUNCTION__, nfoFile);
CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
- g_localizeStrings.Get(20302), nfoFile);
+ g_localizeStrings.Get(20302),
+ CURL::GetRedacted(nfoFile));
iFailCount++;
}
}
diff --git a/xbmc/music/MusicUtils.cpp b/xbmc/music/MusicUtils.cpp
index f3318996c6..77e2d04989 100644
--- a/xbmc/music/MusicUtils.cpp
+++ b/xbmc/music/MusicUtils.cpp
@@ -42,6 +42,8 @@
#include "utils/log.h"
#include "view/GUIViewState.h"
+#include <memory>
+
using namespace MUSIC_INFO;
using namespace XFILE;
using namespace std::chrono_literals;
@@ -155,7 +157,7 @@ public:
const auto appPlayer = components.GetComponent<CApplicationPlayer>();
if (appPlayer->IsPlayingAudio() && g_application.CurrentFileItem().HasMusicInfoTag())
{
- CFileItemPtr songitem = CFileItemPtr(new CFileItem(g_application.CurrentFileItem()));
+ CFileItemPtr songitem = std::make_shared<CFileItem>(g_application.CurrentFileItem());
if (HasSongExtraArtChanged(songitem, type, itemID, db))
g_application.UpdateCurrentPlayArt();
}
@@ -653,7 +655,8 @@ std::string GetMusicDbItemPath(const CFileItem& item)
}
void AddItemToPlayListAndPlay(const std::shared_ptr<CFileItem>& itemToQueue,
- const std::shared_ptr<CFileItem>& itemToPlay)
+ const std::shared_ptr<CFileItem>& itemToPlay,
+ const std::string& player)
{
// recursively add items to list
CFileItemList queuedItems;
@@ -685,7 +688,7 @@ void AddItemToPlayListAndPlay(const std::shared_ptr<CFileItem>& itemToQueue,
}
playlistPlayer.SetCurrentPlaylist(PLAYLIST::TYPE_MUSIC);
- playlistPlayer.Play(pos, "");
+ playlistPlayer.Play(pos, player);
}
} // unnamed namespace
@@ -702,6 +705,7 @@ bool IsAutoPlayNextItem(const CFileItem& item)
}
void PlayItem(const std::shared_ptr<CFileItem>& itemIn,
+ const std::string& player,
ContentUtils::PlayMode mode /* = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM */)
{
auto item = itemIn;
@@ -717,7 +721,7 @@ void PlayItem(const std::shared_ptr<CFileItem>& itemIn,
if (item->m_bIsFolder)
{
- AddItemToPlayListAndPlay(item, nullptr);
+ AddItemToPlayListAndPlay(item, nullptr, player);
}
else if (item->HasMusicInfoTag())
{
@@ -746,7 +750,7 @@ void PlayItem(const std::shared_ptr<CFileItem>& itemIn,
if (item->GetStartOffset() == STARTOFFSET_RESUME)
parentItem->SetStartOffset(STARTOFFSET_RESUME);
- AddItemToPlayListAndPlay(parentItem, item);
+ AddItemToPlayListAndPlay(parentItem, item, player);
}
else // mode == PlayMode::PLAY_ONLY_THIS
{
@@ -754,7 +758,7 @@ void PlayItem(const std::shared_ptr<CFileItem>& itemIn,
auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
playlistPlayer.Reset();
playlistPlayer.SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
- playlistPlayer.Play(item, "");
+ playlistPlayer.Play(item, player);
}
}
}
@@ -849,6 +853,19 @@ bool GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList&
true); // can be cancelled
}
+namespace
+{
+bool IsNonExistingUserPartyModePlaylist(const CFileItem& item)
+{
+ if (!item.IsSmartPlayList())
+ return false;
+
+ const std::string& path{item.GetPath()};
+ const auto profileManager{CServiceBroker::GetSettingsComponent()->GetProfileManager()};
+ return ((profileManager->GetUserDataItem("PartyMode.xsp") == path) && !CFileUtils::Exists(path));
+}
+} // unnamed namespace
+
bool IsItemPlayable(const CFileItem& item)
{
// Exclude all parent folders
@@ -868,10 +885,6 @@ bool IsItemPlayable(const CFileItem& item)
StringUtils::StartsWithNoCase(item.GetPath(), "newplaylist://"))
return false;
- // Exclude unwanted windows
- if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_MUSIC_PLAYLIST)
- return false;
-
// Include playlists located at one of the possible music playlist locations
if (item.IsPlayList())
{
@@ -889,13 +902,16 @@ bool IsItemPlayable(const CFileItem& item)
if (StringUtils::StartsWith(item.GetPath(), StringUtils::Format("{}/music/", path)))
return true;
- if (!item.m_bIsFolder)
+ if (!item.m_bIsFolder && !item.HasMusicInfoTag())
{
// Unknown location. Type cannot be determined for non-folder items.
return false;
}
}
+ if (IsNonExistingUserPartyModePlaylist(item))
+ return false;
+
if (item.m_bIsFolder &&
(item.IsMusicDb() || StringUtils::StartsWithNoCase(item.GetPath(), "library://music/")))
{
diff --git a/xbmc/music/MusicUtils.h b/xbmc/music/MusicUtils.h
index 5505b61c6e..cd28ff5f27 100644
--- a/xbmc/music/MusicUtils.h
+++ b/xbmc/music/MusicUtils.h
@@ -91,9 +91,11 @@ bool IsAutoPlayNextItem(const CFileItem& item);
all items contained in the folder and start playback of the playlist. If item is a single music
item, start playback directly, without adding it to the music playlist first.
\param item [in] the item to play
+ \param player [in] the player to use, empty for default player
\param mode [in] queue all successors and play them after item
*/
void PlayItem(const std::shared_ptr<CFileItem>& item,
+ const std::string& player,
ContentUtils::PlayMode mode = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM);
enum class QueuePosition
diff --git a/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp b/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp
index a9e223244f..a443f99731 100644
--- a/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp
+++ b/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp
@@ -408,15 +408,15 @@ void CGUIDialogInfoProviderSettings::InitializeSettings()
entries.clear();
if (m_singleScraperType == CONTENT_ALBUMS)
{
- entries.push_back(TranslatableIntegerSettingOption(38066, INFOPROVIDER_THISITEM));
- entries.push_back(TranslatableIntegerSettingOption(38067, INFOPROVIDER_ALLVIEW));
+ entries.emplace_back(38066, INFOPROVIDER_THISITEM);
+ entries.emplace_back(38067, INFOPROVIDER_ALLVIEW);
}
else
{
- entries.push_back(TranslatableIntegerSettingOption(38064, INFOPROVIDER_THISITEM));
- entries.push_back(TranslatableIntegerSettingOption(38065, INFOPROVIDER_ALLVIEW));
+ entries.emplace_back(38064, INFOPROVIDER_THISITEM);
+ entries.emplace_back(38065, INFOPROVIDER_ALLVIEW);
}
- entries.push_back(TranslatableIntegerSettingOption(38063, INFOPROVIDER_DEFAULT));
+ entries.emplace_back(38063, INFOPROVIDER_DEFAULT);
AddList(group1, SETTING_APPLYTOITEMS, 38338, SettingLevel::Basic, m_applyToItems, entries, 38339); // "Apply settings to"
}
diff --git a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
index a4b282a563..a7dca1fc3e 100644
--- a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
+++ b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
@@ -1039,5 +1039,5 @@ void CGUIDialogMusicInfo::ShowFor(CFileItem* pItem)
void CGUIDialogMusicInfo::OnPlayItem(const std::shared_ptr<CFileItem>& item)
{
Close(true);
- MUSIC_UTILS::PlayItem(item);
+ MUSIC_UTILS::PlayItem(item, "");
}
diff --git a/xbmc/music/dialogs/GUIDialogSongInfo.cpp b/xbmc/music/dialogs/GUIDialogSongInfo.cpp
index 8efca94ab4..70d02f96e8 100644
--- a/xbmc/music/dialogs/GUIDialogSongInfo.cpp
+++ b/xbmc/music/dialogs/GUIDialogSongInfo.cpp
@@ -519,5 +519,5 @@ void CGUIDialogSongInfo::ShowFor(CFileItem* pItem)
void CGUIDialogSongInfo::OnPlaySong(const std::shared_ptr<CFileItem>& item)
{
Close(true);
- MUSIC_UTILS::PlayItem(item);
+ MUSIC_UTILS::PlayItem(item, "");
}
diff --git a/xbmc/music/windows/GUIWindowMusicBase.cpp b/xbmc/music/windows/GUIWindowMusicBase.cpp
index fc893beed3..9dc4e2dddd 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.cpp
+++ b/xbmc/music/windows/GUIWindowMusicBase.cpp
@@ -8,34 +8,24 @@
#include "GUIWindowMusicBase.h"
+#include "Autorun.h"
+#include "FileItem.h"
+#include "GUIInfoManager.h"
+#include "GUIPassword.h"
#include "GUIUserMessages.h"
+#include "PartyModeManager.h"
#include "PlayListPlayer.h"
#include "ServiceBroker.h"
+#include "URL.h"
#include "Util.h"
+#include "addons/gui/GUIDialogAddonInfo.h"
#include "application/Application.h"
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
-#include "dialogs/GUIDialogMediaSource.h"
-#include "input/actions/Action.h"
-#include "input/actions/ActionIDs.h"
-#include "music/MusicDbUrl.h"
-#include "music/MusicLibraryQueue.h"
-#include "music/MusicUtils.h"
-#include "music/dialogs/GUIDialogInfoProviderSettings.h"
-#include "music/dialogs/GUIDialogMusicInfo.h"
-#include "playlists/PlayList.h"
-#include "playlists/PlayListFactory.h"
#ifdef HAS_CDDA_RIPPER
#include "cdrip/CDDARipper.h"
#endif
-#include "Autorun.h"
-#include "FileItem.h"
-#include "GUIInfoManager.h"
-#include "GUIPassword.h"
-#include "PartyModeManager.h"
-#include "URL.h"
-#include "addons/gui/GUIDialogAddonInfo.h"
-#include "cores/playercorefactory/PlayerCoreFactory.h"
+#include "dialogs/GUIDialogMediaSource.h"
#include "dialogs/GUIDialogProgress.h"
#include "dialogs/GUIDialogSmartPlaylistEditor.h"
#include "dialogs/GUIDialogYesNo.h"
@@ -45,10 +35,19 @@
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
#include "guilib/guiinfo/GUIInfoLabels.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
#include "messaging/helpers/DialogHelper.h"
#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicDbUrl.h"
+#include "music/MusicLibraryQueue.h"
+#include "music/MusicUtils.h"
+#include "music/dialogs/GUIDialogInfoProviderSettings.h"
+#include "music/dialogs/GUIDialogMusicInfo.h"
#include "music/infoscanner/MusicInfoScanner.h"
#include "music/tags/MusicInfoTag.h"
+#include "playlists/PlayList.h"
+#include "playlists/PlayListFactory.h"
#include "profiles/ProfileManager.h"
#include "settings/AdvancedSettings.h"
#include "settings/MediaSourceSettings.h"
@@ -66,6 +65,7 @@
#include "view/GUIViewState.h"
#include <algorithm>
+#include <memory>
using namespace XFILE;
using namespace MUSICDATABASEDIRECTORY;
@@ -442,19 +442,6 @@ void CGUIWindowMusicBase::GetContextButtons(int itemNumber, CContextButtons &but
//! @todo get rid of IsAddonsPath and IsScript check. CanQueue should be enough!
if (item->CanQueue() && !item->IsAddonsPath() && !item->IsScript())
{
- if (!item->m_bIsFolder &&
- (!item->IsPlayList() ||
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders))
- {
- const CPlayerCoreFactory& playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- // check what players we have, if we have multiple display play with option
- std::vector<std::string> players;
- playerCoreFactory.GetPlayers(*item, players);
- if (players.size() >= 1)
- buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213); // Play With...
- }
-
if (item->IsSmartPlayList())
buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
@@ -527,18 +514,6 @@ bool CGUIWindowMusicBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
return true;
}
- case CONTEXT_BUTTON_PLAY_WITH:
- {
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- std::vector<std::string> players;
- playerCoreFactory.GetPlayers(*item, players);
- std::string player = playerCoreFactory.SelectPlayerDialog(players);
- if (!player.empty())
- OnClick(itemNumber, player);
- return true;
- }
-
case CONTEXT_BUTTON_PLAY_PARTYMODE:
g_partyModeManager.Enable(PARTYMODECONTEXT_MUSIC, item->GetPath());
return true;
@@ -868,7 +843,7 @@ bool CGUIWindowMusicBase::GetDirectory(const std::string &strDirectory, CFileIte
newPlaylist->m_bIsFolder = true;
items.Add(newPlaylist);
- newPlaylist.reset(new CFileItem("newplaylist://", false));
+ newPlaylist = std::make_shared<CFileItem>("newplaylist://", false);
newPlaylist->SetLabel(g_localizeStrings.Get(525));
newPlaylist->SetArt("icon", "DefaultAddSource.png");
newPlaylist->SetLabelPreformatted(true);
@@ -876,7 +851,7 @@ bool CGUIWindowMusicBase::GetDirectory(const std::string &strDirectory, CFileIte
newPlaylist->SetCanQueue(false);
items.Add(newPlaylist);
- newPlaylist.reset(new CFileItem("newsmartplaylist://music", false));
+ newPlaylist = std::make_shared<CFileItem>("newsmartplaylist://music", false);
newPlaylist->SetLabel(g_localizeStrings.Get(21437));
newPlaylist->SetArt("icon", "DefaultAddSource.png");
newPlaylist->SetLabelPreformatted(true);
diff --git a/xbmc/music/windows/GUIWindowMusicPlaylist.cpp b/xbmc/music/windows/GUIWindowMusicPlaylist.cpp
index fba83f4e8f..f3ecb72847 100644
--- a/xbmc/music/windows/GUIWindowMusicPlaylist.cpp
+++ b/xbmc/music/windows/GUIWindowMusicPlaylist.cpp
@@ -17,7 +17,6 @@
#include "application/Application.h"
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
-#include "cores/playercorefactory/PlayerCoreFactory.h"
#include "dialogs/GUIDialogSmartPlaylistEditor.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIKeyboardFactory.h"
@@ -38,22 +37,22 @@
#include "utils/log.h"
#include "view/GUIViewState.h"
-#define CONTROL_BTNVIEWASICONS 2
-#define CONTROL_BTNSORTBY 3
-#define CONTROL_BTNSORTASC 4
-#define CONTROL_LABELFILES 12
+#define CONTROL_BTNVIEWASICONS 2
+#define CONTROL_BTNSORTBY 3
+#define CONTROL_BTNSORTASC 4
+#define CONTROL_LABELFILES 12
-#define CONTROL_BTNSHUFFLE 20
-#define CONTROL_BTNSAVE 21
-#define CONTROL_BTNCLEAR 22
+#define CONTROL_BTNSHUFFLE 20
+#define CONTROL_BTNSAVE 21
+#define CONTROL_BTNCLEAR 22
-#define CONTROL_BTNPLAY 23
-#define CONTROL_BTNNEXT 24
-#define CONTROL_BTNPREVIOUS 25
-#define CONTROL_BTNREPEAT 26
+#define CONTROL_BTNPLAY 23
+#define CONTROL_BTNNEXT 24
+#define CONTROL_BTNPREVIOUS 25
+#define CONTROL_BTNREPEAT 26
CGUIWindowMusicPlayList::CGUIWindowMusicPlayList(void)
- : CGUIWindowMusicBase(WINDOW_MUSIC_PLAYLIST, "MyPlaylist.xml")
+ : CGUIWindowMusicBase(WINDOW_MUSIC_PLAYLIST, "MyPlaylist.xml")
{
m_musicInfoLoader.SetObserver(this);
m_movingFrom = -1;
@@ -63,16 +62,16 @@ CGUIWindowMusicPlayList::~CGUIWindowMusicPlayList(void) = default;
bool CGUIWindowMusicPlayList::OnMessage(CGUIMessage& message)
{
- switch ( message.GetMessage() )
+ switch (message.GetMessage())
{
- case GUI_MSG_PLAYLISTPLAYER_REPEAT:
+ case GUI_MSG_PLAYLISTPLAYER_REPEAT:
{
UpdateButtons();
}
break;
- case GUI_MSG_PLAYLISTPLAYER_RANDOM:
- case GUI_MSG_PLAYLIST_CHANGED:
+ case GUI_MSG_PLAYLISTPLAYER_RANDOM:
+ case GUI_MSG_PLAYLIST_CHANGED:
{
// global playlist changed outside playlist window
UpdateButtons();
@@ -91,11 +90,10 @@ bool CGUIWindowMusicPlayList::OnMessage(CGUIMessage& message)
m_iLastControl = CONTROL_BTNVIEWASICONS;
SET_CONTROL_FOCUS(m_iLastControl, 0);
}
-
}
break;
- case GUI_MSG_WINDOW_DEINIT:
+ case GUI_MSG_WINDOW_DEINIT:
{
if (m_musicInfoLoader.IsLoading())
m_musicInfoLoader.StopThread();
@@ -104,7 +102,7 @@ bool CGUIWindowMusicPlayList::OnMessage(CGUIMessage& message)
}
break;
- case GUI_MSG_WINDOW_INIT:
+ case GUI_MSG_WINDOW_INIT:
{
// Setup item cache for tagloader
m_musicInfoLoader.UseCacheOnHD("special://temp/archive_cache/MusicPlaylist.fi");
@@ -135,7 +133,7 @@ bool CGUIWindowMusicPlayList::OnMessage(CGUIMessage& message)
}
break;
- case GUI_MSG_CLICKED:
+ case GUI_MSG_CLICKED:
{
int iControl = message.GetSenderId();
if (iControl == CONTROL_BTNSHUFFLE)
@@ -219,12 +217,11 @@ bool CGUIWindowMusicPlayList::OnMessage(CGUIMessage& message)
}
}
break;
-
}
return CGUIWindowMusicBase::OnMessage(message);
}
-bool CGUIWindowMusicPlayList::OnAction(const CAction &action)
+bool CGUIWindowMusicPlayList::OnAction(const CAction& action)
{
if (action.GetID() == ACTION_PARENT_DIR)
{
@@ -257,7 +254,9 @@ bool CGUIWindowMusicPlayList::OnBack(int actionID)
return CGUIWindowMusicBase::OnBack(actionID);
}
-bool CGUIWindowMusicPlayList::MoveCurrentPlayListItem(int iItem, int iAction, bool bUpdate /* = true */)
+bool CGUIWindowMusicPlayList::MoveCurrentPlayListItem(int iItem,
+ int iAction,
+ bool bUpdate /* = true */)
{
int iSelected = iItem;
int iNew = iSelected;
@@ -303,14 +302,16 @@ bool CGUIWindowMusicPlayList::MoveCurrentPlayListItem(int iItem, int iAction, bo
void CGUIWindowMusicPlayList::SavePlayList()
{
std::string strNewFileName;
- if (CGUIKeyboardFactory::ShowAndGetInput(strNewFileName, CVariant{g_localizeStrings.Get(16012)}, false))
+ if (CGUIKeyboardFactory::ShowAndGetInput(strNewFileName, CVariant{g_localizeStrings.Get(16012)},
+ false))
{
+ // need 2 rename it
strNewFileName = CUtil::MakeLegalFileName(std::move(strNewFileName));
strNewFileName += ".m3u8";
- std::string strPath = URIUtils::AddFileToFolder(
- CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH),
- "music",
- strNewFileName);
+ std::string strPath =
+ URIUtils::AddFileToFolder(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_SYSTEM_PLAYLISTSPATH),
+ "music", strNewFileName);
// get selected item
int iItem = m_viewControl.GetSelectedItem();
@@ -360,14 +361,15 @@ void CGUIWindowMusicPlayList::ClearPlayList()
void CGUIWindowMusicPlayList::RemovePlayListItem(int iItem)
{
- if (iItem < 0 || iItem > m_vecItems->Size()) return;
+ if (iItem < 0 || iItem > m_vecItems->Size())
+ return;
const auto& components = CServiceBroker::GetAppComponents();
const auto appPlayer = components.GetComponent<CApplicationPlayer>();
// The current playing song can't be removed
if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC &&
appPlayer->IsPlayingAudio() && CServiceBroker::GetPlaylistPlayer().GetCurrentSong() == iItem)
- return ;
+ return;
CServiceBroker::GetPlaylistPlayer().Remove(PLAYLIST::TYPE_MUSIC, iItem);
@@ -450,7 +452,7 @@ void CGUIWindowMusicPlayList::UpdateButtons()
MarkPlaying();
}
-bool CGUIWindowMusicPlayList::OnPlayMedia(int iItem, const std::string &player)
+bool CGUIWindowMusicPlayList::OnPlayMedia(int iItem, const std::string& player)
{
if (g_partyModeManager.IsEnabled())
g_partyModeManager.Play(iItem);
@@ -469,7 +471,7 @@ bool CGUIWindowMusicPlayList::OnPlayMedia(int iItem, const std::string &player)
{
// Reset Playlistplayer, playback started now does
// not use the playlistplayer.
- CFileItemPtr pItem=m_vecItems->Get(iItem);
+ CFileItemPtr pItem = m_vecItems->Get(iItem);
CServiceBroker::GetPlaylistPlayer().Reset();
CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
g_application.PlayFile(*pItem, player);
@@ -483,7 +485,8 @@ void CGUIWindowMusicPlayList::OnItemLoaded(CFileItem* pItem)
{
if (pItem->HasMusicInfoTag() && pItem->GetMusicInfoTag()->Loaded())
{ // set label 1+2 from tags
- const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const std::shared_ptr<CSettings> settings =
+ CServiceBroker::GetSettingsComponent()->GetSettings();
std::string strTrack = settings->GetString(CSettings::SETTING_MUSICFILES_NOWPLAYINGTRACKFORMAT);
if (strTrack.empty())
strTrack = settings->GetString(CSettings::SETTING_MUSICFILES_TRACKFORMAT);
@@ -513,7 +516,8 @@ void CGUIWindowMusicPlayList::OnItemLoaded(CFileItem* pItem)
}
}
-bool CGUIWindowMusicPlayList::Update(const std::string& strDirectory, bool updateFilterPath /* = true */)
+bool CGUIWindowMusicPlayList::Update(const std::string& strDirectory,
+ bool updateFilterPath /* = true */)
{
if (m_musicInfoLoader.IsLoading())
m_musicInfoLoader.StopThread();
@@ -528,7 +532,7 @@ bool CGUIWindowMusicPlayList::Update(const std::string& strDirectory, bool updat
return true;
}
-void CGUIWindowMusicPlayList::GetContextButtons(int itemNumber, CContextButtons &buttons)
+void CGUIWindowMusicPlayList::GetContextButtons(int itemNumber, CContextButtons& buttons)
{
// is this playlist playing?
int itemPlaying = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
@@ -542,21 +546,13 @@ void CGUIWindowMusicPlayList::GetContextButtons(int itemNumber, CContextButtons
{
// we can move the item to any position not where we are, and any position not above currently
// playing item in party mode
- if (itemNumber != m_movingFrom && (!g_partyModeManager.IsEnabled() || itemNumber > itemPlaying))
- buttons.Add(CONTEXT_BUTTON_MOVE_HERE, 13252); // move item here
+ if (itemNumber != m_movingFrom &&
+ (!g_partyModeManager.IsEnabled() || itemNumber > itemPlaying))
+ buttons.Add(CONTEXT_BUTTON_MOVE_HERE, 13252); // move item here
buttons.Add(CONTEXT_BUTTON_CANCEL_MOVE, 13253);
}
else
{
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- // aren't in a move
- // check what players we have, if we have multiple display play with option
- std::vector<std::string> players;
- playerCoreFactory.GetPlayers(*item, players);
- if (players.size() > 1)
- buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213); // Play With...
-
if (itemNumber > (g_partyModeManager.IsEnabled() ? 1 : 0))
buttons.Add(CONTEXT_BUTTON_MOVE_ITEM_UP, 13332);
if (itemNumber + 1 < m_vecItems->Size())
@@ -571,7 +567,7 @@ void CGUIWindowMusicPlayList::GetContextButtons(int itemNumber, CContextButtons
if (g_partyModeManager.IsEnabled())
{
buttons.Add(CONTEXT_BUTTON_EDIT_PARTYMODE, 21439);
- buttons.Add(CONTEXT_BUTTON_CANCEL_PARTYMODE, 588); // cancel party mode
+ buttons.Add(CONTEXT_BUTTON_CANCEL_PARTYMODE, 588); // cancel party mode
}
}
@@ -579,76 +575,60 @@ bool CGUIWindowMusicPlayList::OnContextButton(int itemNumber, CONTEXT_BUTTON but
{
switch (button)
{
- case CONTEXT_BUTTON_PLAY_WITH:
- {
- CFileItemPtr item;
- if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
- item = m_vecItems->Get(itemNumber);
- if (!item)
- break;
-
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- std::vector<std::string> players;
- playerCoreFactory.GetPlayers(*item, players);
- std::string player = playerCoreFactory.SelectPlayerDialog(players);
- if (!player.empty())
- OnClick(itemNumber, player);
+ case CONTEXT_BUTTON_MOVE_ITEM:
+ m_movingFrom = itemNumber;
return true;
- }
- case CONTEXT_BUTTON_MOVE_ITEM:
- m_movingFrom = itemNumber;
- return true;
-
- case CONTEXT_BUTTON_MOVE_HERE:
- MoveItem(m_movingFrom, itemNumber);
- m_movingFrom = -1;
- return true;
- case CONTEXT_BUTTON_CANCEL_MOVE:
- m_movingFrom = -1;
- return true;
+ case CONTEXT_BUTTON_MOVE_HERE:
+ MoveItem(m_movingFrom, itemNumber);
+ m_movingFrom = -1;
+ return true;
- case CONTEXT_BUTTON_MOVE_ITEM_UP:
- OnMove(itemNumber, ACTION_MOVE_ITEM_UP);
- return true;
+ case CONTEXT_BUTTON_CANCEL_MOVE:
+ m_movingFrom = -1;
+ return true;
- case CONTEXT_BUTTON_MOVE_ITEM_DOWN:
- OnMove(itemNumber, ACTION_MOVE_ITEM_DOWN);
- return true;
+ case CONTEXT_BUTTON_MOVE_ITEM_UP:
+ OnMove(itemNumber, ACTION_MOVE_ITEM_UP);
+ return true;
- case CONTEXT_BUTTON_DELETE:
- RemovePlayListItem(itemNumber);
- return true;
+ case CONTEXT_BUTTON_MOVE_ITEM_DOWN:
+ OnMove(itemNumber, ACTION_MOVE_ITEM_DOWN);
+ return true;
- case CONTEXT_BUTTON_CANCEL_PARTYMODE:
- g_partyModeManager.Disable();
- return true;
+ case CONTEXT_BUTTON_DELETE:
+ RemovePlayListItem(itemNumber);
+ return true;
- case CONTEXT_BUTTON_EDIT_PARTYMODE:
- {
- const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+ case CONTEXT_BUTTON_CANCEL_PARTYMODE:
+ g_partyModeManager.Disable();
+ return true;
- std::string playlist = profileManager->GetUserDataItem("PartyMode.xsp");
- if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist))
+ case CONTEXT_BUTTON_EDIT_PARTYMODE:
{
- // apply new rules
- g_partyModeManager.Disable();
- g_partyModeManager.Enable();
+ const std::shared_ptr<CProfileManager> profileManager =
+ CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ std::string playlist = profileManager->GetUserDataItem("PartyMode.xsp");
+ if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist))
+ {
+ // apply new rules
+ g_partyModeManager.Disable();
+ g_partyModeManager.Enable();
+ }
+ return true;
}
- return true;
- }
- default:
- break;
+ default:
+ break;
}
return CGUIWindowMusicBase::OnContextButton(itemNumber, button);
}
-
void CGUIWindowMusicPlayList::OnMove(int iItem, int iAction)
{
- if (iItem < 0 || iItem >= m_vecItems->Size()) return;
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return;
bool bRestart = m_musicInfoLoader.IsLoading();
if (bRestart)
@@ -662,8 +642,10 @@ void CGUIWindowMusicPlayList::OnMove(int iItem, int iAction)
void CGUIWindowMusicPlayList::MoveItem(int iStart, int iDest)
{
- if (iStart < 0 || iStart >= m_vecItems->Size()) return;
- if (iDest < 0 || iDest >= m_vecItems->Size()) return;
+ if (iStart < 0 || iStart >= m_vecItems->Size())
+ return;
+ if (iDest < 0 || iDest >= m_vecItems->Size())
+ return;
// default to move up
int iAction = ACTION_MOVE_ITEM_UP;
@@ -711,4 +693,3 @@ void CGUIWindowMusicPlayList::MarkPlaying()
m_vecItems->Get(iSong)->Select(true);
}*/
}
-
diff --git a/xbmc/music/windows/GUIWindowMusicPlaylistEditor.cpp b/xbmc/music/windows/GUIWindowMusicPlaylistEditor.cpp
index a6ceec2577..abece9c660 100644
--- a/xbmc/music/windows/GUIWindowMusicPlaylistEditor.cpp
+++ b/xbmc/music/windows/GUIWindowMusicPlaylistEditor.cpp
@@ -404,6 +404,9 @@ void CGUIWindowMusicPlaylistEditor::AppendToPlaylist(CFileItemList &newItems)
void CGUIWindowMusicPlaylistEditor::OnSourcesContext()
{
+ static constexpr int CONTEXT_BUTTON_QUEUE_ITEM = 0;
+ static constexpr int CONTEXT_BUTTON_BROWSE_INTO = 1;
+
CFileItemPtr item = GetCurrentListItem();
CContextButtons buttons;
if (item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
@@ -437,18 +440,3 @@ void CGUIWindowMusicPlaylistEditor::OnPlaylistContext()
else if (btnid == CONTEXT_BUTTON_DELETE)
OnDeletePlaylistItem(item);
}
-
-bool CGUIWindowMusicPlaylistEditor::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
-{
- switch (button)
- {
- case CONTEXT_BUTTON_QUEUE_ITEM:
- OnQueueItem(itemNumber);
- return true;
-
- default:
- break;
- }
-
- return CGUIWindowMusicBase::OnContextButton(itemNumber, button);
-}
diff --git a/xbmc/music/windows/GUIWindowMusicPlaylistEditor.h b/xbmc/music/windows/GUIWindowMusicPlaylistEditor.h
index 3859b556e8..8e7185921d 100644
--- a/xbmc/music/windows/GUIWindowMusicPlaylistEditor.h
+++ b/xbmc/music/windows/GUIWindowMusicPlaylistEditor.h
@@ -27,8 +27,7 @@ protected:
bool GetDirectory(const std::string &strDirectory, CFileItemList &items) override;
void UpdateButtons() override;
bool Update(const std::string &strDirectory, bool updateFilterPath = true) override;
- void OnPrepareFileItems(CFileItemList &items) override;
- bool OnContextButton(int itemNumber, CONTEXT_BUTTON button) override;
+ void OnPrepareFileItems(CFileItemList& items) override;
void OnQueueItem(int iItem, bool first = false) override;
std::string GetStartFolder(const std::string& dir) override { return ""; }
diff --git a/xbmc/music/windows/MusicFileItemListModifier.cpp b/xbmc/music/windows/MusicFileItemListModifier.cpp
index c78de793fd..fe04aed423 100644
--- a/xbmc/music/windows/MusicFileItemListModifier.cpp
+++ b/xbmc/music/windows/MusicFileItemListModifier.cpp
@@ -17,6 +17,8 @@
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
+#include <memory>
+
using namespace XFILE::MUSICDATABASEDIRECTORY;
bool CMusicFileItemListModifier::CanModify(const CFileItemList &items) const
@@ -74,7 +76,7 @@ void CMusicFileItemListModifier::AddQueuingFolder(CFileItemList& items)
switch (nodeChildType)
{
case NODE_TYPE_ARTIST:
- pItem.reset(new CFileItem(g_localizeStrings.Get(15103))); // "All Artists"
+ pItem = std::make_shared<CFileItem>(g_localizeStrings.Get(15103)); // "All Artists"
musicUrl.AppendPath("-1/");
pItem->SetPath(musicUrl.ToString());
break;
@@ -84,14 +86,14 @@ void CMusicFileItemListModifier::AddQueuingFolder(CFileItemList& items)
case NODE_TYPE_ALBUM_RECENTLY_PLAYED:
case NODE_TYPE_ALBUM_RECENTLY_ADDED:
case NODE_TYPE_ALBUM_TOP100:
- pItem.reset(new CFileItem(g_localizeStrings.Get(15102))); // "All Albums"
+ pItem = std::make_shared<CFileItem>(g_localizeStrings.Get(15102)); // "All Albums"
musicUrl.AppendPath("-1/");
pItem->SetPath(musicUrl.ToString());
break;
// Disc node
case NODE_TYPE_DISC:
- pItem.reset(new CFileItem(g_localizeStrings.Get(38075))); // "All Discs"
+ pItem = std::make_shared<CFileItem>(g_localizeStrings.Get(38075)); // "All Discs"
musicUrl.AppendPath("-1/");
pItem->SetPath(musicUrl.ToString());
break;
diff --git a/xbmc/network/DNSNameCache.cpp b/xbmc/network/DNSNameCache.cpp
index af2ac1ad1f..63fcfae9ed 100644
--- a/xbmc/network/DNSNameCache.cpp
+++ b/xbmc/network/DNSNameCache.cpp
@@ -24,6 +24,10 @@
#include <netdb.h>
#include <netinet/in.h>
+#if defined(TARGET_FREEBSD)
+#include <sys/socket.h>
+#endif
+
CDNSNameCache g_DNSCache;
CCriticalSection CDNSNameCache::m_critical;
@@ -38,18 +42,19 @@ bool CDNSNameCache::Lookup(const std::string& strHostName, std::string& strIpAdd
return false;
// first see if this is already an ip address
- unsigned long address = inet_addr(strHostName.c_str());
+ in_addr addr4;
+ in6_addr addr6;
strIpAddress.clear();
- if (address != INADDR_NONE)
+ if (inet_pton(AF_INET, strHostName.c_str(), &addr4) ||
+ inet_pton(AF_INET6, strHostName.c_str(), &addr6))
{
- strIpAddress = StringUtils::Format("{}.{}.{}.{}", (address & 0xFF), (address & 0xFF00) >> 8,
- (address & 0xFF0000) >> 16, (address & 0xFF000000) >> 24);
+ strIpAddress = strHostName;
return true;
}
// check if there's a custom entry or if it's already cached
- if(g_DNSCache.GetCached(strHostName, strIpAddress))
+ if (g_DNSCache.GetCached(strHostName, strIpAddress))
return true;
// perform dns lookup
diff --git a/xbmc/network/EventClient.cpp b/xbmc/network/EventClient.cpp
index d3b67dc55e..5570ad9cda 100644
--- a/xbmc/network/EventClient.cpp
+++ b/xbmc/network/EventClient.cpp
@@ -610,7 +610,7 @@ bool CEventClient::OnPacketACTION(CEventPacket *packet)
case AT_BUTTON:
{
std::unique_lock<CCriticalSection> lock(m_critSection);
- m_actionQueue.push(CEventAction(actionString.c_str(), actionType));
+ m_actionQueue.emplace(actionString.c_str(), actionType);
}
break;
diff --git a/xbmc/network/GUIDialogNetworkSetup.cpp b/xbmc/network/GUIDialogNetworkSetup.cpp
index e2fd165f17..0b56e36d10 100644
--- a/xbmc/network/GUIDialogNetworkSetup.cpp
+++ b/xbmc/network/GUIDialogNetworkSetup.cpp
@@ -194,8 +194,7 @@ void CGUIDialogNetworkSetup::InitializeSettings()
// Add our protocols
TranslatableIntegerSettingOptions labels;
for (size_t idx = 0; idx < m_protocols.size(); ++idx)
- labels.push_back(
- TranslatableIntegerSettingOption(m_protocols[idx].label, idx, m_protocols[idx].addonId));
+ labels.emplace_back(m_protocols[idx].label, static_cast<int>(idx), m_protocols[idx].addonId);
AddSpinner(group, SETTING_PROTOCOL, 1008, SettingLevel::Basic, m_protocol, labels);
AddEdit(group, SETTING_SERVER_ADDRESS, 1010, SettingLevel::Basic, m_server, true);
diff --git a/xbmc/network/TCPServer.cpp b/xbmc/network/TCPServer.cpp
index cd055c4ade..75334245b4 100644
--- a/xbmc/network/TCPServer.cpp
+++ b/xbmc/network/TCPServer.cpp
@@ -719,7 +719,8 @@ void CTCPServer::CWebSocketClient::PushBuffer(CTCPServer *host, const char *buff
if (send)
{
for (unsigned int index = 0; index < frames.size(); index++)
- Send(frames.at(index)->GetFrameData(), (unsigned int)frames.at(index)->GetFrameLength());
+ CTCPClient::Send(frames.at(index)->GetFrameData(),
+ static_cast<unsigned int>(frames.at(index)->GetFrameLength()));
}
else
{
diff --git a/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp b/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp
index 3d2dccb275..33ab67ae32 100644
--- a/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp
+++ b/xbmc/network/httprequesthandler/HTTPImageTransformationHandler.cpp
@@ -150,12 +150,13 @@ MHD_RESULT CHTTPImageTransformationHandler::HandleRequest()
// nothing else to do if the request is not ranged
if (!GetRequestedRanges(m_response.totalLength))
{
- m_responseData.push_back(CHttpResponseRange(m_buffer, 0, m_response.totalLength - 1));
+ m_responseData.emplace_back(m_buffer, 0, m_response.totalLength - 1);
return MHD_YES;
}
for (HttpRanges::const_iterator range = m_request.ranges.Begin(); range != m_request.ranges.End(); ++range)
- m_responseData.push_back(CHttpResponseRange(m_buffer + range->GetFirstPosition(), range->GetFirstPosition(), range->GetLastPosition()));
+ m_responseData.emplace_back(m_buffer + range->GetFirstPosition(), range->GetFirstPosition(),
+ range->GetLastPosition());
return MHD_YES;
}
diff --git a/xbmc/network/test/CMakeLists.txt b/xbmc/network/test/CMakeLists.txt
index a323d1835b..05eb260c0b 100644
--- a/xbmc/network/test/CMakeLists.txt
+++ b/xbmc/network/test/CMakeLists.txt
@@ -1,5 +1,7 @@
-if(MICROHTTPD_FOUND)
- set(SOURCES TestWebServer.cpp)
+set(SOURCES TestNetwork.cpp)
- core_add_test_library(network_test)
+if(MICROHTTPD_FOUND)
+ list(APPEND SOURCES TestWebServer.cpp)
endif()
+
+core_add_test_library(network_test)
diff --git a/xbmc/network/test/TestNetwork.cpp b/xbmc/network/test/TestNetwork.cpp
new file mode 100644
index 0000000000..df4e848b11
--- /dev/null
+++ b/xbmc/network/test/TestNetwork.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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 "ServiceBroker.h"
+#include "network/Network.h"
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+class TestNetwork : public testing::Test
+{
+public:
+ TestNetwork() = default;
+ ~TestNetwork() = default;
+
+ bool PingHost(const std::string& ip) const
+ {
+ static auto& network = CServiceBroker::GetNetwork();
+
+ return network.PingHost(inet_addr(ip.c_str()), GetPort(), GetTimeout());
+ }
+
+ unsigned int GetPort() const { return m_port; }
+ unsigned int GetTimeout() const { return m_timeoutMs; }
+
+private:
+ unsigned int m_port{0};
+ unsigned int m_timeoutMs{100};
+};
+
+TEST_F(TestNetwork, PingHost)
+{
+ EXPECT_TRUE(PingHost("127.0.0.1"));
+ EXPECT_FALSE(PingHost("10.254.254.254"));
+}
diff --git a/xbmc/network/upnp/UPnPInternal.cpp b/xbmc/network/upnp/UPnPInternal.cpp
index fddc3608c0..c4ec1d946c 100644
--- a/xbmc/network/upnp/UPnPInternal.cpp
+++ b/xbmc/network/upnp/UPnPInternal.cpp
@@ -9,7 +9,7 @@
#include "FileItem.h"
#include "ServiceBroker.h"
-#include "TextureDatabase.h"
+#include "TextureCache.h"
#include "ThumbLoader.h"
#include "UPnPServer.h"
#include "URL.h"
@@ -32,6 +32,8 @@
#include <algorithm>
#include <array>
+#include <memory>
+#include <optional>
#include <string_view>
#include <Platinum/Source/Platinum/Platinum.h>
@@ -39,6 +41,22 @@
using namespace MUSIC_INFO;
using namespace XFILE;
+namespace
+{
+std::optional<std::string> GetImageDLNAProfile(const std::string& imgPath)
+{
+ if (URIUtils::HasExtension(imgPath, ".png"))
+ {
+ return "PNG_TN";
+ }
+ else if (URIUtils::HasExtension(imgPath, ".jpg") || URIUtils::HasExtension(imgPath, ".jpeg"))
+ {
+ return "JPEG_TN";
+ }
+ return std::nullopt;
+}
+} // namespace
+
namespace UPNP
{
@@ -71,25 +89,27 @@ constexpr NPT_HttpFileRequestHandler_DefaultFileTypeMapEntry kodiPlatinumMimeTyp
+---------------------------------------------------------------------*/
EClientQuirks GetClientQuirks(const PLT_HttpRequestContext* context)
{
- if(context == NULL)
- return ECLIENTQUIRKS_NONE;
+ if (context == NULL)
+ return ECLIENTQUIRKS_NONE;
unsigned int quirks = 0;
- const NPT_String* user_agent = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_USER_AGENT);
- const NPT_String* server = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_SERVER);
-
- if (user_agent) {
- if (user_agent->Find("XBox", 0, true) >= 0 ||
- user_agent->Find("Xenon", 0, true) >= 0)
- quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
+ const NPT_String* user_agent =
+ context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_USER_AGENT);
+ const NPT_String* server =
+ context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_SERVER);
- if (user_agent->Find("Windows-Media-Player", 0, true) >= 0)
- quirks |= ECLIENTQUIRKS_UNKNOWNSERIES;
+ if (user_agent)
+ {
+ if (user_agent->Find("XBox", 0, true) >= 0 || user_agent->Find("Xenon", 0, true) >= 0)
+ quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
+ if (user_agent->Find("Windows-Media-Player", 0, true) >= 0)
+ quirks |= ECLIENTQUIRKS_UNKNOWNSERIES;
}
- if (server) {
- if (server->Find("Xbox", 0, true) >= 0)
- quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
+ if (server)
+ {
+ if (server->Find("Xbox", 0, true) >= 0)
+ quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
}
return (EClientQuirks)quirks;
@@ -98,327 +118,361 @@ EClientQuirks GetClientQuirks(const PLT_HttpRequestContext* context)
/*----------------------------------------------------------------------
| GetMediaControllerQuirks
+---------------------------------------------------------------------*/
-EMediaControllerQuirks GetMediaControllerQuirks(const PLT_DeviceData *device)
+EMediaControllerQuirks GetMediaControllerQuirks(const PLT_DeviceData* device)
{
- if (device == NULL)
- return EMEDIACONTROLLERQUIRKS_NONE;
+ if (device == NULL)
+ return EMEDIACONTROLLERQUIRKS_NONE;
- unsigned int quirks = 0;
+ unsigned int quirks = 0;
- if (device->m_Manufacturer.Find("Samsung Electronics") >= 0)
- quirks |= EMEDIACONTROLLERQUIRKS_X_MKV;
+ if (device->m_Manufacturer.Find("Samsung Electronics") >= 0)
+ quirks |= EMEDIACONTROLLERQUIRKS_X_MKV;
- return (EMediaControllerQuirks)quirks;
+ return (EMediaControllerQuirks)quirks;
}
/*----------------------------------------------------------------------
| GetMimeType
+---------------------------------------------------------------------*/
-NPT_String
-GetMimeType(const char* filename,
- const PLT_HttpRequestContext* context /* = NULL */)
+NPT_String GetMimeType(const char* filename, const PLT_HttpRequestContext* context /* = NULL */)
{
- NPT_String ext = URIUtils::GetExtension(filename).c_str();
- ext.TrimLeft('.');
- ext = ext.ToLowercase();
+ NPT_String ext = URIUtils::GetExtension(filename).c_str();
+ ext.TrimLeft('.');
+ ext = ext.ToLowercase();
- return PLT_MimeType::GetMimeTypeFromExtension(ext, context);
+ return PLT_MimeType::GetMimeTypeFromExtension(ext, context);
}
/*----------------------------------------------------------------------
| GetMimeType
+---------------------------------------------------------------------*/
-NPT_String
-GetMimeType(const CFileItem& item,
- const PLT_HttpRequestContext* context /* = NULL */)
+NPT_String GetMimeType(const CFileItem& item, const PLT_HttpRequestContext* context /* = NULL */)
{
- std::string path = item.GetPath();
- if (item.HasVideoInfoTag() && !item.GetVideoInfoTag()->GetPath().empty()) {
- path = item.GetVideoInfoTag()->GetPath();
- } else if (item.HasMusicInfoTag() && !item.GetMusicInfoTag()->GetURL().empty()) {
- path = item.GetMusicInfoTag()->GetURL();
- }
+ std::string path = item.GetPath();
+ if (item.HasVideoInfoTag() && !item.GetVideoInfoTag()->GetPath().empty())
+ {
+ path = item.GetVideoInfoTag()->GetPath();
+ }
+ else if (item.HasMusicInfoTag() && !item.GetMusicInfoTag()->GetURL().empty())
+ {
+ path = item.GetMusicInfoTag()->GetURL();
+ }
- if (URIUtils::IsStack(path))
- path = XFILE::CStackDirectory::GetFirstStackedFile(path);
+ if (URIUtils::IsStack(path))
+ path = XFILE::CStackDirectory::GetFirstStackedFile(path);
- NPT_String ext = URIUtils::GetExtension(path).c_str();
- ext.TrimLeft('.');
- ext = ext.ToLowercase();
+ NPT_String ext = URIUtils::GetExtension(path).c_str();
+ ext.TrimLeft('.');
+ ext = ext.ToLowercase();
- NPT_String mime;
+ NPT_String mime;
- if (!ext.IsEmpty())
- {
- /* We look first to our extensions/overrides of libplatinum mimetypes. If not found, fallback to
+ if (!ext.IsEmpty())
+ {
+ /* We look first to our extensions/overrides of libplatinum mimetypes. If not found, fallback to
Platinum definitions.
*/
- const auto kodiOverrideMimeType = std::find_if(
- std::begin(kodiPlatinumMimeTypeExtensions), std::end(kodiPlatinumMimeTypeExtensions),
- [&](const auto& mimeTypeEntry) { return mimeTypeEntry.extension == ext; });
- if (kodiOverrideMimeType != std::end(kodiPlatinumMimeTypeExtensions))
- {
- mime = kodiOverrideMimeType->mime_type;
- }
- else
- {
- /* Give priority to Platinum mime types as they are defined to map extension to DLNA compliant mime types
+ const auto kodiOverrideMimeType = std::find_if(
+ std::begin(kodiPlatinumMimeTypeExtensions), std::end(kodiPlatinumMimeTypeExtensions),
+ [&](const auto& mimeTypeEntry) { return mimeTypeEntry.extension == ext; });
+ if (kodiOverrideMimeType != std::end(kodiPlatinumMimeTypeExtensions))
+ {
+ mime = kodiOverrideMimeType->mime_type;
+ }
+ else
+ {
+ /* Give priority to Platinum mime types as they are defined to map extension to DLNA compliant mime types
or custom types according to context (who asked for it)
*/
- mime = PLT_MimeType::GetMimeTypeFromExtension(ext, context);
- if (mime == "application/octet-stream")
- {
- mime = "";
- }
+ mime = PLT_MimeType::GetMimeTypeFromExtension(ext, context);
+ if (mime == "application/octet-stream")
+ {
+ mime = "";
}
}
+ }
- /* if Platinum couldn't map it, default to Kodi internal mapping */
- if (mime.IsEmpty()) {
- NPT_String mime = item.GetMimeType().c_str();
- if (mime == "application/octet-stream") mime = "";
- }
+ /* if Platinum couldn't map it, default to Kodi internal mapping */
+ if (mime.IsEmpty())
+ {
+ NPT_String mime = item.GetMimeType().c_str();
+ if (mime == "application/octet-stream")
+ mime = "";
+ }
- /* fallback to generic mime type if not found */
- if (mime.IsEmpty()) {
- if (item.IsVideo() || item.IsVideoDb() )
- mime = "video/" + ext;
- else if (item.IsAudio() || item.IsMusicDb() )
- mime = "audio/" + ext;
- else if (item.IsPicture() )
- mime = "image/" + ext;
- else if (item.IsSubtitle())
- mime = "text/" + ext;
- }
+ /* fallback to generic mime type if not found */
+ if (mime.IsEmpty())
+ {
+ if (item.IsVideo() || item.IsVideoDb())
+ mime = "video/" + ext;
+ else if (item.IsAudio() || item.IsMusicDb())
+ mime = "audio/" + ext;
+ else if (item.IsPicture())
+ mime = "image/" + ext;
+ else if (item.IsSubtitle())
+ mime = "text/" + ext;
+ }
- /* nothing we can figure out */
- if (mime.IsEmpty()) {
- mime = "application/octet-stream";
- }
+ /* nothing we can figure out */
+ if (mime.IsEmpty())
+ {
+ mime = "application/octet-stream";
+ }
- return mime;
+ return mime;
}
/*----------------------------------------------------------------------
| GetProtocolInfo
+---------------------------------------------------------------------*/
-const NPT_String
-GetProtocolInfo(const CFileItem& item,
- const char* protocol,
- const PLT_HttpRequestContext* context /* = NULL */)
+const NPT_String GetProtocolInfo(const CFileItem& item,
+ const char* protocol,
+ const PLT_HttpRequestContext* context /* = NULL */)
{
- NPT_String proto = protocol;
+ NPT_String proto = protocol;
- //! @todo fixup the protocol just in case nothing was passed
- if (proto.IsEmpty()) {
- proto = item.GetURL().GetProtocol().c_str();
- }
+ //! @todo fixup the protocol just in case nothing was passed
+ if (proto.IsEmpty())
+ {
+ proto = item.GetURL().GetProtocol().c_str();
+ }
- /**
+ /**
* map protocol to right prefix and use xbmc-get for
* unsupported UPnP protocols for other xbmc clients
* @todo add rtsp ?
*/
- if (proto == "http") {
- proto = "http-get";
- } else {
- proto = "xbmc-get";
- }
+ if (proto == "http")
+ {
+ proto = "http-get";
+ }
+ else
+ {
+ proto = "xbmc-get";
+ }
- /* we need a valid extension to retrieve the mimetype for the protocol info */
- NPT_String mime = GetMimeType(item, context);
- proto += ":*:" + mime + ":" + PLT_ProtocolInfo::GetDlnaExtension(mime, context);
- return proto;
+ /* we need a valid extension to retrieve the mimetype for the protocol info */
+ NPT_String mime = GetMimeType(item, context);
+ proto += ":*:" + mime + ":" + PLT_ProtocolInfo::GetDlnaExtension(mime, context);
+ return proto;
}
- /*----------------------------------------------------------------------
+/*----------------------------------------------------------------------
| CResourceFinder
+---------------------------------------------------------------------*/
CResourceFinder::CResourceFinder(const char* protocol, const char* content)
- : m_Protocol(protocol)
- , m_Content(content)
+ : m_Protocol(protocol), m_Content(content)
{
}
-bool CResourceFinder::operator()(const PLT_MediaItemResource& resource) const {
- if (m_Content.IsEmpty())
- return (resource.m_ProtocolInfo.GetProtocol().Compare(m_Protocol, true) == 0);
- else
- return ((resource.m_ProtocolInfo.GetProtocol().Compare(m_Protocol, true) == 0)
- && resource.m_ProtocolInfo.GetContentType().StartsWith(m_Content, true));
+bool CResourceFinder::operator()(const PLT_MediaItemResource& resource) const
+{
+ if (m_Content.IsEmpty())
+ return (resource.m_ProtocolInfo.GetProtocol().Compare(m_Protocol, true) == 0);
+ else
+ return ((resource.m_ProtocolInfo.GetProtocol().Compare(m_Protocol, true) == 0) &&
+ resource.m_ProtocolInfo.GetContentType().StartsWith(m_Content, true));
}
/*----------------------------------------------------------------------
| PopulateObjectFromTag
+---------------------------------------------------------------------*/
-NPT_Result
-PopulateObjectFromTag(CMusicInfoTag& tag,
- PLT_MediaObject& object,
- NPT_String* file_path,
- PLT_MediaItemResource* resource,
- EClientQuirks quirks,
- UPnPService service /* = UPnPServiceNone */)
+NPT_Result PopulateObjectFromTag(CMusicInfoTag& tag,
+ PLT_MediaObject& object,
+ NPT_String* file_path,
+ PLT_MediaItemResource* resource,
+ EClientQuirks quirks,
+ UPnPService service /* = UPnPServiceNone */)
{
- if (!tag.GetURL().empty() && file_path)
- *file_path = tag.GetURL().c_str();
-
- std::vector<std::string> genres = tag.GetGenre();
- for (unsigned int index = 0; index < genres.size(); index++)
- object.m_Affiliation.genres.Add(genres.at(index).c_str());
- object.m_Title = tag.GetTitle().c_str();
- object.m_Affiliation.album = tag.GetAlbum().c_str();
- for (unsigned int index = 0; index < tag.GetArtist().size(); index++)
- {
- object.m_People.artists.Add(tag.GetArtist().at(index).c_str());
- object.m_People.artists.Add(tag.GetArtist().at(index).c_str(), "Performer");
- }
- object.m_People.artists.Add((!tag.GetAlbumArtistString().empty() ? tag.GetAlbumArtistString() : tag.GetArtistString()).c_str(), "AlbumArtist");
- if(tag.GetAlbumArtistString().empty())
- object.m_Creator = tag.GetArtistString().c_str();
- else
- object.m_Creator = tag.GetAlbumArtistString().c_str();
- object.m_MiscInfo.original_track_number = tag.GetTrackNumber();
- if(tag.GetDatabaseId() >= 0) {
- object.m_ReferenceID = EncodeObjectId(StringUtils::Format("musicdb://songs/{}{}", tag.GetDatabaseId(), URIUtils::GetExtension(tag.GetURL())));
- }
- if (object.m_ReferenceID == object.m_ObjectID)
- object.m_ReferenceID = "";
+ if (!tag.GetURL().empty() && file_path)
+ *file_path = tag.GetURL().c_str();
+
+ std::vector<std::string> genres = tag.GetGenre();
+ for (unsigned int index = 0; index < genres.size(); index++)
+ object.m_Affiliation.genres.Add(genres.at(index).c_str());
+ object.m_Title = tag.GetTitle().c_str();
+ object.m_Affiliation.album = tag.GetAlbum().c_str();
+ for (unsigned int index = 0; index < tag.GetArtist().size(); index++)
+ {
+ object.m_People.artists.Add(tag.GetArtist().at(index).c_str());
+ object.m_People.artists.Add(tag.GetArtist().at(index).c_str(), "Performer");
+ }
+ object.m_People.artists.Add(
+ (!tag.GetAlbumArtistString().empty() ? tag.GetAlbumArtistString() : tag.GetArtistString())
+ .c_str(),
+ "AlbumArtist");
+ if (tag.GetAlbumArtistString().empty())
+ object.m_Creator = tag.GetArtistString().c_str();
+ else
+ object.m_Creator = tag.GetAlbumArtistString().c_str();
+ object.m_MiscInfo.original_track_number = tag.GetTrackNumber();
+ if (tag.GetDatabaseId() >= 0)
+ {
+ object.m_ReferenceID = EncodeObjectId(StringUtils::Format(
+ "musicdb://songs/{}{}", tag.GetDatabaseId(), URIUtils::GetExtension(tag.GetURL())));
+ }
+ if (object.m_ReferenceID == object.m_ObjectID)
+ object.m_ReferenceID = "";
- object.m_MiscInfo.last_time = tag.GetLastPlayed().GetAsW3CDateTime().c_str();
- object.m_MiscInfo.play_count = tag.GetPlayCount();
+ object.m_MiscInfo.last_time = tag.GetLastPlayed().GetAsW3CDateTime().c_str();
+ object.m_MiscInfo.play_count = tag.GetPlayCount();
- if (resource) resource->m_Duration = tag.GetDuration();
+ if (resource)
+ resource->m_Duration = tag.GetDuration();
- return NPT_SUCCESS;
+ return NPT_SUCCESS;
}
/*----------------------------------------------------------------------
| PopulateObjectFromTag
+---------------------------------------------------------------------*/
-NPT_Result
-PopulateObjectFromTag(CVideoInfoTag& tag,
- PLT_MediaObject& object,
- NPT_String* file_path,
- PLT_MediaItemResource* resource,
- EClientQuirks quirks,
- UPnPService service /* = UPnPServiceNone */)
+NPT_Result PopulateObjectFromTag(CVideoInfoTag& tag,
+ PLT_MediaObject& object,
+ NPT_String* file_path,
+ PLT_MediaItemResource* resource,
+ EClientQuirks quirks,
+ UPnPService service /* = UPnPServiceNone */)
{
- if (!tag.m_strFileNameAndPath.empty() && file_path)
- *file_path = tag.m_strFileNameAndPath.c_str();
-
- if (tag.m_iDbId != -1 ) {
- if (tag.m_type == MediaTypeMusicVideo) {
- object.m_ObjectClass.type = "object.item.videoItem.musicVideoClip";
- object.m_Creator = StringUtils::Join(tag.m_artist, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator).c_str();
- for (const auto& itArtist : tag.m_artist)
- object.m_People.artists.Add(itArtist.c_str());
- object.m_Affiliation.album = tag.m_strAlbum.c_str();
- object.m_Title = tag.m_strTitle.c_str();
- object.m_Date = tag.GetPremiered().GetAsW3CDate().c_str();
- object.m_ReferenceID = EncodeObjectId(StringUtils::Format("videodb://musicvideos/titles/{}", tag.m_iDbId));
- } else if (tag.m_type == MediaTypeMovie) {
- object.m_ObjectClass.type = "object.item.videoItem.movie";
- object.m_Title = tag.m_strTitle.c_str();
- object.m_Date = tag.GetPremiered().GetAsW3CDate().c_str();
- object.m_ReferenceID = EncodeObjectId(StringUtils::Format("videodb://movies/titles/{}", tag.m_iDbId));
- } else {
- object.m_Recorded.series_title = tag.m_strShowTitle.c_str();
-
- if (tag.m_type == MediaTypeTvShow) {
- object.m_ObjectClass.type = "object.container.album.videoAlbum.videoBroadcastShow";
- object.m_Title = tag.m_strTitle.c_str();
- object.m_Recorded.episode_number = tag.m_iEpisode;
- object.m_Recorded.episode_count = tag.m_iEpisode;
- if (!tag.m_premiered.IsValid() && tag.GetYear() > 0)
- object.m_Date = CDateTime(tag.GetYear(), 1, 1, 0, 0, 0).GetAsW3CDate().c_str();
- else
- object.m_Date = tag.m_premiered.GetAsW3CDate().c_str();
- object.m_ReferenceID = EncodeObjectId(StringUtils::Format("videodb://tvshows/titles/{}", tag.m_iDbId));
- } else if (tag.m_type == MediaTypeSeason) {
- object.m_ObjectClass.type = "object.container.album.videoAlbum.videoBroadcastSeason";
- object.m_Title = tag.m_strTitle.c_str();
- object.m_Recorded.episode_season = tag.m_iSeason;
- object.m_Recorded.episode_count = tag.m_iEpisode;
- if (!tag.m_premiered.IsValid() && tag.GetYear() > 0)
- object.m_Date = CDateTime(tag.GetYear(), 1, 1, 0, 0, 0).GetAsW3CDate().c_str();
- else
- object.m_Date = tag.m_premiered.GetAsW3CDate().c_str();
- object.m_ReferenceID = EncodeObjectId(StringUtils::Format("videodb://tvshows/titles/{}/{}", tag.m_iIdShow, tag.m_iSeason));
- } else {
- object.m_ObjectClass.type = "object.item.videoItem.videoBroadcast";
- object.m_Recorded.program_title = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
- object.m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
- object.m_Recorded.program_title += (" : " + tag.m_strTitle).c_str();
- object.m_Recorded.episode_number = tag.m_iEpisode;
- object.m_Recorded.episode_season = tag.m_iSeason;
- object.m_Title = object.m_Recorded.series_title + " - " + object.m_Recorded.program_title;
- object.m_ReferenceID = EncodeObjectId(StringUtils::Format("videodb://tvshows/titles/{}/{}/{}", tag.m_iIdShow, tag.m_iSeason, tag.m_iDbId));
- object.m_Date = tag.m_firstAired.GetAsW3CDate().c_str();
- }
- }
+ if (!tag.m_strFileNameAndPath.empty() && file_path)
+ *file_path = tag.m_strFileNameAndPath.c_str();
+
+ if (tag.m_iDbId != -1)
+ {
+ if (tag.m_type == MediaTypeMusicVideo)
+ {
+ object.m_ObjectClass.type = "object.item.videoItem.musicVideoClip";
+ object.m_Creator =
+ StringUtils::Join(
+ tag.m_artist,
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator)
+ .c_str();
+ for (const auto& itArtist : tag.m_artist)
+ object.m_People.artists.Add(itArtist.c_str());
+ object.m_Affiliation.album = tag.m_strAlbum.c_str();
+ object.m_Title = tag.m_strTitle.c_str();
+ object.m_Date = tag.GetPremiered().GetAsW3CDate().c_str();
+ object.m_ReferenceID =
+ EncodeObjectId(StringUtils::Format("videodb://musicvideos/titles/{}", tag.m_iDbId));
}
+ else if (tag.m_type == MediaTypeMovie)
+ {
+ object.m_ObjectClass.type = "object.item.videoItem.movie";
+ object.m_Title = tag.m_strTitle.c_str();
+ object.m_Date = tag.GetPremiered().GetAsW3CDate().c_str();
+ object.m_ReferenceID =
+ EncodeObjectId(StringUtils::Format("videodb://movies/titles/{}", tag.m_iDbId));
+ }
+ else
+ {
+ object.m_Recorded.series_title = tag.m_strShowTitle.c_str();
- if(quirks & ECLIENTQUIRKS_BASICVIDEOCLASS)
- object.m_ObjectClass.type = "object.item.videoItem";
+ if (tag.m_type == MediaTypeTvShow)
+ {
+ object.m_ObjectClass.type = "object.container.album.videoAlbum.videoBroadcastShow";
+ object.m_Title = tag.m_strTitle.c_str();
+ object.m_Recorded.episode_number = tag.m_iEpisode;
+ object.m_Recorded.episode_count = tag.m_iEpisode;
+ if (!tag.m_premiered.IsValid() && tag.GetYear() > 0)
+ object.m_Date = CDateTime(tag.GetYear(), 1, 1, 0, 0, 0).GetAsW3CDate().c_str();
+ else
+ object.m_Date = tag.m_premiered.GetAsW3CDate().c_str();
+ object.m_ReferenceID =
+ EncodeObjectId(StringUtils::Format("videodb://tvshows/titles/{}", tag.m_iDbId));
+ }
+ else if (tag.m_type == MediaTypeSeason)
+ {
+ object.m_ObjectClass.type = "object.container.album.videoAlbum.videoBroadcastSeason";
+ object.m_Title = tag.m_strTitle.c_str();
+ object.m_Recorded.episode_season = tag.m_iSeason;
+ object.m_Recorded.episode_count = tag.m_iEpisode;
+ if (!tag.m_premiered.IsValid() && tag.GetYear() > 0)
+ object.m_Date = CDateTime(tag.GetYear(), 1, 1, 0, 0, 0).GetAsW3CDate().c_str();
+ else
+ object.m_Date = tag.m_premiered.GetAsW3CDate().c_str();
+ object.m_ReferenceID = EncodeObjectId(
+ StringUtils::Format("videodb://tvshows/titles/{}/{}", tag.m_iIdShow, tag.m_iSeason));
+ }
+ else
+ {
+ object.m_ObjectClass.type = "object.item.videoItem.videoBroadcast";
+ object.m_Recorded.program_title =
+ "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
+ object.m_Recorded.program_title +=
+ "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
+ object.m_Recorded.program_title += (" : " + tag.m_strTitle).c_str();
+ object.m_Recorded.episode_number = tag.m_iEpisode;
+ object.m_Recorded.episode_season = tag.m_iSeason;
+ object.m_Title = object.m_Recorded.series_title + " - " + object.m_Recorded.program_title;
+ object.m_ReferenceID = EncodeObjectId(StringUtils::Format(
+ "videodb://tvshows/titles/{}/{}/{}", tag.m_iIdShow, tag.m_iSeason, tag.m_iDbId));
+ object.m_Date = tag.m_firstAired.GetAsW3CDate().c_str();
+ }
+ }
+ }
- if(object.m_ReferenceID == object.m_ObjectID)
- object.m_ReferenceID = "";
+ if (quirks & ECLIENTQUIRKS_BASICVIDEOCLASS)
+ object.m_ObjectClass.type = "object.item.videoItem";
- for (unsigned int index = 0; index < tag.m_studio.size(); index++)
- object.m_People.publisher.Add(tag.m_studio[index].c_str());
+ if (object.m_ReferenceID == object.m_ObjectID)
+ object.m_ReferenceID = "";
- object.m_XbmcInfo.date_added = tag.m_dateAdded.GetAsW3CDate().c_str();
- object.m_XbmcInfo.rating = tag.GetRating().rating;
- object.m_XbmcInfo.votes = tag.GetRating().votes;
- object.m_XbmcInfo.unique_identifier = tag.GetUniqueID().c_str();
- for (const auto& country : tag.m_country)
- object.m_XbmcInfo.countries.Add(country.c_str());
- object.m_XbmcInfo.user_rating = tag.m_iUserRating;
+ for (unsigned int index = 0; index < tag.m_studio.size(); index++)
+ object.m_People.publisher.Add(tag.m_studio[index].c_str());
- for (unsigned int index = 0; index < tag.m_genre.size(); index++)
- object.m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
+ object.m_XbmcInfo.date_added = tag.m_dateAdded.GetAsW3CDate().c_str();
+ object.m_XbmcInfo.rating = tag.GetRating().rating;
+ object.m_XbmcInfo.votes = tag.GetRating().votes;
+ object.m_XbmcInfo.unique_identifier = tag.GetUniqueID().c_str();
+ for (const auto& country : tag.m_country)
+ object.m_XbmcInfo.countries.Add(country.c_str());
+ object.m_XbmcInfo.user_rating = tag.m_iUserRating;
- for (CVideoInfoTag::iCast it = tag.m_cast.begin(); it != tag.m_cast.end(); ++it)
- {
- object.m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
- }
+ for (unsigned int index = 0; index < tag.m_genre.size(); index++)
+ object.m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
- for (unsigned int index = 0; index < tag.m_director.size(); index++)
- object.m_People.directors.Add(tag.m_director[index].c_str());
-
- for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
- object.m_People.authors.Add(tag.m_writingCredits[index].c_str());
-
- object.m_Description.description = tag.m_strTagLine.c_str();
- object.m_Description.long_description = tag.m_strPlot.c_str();
- object.m_Description.rating = tag.m_strMPAARating.c_str();
- object.m_MiscInfo.last_position = (NPT_UInt32)tag.GetResumePoint().timeInSeconds;
- object.m_XbmcInfo.last_playerstate = tag.GetResumePoint().playerState.c_str();
- object.m_MiscInfo.last_time = tag.m_lastPlayed.GetAsW3CDateTime().c_str();
- object.m_MiscInfo.play_count = tag.GetPlayCount();
- if (resource) {
- resource->m_Duration = tag.GetDuration();
- if (tag.HasStreamDetails()) {
- const CStreamDetails &details = tag.m_streamDetails;
- resource->m_Resolution = NPT_String::FromInteger(details.GetVideoWidth()) + "x" + NPT_String::FromInteger(details.GetVideoHeight());
- resource->m_NbAudioChannels = details.GetAudioChannels();
- }
+ for (CVideoInfoTag::iCast it = tag.m_cast.begin(); it != tag.m_cast.end(); ++it)
+ {
+ object.m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
+ }
+
+ for (unsigned int index = 0; index < tag.m_director.size(); index++)
+ object.m_People.directors.Add(tag.m_director[index].c_str());
+
+ for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
+ object.m_People.authors.Add(tag.m_writingCredits[index].c_str());
+
+ object.m_Description.description = tag.m_strTagLine.c_str();
+ object.m_Description.long_description = tag.m_strPlot.c_str();
+ object.m_Description.rating = tag.m_strMPAARating.c_str();
+ object.m_MiscInfo.last_position = (NPT_UInt32)tag.GetResumePoint().timeInSeconds;
+ object.m_XbmcInfo.last_playerstate = tag.GetResumePoint().playerState.c_str();
+ object.m_MiscInfo.last_time = tag.m_lastPlayed.GetAsW3CDateTime().c_str();
+ object.m_MiscInfo.play_count = tag.GetPlayCount();
+ if (resource)
+ {
+ resource->m_Duration = tag.GetDuration();
+ if (tag.HasStreamDetails())
+ {
+ const CStreamDetails& details = tag.m_streamDetails;
+ resource->m_Resolution = NPT_String::FromInteger(details.GetVideoWidth()) + "x" +
+ NPT_String::FromInteger(details.GetVideoHeight());
+ resource->m_NbAudioChannels = details.GetAudioChannels();
}
+ }
- return NPT_SUCCESS;
+ return NPT_SUCCESS;
}
/*----------------------------------------------------------------------
| BuildObject
+---------------------------------------------------------------------*/
-PLT_MediaObject*
-BuildObject(CFileItem& item,
- NPT_String& file_path,
- bool with_count,
- NPT_Reference<CThumbLoader>& thumb_loader,
- const PLT_HttpRequestContext* context /* = NULL */,
- CUPnPServer* upnp_server /* = NULL */,
- UPnPService upnp_service /* = UPnPServiceNone */)
+PLT_MediaObject* BuildObject(CFileItem& item,
+ NPT_String& file_path,
+ bool with_count,
+ NPT_Reference<CThumbLoader>& thumb_loader,
+ const PLT_HttpRequestContext* context /* = NULL */,
+ CUPnPServer* upnp_server /* = NULL */,
+ UPnPService upnp_service /* = UPnPServiceNone */)
{
static Logger logger = CServiceBroker::GetLogging().GetLogger("UPNP::BuildObject");
@@ -426,7 +480,8 @@ BuildObject(CFileItem& item,
PLT_MediaObject* object = NULL;
std::string thumb;
- logger->debug("Building didl for plain object '{}' (encoded value: '{}')", item.GetPath(), EncodeObjectId(item.GetPath()).GetChars());
+ logger->debug("Building didl for plain object '{}' (encoded value: '{}')", item.GetPath(),
+ EncodeObjectId(item.GetPath()).GetChars());
auto settingsComponent = CServiceBroker::GetSettingsComponent();
if (!settingsComponent)
@@ -451,547 +506,642 @@ BuildObject(CFileItem& item,
context->GetLocalAddress().GetPort(), "/");
ips.Remove(context->GetLocalAddress().GetIpAddress());
ips.Insert(ips.GetFirstItem(), context->GetLocalAddress().GetIpAddress());
- } else if(upnp_server) {
- rooturi = NPT_HttpUrl("localhost", upnp_server->GetPort(), "/");
+ }
+ else if (upnp_server)
+ {
+ rooturi = NPT_HttpUrl("localhost", upnp_server->GetPort(), "/");
+ }
+
+ if (!item.m_bIsFolder)
+ {
+ object = new PLT_MediaItem();
+ object->m_ObjectID = EncodeObjectId(item.GetPath());
+
+ /* Setup object type */
+ if (item.IsMusicDb() || item.IsAudio())
+ {
+ object->m_ObjectClass.type = "object.item.audioItem.musicTrack";
+
+ if (item.HasMusicInfoTag())
+ {
+ CMusicInfoTag* tag = item.GetMusicInfoTag();
+ PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks, upnp_service);
+ }
}
+ else if (item.IsVideoDb() || item.IsVideo())
+ {
+ object->m_ObjectClass.type = "object.item.videoItem";
- if (!item.m_bIsFolder) {
- object = new PLT_MediaItem();
- object->m_ObjectID = EncodeObjectId(item.GetPath());
-
- /* Setup object type */
- if (item.IsMusicDb() || item.IsAudio()) {
- object->m_ObjectClass.type = "object.item.audioItem.musicTrack";
-
- if (item.HasMusicInfoTag()) {
- CMusicInfoTag *tag = item.GetMusicInfoTag();
- PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks, upnp_service);
- }
- } else if (item.IsVideoDb() || item.IsVideo()) {
- object->m_ObjectClass.type = "object.item.videoItem";
-
- if(quirks & ECLIENTQUIRKS_UNKNOWNSERIES)
- object->m_Affiliation.album = "[Unknown Series]";
-
- if (item.HasVideoInfoTag()) {
- CVideoInfoTag *tag = item.GetVideoInfoTag();
- PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks, upnp_service);
- }
- } else if (item.IsPicture()) {
- object->m_ObjectClass.type = "object.item.imageItem.photo";
- } else {
- object->m_ObjectClass.type = "object.item";
- }
+ if (quirks & ECLIENTQUIRKS_UNKNOWNSERIES)
+ object->m_Affiliation.album = "[Unknown Series]";
- // duration of zero is invalid
- if (resource.m_Duration == 0) resource.m_Duration = -1;
+ if (item.HasVideoInfoTag())
+ {
+ CVideoInfoTag* tag = item.GetVideoInfoTag();
+ PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks, upnp_service);
+ }
+ }
+ else if (item.IsPicture())
+ {
+ object->m_ObjectClass.type = "object.item.imageItem.photo";
+ }
+ else
+ {
+ object->m_ObjectClass.type = "object.item";
+ }
- // Set the resource file size
- resource.m_Size = item.m_dwSize;
- if(resource.m_Size == 0)
- resource.m_Size = (NPT_LargeSize)-1;
+ // duration of zero is invalid
+ if (resource.m_Duration == 0)
+ resource.m_Duration = -1;
- // set date
- if (object->m_Date.IsEmpty() && item.m_dateTime.IsValid()) {
- object->m_Date = item.m_dateTime.GetAsW3CDate().c_str();
- }
+ // Set the resource file size
+ resource.m_Size = item.m_dwSize;
+ if (resource.m_Size == 0)
+ resource.m_Size = (NPT_LargeSize)-1;
- if (upnp_server) {
- upnp_server->AddSafeResourceUri(object, rooturi, ips, file_path, GetProtocolInfo(item, "http", context));
- }
+ // set date
+ if (object->m_Date.IsEmpty() && item.m_dateTime.IsValid())
+ {
+ object->m_Date = item.m_dateTime.GetAsW3CDate().c_str();
+ }
- // if the item is remote, add a direct link to the item
- if (URIUtils::IsRemote((const char*)file_path)) {
- resource.m_ProtocolInfo = PLT_ProtocolInfo(GetProtocolInfo(item, item.GetURL().GetProtocol().c_str(), context));
- resource.m_Uri = file_path;
-
- // if the direct link can be served directly using http, then push it in front
- // otherwise keep the xbmc-get resource last and let a compatible client look for it
- if (resource.m_ProtocolInfo.ToString().StartsWith("xbmc", true)) {
- object->m_Resources.Add(resource);
- } else {
- object->m_Resources.Insert(object->m_Resources.GetFirstItem(), resource);
- }
- }
+ if (upnp_server)
+ {
+ upnp_server->AddSafeResourceUri(object, rooturi, ips, file_path,
+ GetProtocolInfo(item, "http", context));
+ }
- // copy across the known metadata
- for(unsigned i=0; i<object->m_Resources.GetItemCount(); i++) {
- object->m_Resources[i].m_Size = resource.m_Size;
- object->m_Resources[i].m_Duration = resource.m_Duration;
- object->m_Resources[i].m_Resolution = resource.m_Resolution;
- }
+ // if the item is remote, add a direct link to the item
+ if (URIUtils::IsRemote((const char*)file_path))
+ {
+ resource.m_ProtocolInfo =
+ PLT_ProtocolInfo(GetProtocolInfo(item, item.GetURL().GetProtocol().c_str(), context));
+ resource.m_Uri = file_path;
+
+ // if the direct link can be served directly using http, then push it in front
+ // otherwise keep the xbmc-get resource last and let a compatible client look for it
+ if (resource.m_ProtocolInfo.ToString().StartsWith("xbmc", true))
+ {
+ object->m_Resources.Add(resource);
+ }
+ else
+ {
+ object->m_Resources.Insert(object->m_Resources.GetFirstItem(), resource);
+ }
+ }
+
+ // copy across the known metadata
+ for (unsigned i = 0; i < object->m_Resources.GetItemCount(); i++)
+ {
+ object->m_Resources[i].m_Size = resource.m_Size;
+ object->m_Resources[i].m_Duration = resource.m_Duration;
+ object->m_Resources[i].m_Resolution = resource.m_Resolution;
+ }
- // Some upnp clients expect all audio items to have parent root id 4
+ // Some upnp clients expect all audio items to have parent root id 4
#ifdef WMP_ID_MAPPING
- object->m_ParentID = EncodeObjectId("4");
+ object->m_ParentID = EncodeObjectId("4");
#endif
- } else {
- PLT_MediaContainer* container = new PLT_MediaContainer;
- object = container;
-
- /* Assign a title and id for this container */
- container->m_ObjectID = EncodeObjectId(item.GetPath());
- container->m_ObjectClass.type = "object.container";
- container->m_ChildrenCount = -1;
-
- /* this might be overkill, but hey */
- if (item.IsMusicDb()) {
- MUSICDATABASEDIRECTORY::NODE_TYPE node = CMusicDatabaseDirectory::GetDirectoryType(item.GetPath());
- switch(node) {
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ARTIST: {
- container->m_ObjectClass.type += ".person.musicArtist";
- CMusicInfoTag *tag = item.GetMusicInfoTag();
- if (tag) {
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(tag->GetArtistString()).c_str(), "Performer");
- container->m_People.artists.Add(
- CorrectAllItemsSortHack((!tag->GetAlbumArtistString().empty() ? tag->GetAlbumArtistString() : tag->GetArtistString())).c_str(), "AlbumArtist");
- }
+ }
+ else
+ {
+ PLT_MediaContainer* container = new PLT_MediaContainer;
+ object = container;
+
+ /* Assign a title and id for this container */
+ container->m_ObjectID = EncodeObjectId(item.GetPath());
+ container->m_ObjectClass.type = "object.container";
+ container->m_ChildrenCount = -1;
+
+ /* this might be overkill, but hey */
+ if (item.IsMusicDb())
+ {
+ MUSICDATABASEDIRECTORY::NODE_TYPE node =
+ CMusicDatabaseDirectory::GetDirectoryType(item.GetPath());
+ switch (node)
+ {
+ case MUSICDATABASEDIRECTORY::NODE_TYPE_ARTIST:
+ {
+ container->m_ObjectClass.type += ".person.musicArtist";
+ CMusicInfoTag* tag = item.GetMusicInfoTag();
+ if (tag)
+ {
+ container->m_People.artists.Add(CorrectAllItemsSortHack(tag->GetArtistString()).c_str(),
+ "Performer");
+ container->m_People.artists.Add(
+ CorrectAllItemsSortHack((!tag->GetAlbumArtistString().empty()
+ ? tag->GetAlbumArtistString()
+ : tag->GetArtistString()))
+ .c_str(),
+ "AlbumArtist");
+ }
#ifdef WMP_ID_MAPPING
- // Some upnp clients expect all artists to have parent root id 107
- container->m_ParentID = EncodeObjectId("107");
+ // Some upnp clients expect all artists to have parent root id 107
+ container->m_ParentID = EncodeObjectId("107");
#endif
- }
- break;
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM:
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_RECENTLY_ADDED: {
- container->m_ObjectClass.type += ".album.musicAlbum";
- // for Sonos to be happy
- CMusicInfoTag *tag = item.GetMusicInfoTag();
- if (tag) {
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(tag->GetArtistString()).c_str(), "Performer");
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(!tag->GetAlbumArtistString().empty() ? tag->GetAlbumArtistString() : tag->GetArtistString()).c_str(), "AlbumArtist");
- container->m_Affiliation.album = CorrectAllItemsSortHack(tag->GetAlbum()).c_str();
- }
+ }
+ break;
+ case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM:
+ case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_RECENTLY_ADDED:
+ {
+ container->m_ObjectClass.type += ".album.musicAlbum";
+ // for Sonos to be happy
+ CMusicInfoTag* tag = item.GetMusicInfoTag();
+ if (tag)
+ {
+ container->m_People.artists.Add(CorrectAllItemsSortHack(tag->GetArtistString()).c_str(),
+ "Performer");
+ container->m_People.artists.Add(
+ CorrectAllItemsSortHack(!tag->GetAlbumArtistString().empty()
+ ? tag->GetAlbumArtistString()
+ : tag->GetArtistString())
+ .c_str(),
+ "AlbumArtist");
+ container->m_Affiliation.album = CorrectAllItemsSortHack(tag->GetAlbum()).c_str();
+ }
#ifdef WMP_ID_MAPPING
- // Some upnp clients expect all albums to have parent root id 7
- container->m_ParentID = EncodeObjectId("7");
+ // Some upnp clients expect all albums to have parent root id 7
+ container->m_ParentID = EncodeObjectId("7");
#endif
- }
- break;
- case MUSICDATABASEDIRECTORY::NODE_TYPE_GENRE:
- container->m_ObjectClass.type += ".genre.musicGenre";
- break;
- default:
- break;
- }
- } else if (item.IsVideoDb()) {
- VIDEODATABASEDIRECTORY::NODE_TYPE node = CVideoDatabaseDirectory::GetDirectoryType(item.GetPath());
- CVideoInfoTag &tag = *item.GetVideoInfoTag();
- switch(node) {
- case VIDEODATABASEDIRECTORY::NODE_TYPE_GENRE:
- container->m_ObjectClass.type += ".genre.movieGenre";
- break;
- case VIDEODATABASEDIRECTORY::NODE_TYPE_ACTOR:
- container->m_ObjectClass.type += ".person.videoArtist";
- container->m_Creator =
- StringUtils::Join(
- tag.m_artist,
- settingsComponent->GetAdvancedSettings()->m_videoItemSeparator)
- .c_str();
- container->m_Title = tag.m_strTitle.c_str();
- break;
- case VIDEODATABASEDIRECTORY::NODE_TYPE_SEASONS:
- container->m_ObjectClass.type += ".album.videoAlbum.videoBroadcastSeason";
- if (item.HasVideoInfoTag()) {
- CVideoInfoTag *tag = (CVideoInfoTag*)item.GetVideoInfoTag();
- PopulateObjectFromTag(*tag, *container, &file_path, &resource, quirks);
- }
- break;
- case VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_TVSHOWS:
- container->m_ObjectClass.type += ".album.videoAlbum.videoBroadcastShow";
- if (item.HasVideoInfoTag()) {
- CVideoInfoTag *tag = (CVideoInfoTag*)item.GetVideoInfoTag();
- PopulateObjectFromTag(*tag, *container, &file_path, &resource, quirks);
- }
- break;
- default:
- container->m_ObjectClass.type += ".storageFolder";
- break;
- }
- } else if (item.IsPlayList() || item.IsSmartPlayList()) {
- container->m_ObjectClass.type += ".playlistContainer";
}
+ break;
+ case MUSICDATABASEDIRECTORY::NODE_TYPE_GENRE:
+ container->m_ObjectClass.type += ".genre.musicGenre";
+ break;
+ default:
+ break;
+ }
+ }
+ else if (item.IsVideoDb())
+ {
+ VIDEODATABASEDIRECTORY::NODE_TYPE node =
+ CVideoDatabaseDirectory::GetDirectoryType(item.GetPath());
+ CVideoInfoTag& tag = *item.GetVideoInfoTag();
+ switch (node)
+ {
+ case VIDEODATABASEDIRECTORY::NODE_TYPE_GENRE:
+ container->m_ObjectClass.type += ".genre.movieGenre";
+ break;
+ case VIDEODATABASEDIRECTORY::NODE_TYPE_ACTOR:
+ container->m_ObjectClass.type += ".person.videoArtist";
+ container->m_Creator =
+ StringUtils::Join(tag.m_artist,
+ settingsComponent->GetAdvancedSettings()->m_videoItemSeparator)
+ .c_str();
+ container->m_Title = tag.m_strTitle.c_str();
+ break;
+ case VIDEODATABASEDIRECTORY::NODE_TYPE_SEASONS:
+ container->m_ObjectClass.type += ".album.videoAlbum.videoBroadcastSeason";
+ if (item.HasVideoInfoTag())
+ {
+ CVideoInfoTag* tag = (CVideoInfoTag*)item.GetVideoInfoTag();
+ PopulateObjectFromTag(*tag, *container, &file_path, &resource, quirks);
+ }
+ break;
+ case VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_TVSHOWS:
+ container->m_ObjectClass.type += ".album.videoAlbum.videoBroadcastShow";
+ if (item.HasVideoInfoTag())
+ {
+ CVideoInfoTag* tag = (CVideoInfoTag*)item.GetVideoInfoTag();
+ PopulateObjectFromTag(*tag, *container, &file_path, &resource, quirks);
+ }
+ break;
+ default:
+ container->m_ObjectClass.type += ".storageFolder";
+ break;
+ }
+ }
+ else if (item.IsPlayList() || item.IsSmartPlayList())
+ {
+ container->m_ObjectClass.type += ".playlistContainer";
+ }
- if(quirks & ECLIENTQUIRKS_ONLYSTORAGEFOLDER) {
- container->m_ObjectClass.type = "object.container.storageFolder";
- }
+ if (quirks & ECLIENTQUIRKS_ONLYSTORAGEFOLDER)
+ {
+ container->m_ObjectClass.type = "object.container.storageFolder";
+ }
- /* Get the number of children for this container */
- if (with_count && upnp_server) {
- const NPT_String decodedObjectId = DecodeObjectId(object->m_ObjectID.GetChars());
- if (StringUtils::StartsWithNoCase(decodedObjectId, "virtualpath://")) {
- NPT_LargeSize count = 0;
- NPT_CHECK_LABEL(NPT_File::GetSize(file_path, count), failure);
- container->m_ChildrenCount = (NPT_Int32)count;
- } else {
- /* this should be a standard path */
- //! @todo - get file count of this directory
- }
- }
+ /* Get the number of children for this container */
+ if (with_count && upnp_server)
+ {
+ const NPT_String decodedObjectId = DecodeObjectId(object->m_ObjectID.GetChars());
+ if (StringUtils::StartsWithNoCase(decodedObjectId, "virtualpath://"))
+ {
+ NPT_LargeSize count = 0;
+ NPT_CHECK_LABEL(NPT_File::GetSize(file_path, count), failure);
+ container->m_ChildrenCount = (NPT_Int32)count;
+ }
+ else
+ {
+ /* this should be a standard path */
+ //! @todo - get file count of this directory
+ }
}
+ }
- // set a title for the object
- if (object->m_Title.IsEmpty()) {
- if (!item.GetLabel().empty()) {
- std::string title = item.GetLabel();
- if (item.IsPlayList() || !item.m_bIsFolder) URIUtils::RemoveExtension(title);
- object->m_Title = title.c_str();
- }
+ // set a title for the object
+ if (object->m_Title.IsEmpty())
+ {
+ if (!item.GetLabel().empty())
+ {
+ std::string title = item.GetLabel();
+ if (item.IsPlayList() || !item.m_bIsFolder)
+ URIUtils::RemoveExtension(title);
+ object->m_Title = title.c_str();
}
+ }
- if (upnp_server) {
- // determine the correct artwork for this item
- if (!thumb_loader.IsNull())
- thumb_loader->LoadItem(&item);
+ if (upnp_server)
+ {
+ // determine the correct artwork for this item
+ if (!thumb_loader.IsNull())
+ thumb_loader->LoadItem(&item);
- // we have to decide the best art type to serve to the client - use ContentUtils
- // to get it since it depends on the mediatype of the item being served
- thumb = ContentUtils::GetPreferredArtImage(item);
+ // we have to decide the best art type to serve to the client - use ContentUtils
+ // to get it since it depends on the mediatype of the item being served
+ thumb = ContentUtils::GetPreferredArtImage(item);
- if (!thumb.empty()) {
- PLT_AlbumArtInfo art;
- // Set DLNA profileID by extension, defaulting to JPEG.
- if (URIUtils::HasExtension(thumb, ".png"))
- {
- art.dlna_profile = "PNG_TN";
- }
- else
- {
- art.dlna_profile = "JPEG_TN";
- }
- // append /thumb to the safe resource uri to avoid clients flagging the item with
- // the incorrect mimetype (derived from the file extension)
- art.uri = upnp_server->BuildSafeResourceUri(
- rooturi, (*ips.GetFirstItem()).ToString(),
- std::string(CTextureUtils::GetWrappedImageURL(thumb) + "/thumb").c_str());
- object->m_ExtraInfo.album_arts.Add(art);
- }
+ if (!thumb.empty())
+ {
+ PLT_AlbumArtInfo art;
- for (const auto& itArtwork : item.GetArt())
+ // Get DLNA profileId for the image
+ std::optional<std::string> imageProfile = GetImageDLNAProfile(thumb);
+ if (imageProfile.has_value())
+ {
+ art.dlna_profile = imageProfile.value().c_str();
+ }
+ else
+ {
+ // unknown image format (might be a embed thumb previously extracted and cached - e.g. mp3, flac, mvk, etc)
+ bool needsRecaching;
+ const std::string cachedImagePath =
+ CServiceBroker::GetTextureCache()->CheckCachedImage(thumb, needsRecaching);
+ if (!cachedImagePath.empty())
{
- if (!itArtwork.first.empty() && !itArtwork.second.empty())
+ imageProfile = GetImageDLNAProfile(cachedImagePath);
+ if (imageProfile.has_value())
{
- std::string wrappedUrl = CTextureUtils::GetWrappedImageURL(itArtwork.second);
- object->m_XbmcInfo.artwork.Add(
- itArtwork.first.c_str(),
- upnp_server->BuildSafeResourceUri(rooturi, (*ips.GetFirstItem()).ToString(),
- wrappedUrl.c_str()));
- upnp_server->AddSafeResourceUri(object, rooturi, ips, wrappedUrl.c_str(),
- ("xbmc.org:*:" + itArtwork.first + ":*").c_str());
+ thumb = cachedImagePath;
+ art.dlna_profile = imageProfile.value().c_str();
}
}
+ }
+
+ // Always default to JPEG if the profile could not be found
+ if (art.dlna_profile.IsEmpty())
+ {
+ art.dlna_profile = "JPEG_TN";
+ }
+
+ art.uri = upnp_server->BuildSafeResourceUri(rooturi, (*ips.GetFirstItem()).ToString(),
+ CTextureUtils::GetWrappedImageURL(thumb).c_str());
+
+ object->m_ExtraInfo.album_arts.Add(art);
}
- // look for and add external subtitle if we are processing a video file and
- // we are being called by a UPnP player or renderer or the user has chosen
- // to look for external subtitles
- if (upnp_server != NULL && item.IsVideo() &&
- (upnp_service == UPnPPlayer || upnp_service == UPnPRenderer ||
- settings->GetBool(CSettings::SETTING_SERVICES_UPNPLOOKFOREXTERNALSUBTITLES)))
+ for (const auto& itArtwork : item.GetArt())
{
- // find any available external subtitles
- std::vector<std::string> filenames;
- std::vector<std::string> subtitles;
- CUtil::ScanForExternalSubtitles(file_path.GetChars(), filenames);
+ if (!itArtwork.first.empty() && !itArtwork.second.empty())
+ {
+ std::string wrappedUrl = CTextureUtils::GetWrappedImageURL(itArtwork.second);
+ object->m_XbmcInfo.artwork.Add(
+ itArtwork.first.c_str(),
+ upnp_server->BuildSafeResourceUri(rooturi, (*ips.GetFirstItem()).ToString(),
+ wrappedUrl.c_str()));
+ upnp_server->AddSafeResourceUri(object, rooturi, ips, wrappedUrl.c_str(),
+ ("xbmc.org:*:" + itArtwork.first + ":*").c_str());
+ }
+ }
+ }
- std::string ext;
- for (unsigned int i = 0; i < filenames.size(); i++)
- {
- ext = URIUtils::GetExtension(filenames[i]).c_str();
- ext = ext.substr(1);
- std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
- /* Hardcoded check for extension is not the best way, but it can't be allowed to pass all
+ // look for and add external subtitle if we are processing a video file and
+ // we are being called by a UPnP player or renderer or the user has chosen
+ // to look for external subtitles
+ if (upnp_server != NULL && item.IsVideo() &&
+ (upnp_service == UPnPPlayer || upnp_service == UPnPRenderer ||
+ settings->GetBool(CSettings::SETTING_SERVICES_UPNPLOOKFOREXTERNALSUBTITLES)))
+ {
+ // find any available external subtitles
+ std::vector<std::string> filenames;
+ std::vector<std::string> subtitles;
+ CUtil::ScanForExternalSubtitles(file_path.GetChars(), filenames);
+
+ std::string ext;
+ for (unsigned int i = 0; i < filenames.size(); i++)
+ {
+ ext = URIUtils::GetExtension(filenames[i]).c_str();
+ ext = ext.substr(1);
+ std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
+ /* Hardcoded check for extension is not the best way, but it can't be allowed to pass all
subtitle extension (ex. rar or zip). There are the most popular extensions support by UPnP devices.*/
- for (std::string_view type : SupportedSubFormats)
- {
- if (type == ext)
- {
- subtitles.push_back(filenames[i]);
- }
- }
+ for (std::string_view type : SupportedSubFormats)
+ {
+ if (type == ext)
+ {
+ subtitles.push_back(filenames[i]);
}
+ }
+ }
- std::string subtitlePath;
+ std::string subtitlePath;
- if (subtitles.size() == 1)
- {
- subtitlePath = subtitles[0];
- }
- else if (!subtitles.empty())
- {
- std::string preferredLanguage{"en"};
+ if (subtitles.size() == 1)
+ {
+ subtitlePath = subtitles[0];
+ }
+ else if (!subtitles.empty())
+ {
+ std::string preferredLanguage{"en"};
- /* trying to find subtitle with preferred language settings */
- auto setting = settings->GetSetting("locale.subtitlelanguage");
- if (!setting)
- CLog::Log(LOGERROR, "Failed to load setting for: {}", "locale.subtitlelanguage");
- else
- preferredLanguage = setting->ToString();
+ /* trying to find subtitle with preferred language settings */
+ auto setting = settings->GetSetting("locale.subtitlelanguage");
+ if (!setting)
+ CLog::Log(LOGERROR, "Failed to load setting for: {}", "locale.subtitlelanguage");
+ else
+ preferredLanguage = setting->ToString();
- std::string preferredLanguageCode;
- g_LangCodeExpander.ConvertToISO6392B(preferredLanguage, preferredLanguageCode);
+ std::string preferredLanguageCode;
+ g_LangCodeExpander.ConvertToISO6392B(preferredLanguage, preferredLanguageCode);
- for (unsigned int i = 0; i < subtitles.size(); i++)
- {
- ExternalStreamInfo info =
- CUtil::GetExternalStreamDetailsFromFilename(file_path.GetChars(), subtitles[i]);
-
- if (preferredLanguageCode == info.language)
- {
- subtitlePath = subtitles[i];
- break;
- }
- }
- /* if not found subtitle with preferred language, get the first one */
- if (subtitlePath.empty())
- {
- subtitlePath = subtitles[0];
- }
- }
+ for (unsigned int i = 0; i < subtitles.size(); i++)
+ {
+ ExternalStreamInfo info =
+ CUtil::GetExternalStreamDetailsFromFilename(file_path.GetChars(), subtitles[i]);
- if (!subtitlePath.empty())
+ if (preferredLanguageCode == info.language)
{
- /* subtitles are added as 2 resources, 2 sec resources and 1 addon to video resource, to be compatible with
+ subtitlePath = subtitles[i];
+ break;
+ }
+ }
+ /* if not found subtitle with preferred language, get the first one */
+ if (subtitlePath.empty())
+ {
+ subtitlePath = subtitles[0];
+ }
+ }
+
+ if (!subtitlePath.empty())
+ {
+ /* subtitles are added as 2 resources, 2 sec resources and 1 addon to video resource, to be compatible with
the most of the devices; all UPnP devices take the last one it could handle,
and skip ones it doesn't "understand" */
- // add subtitle resource with standard protocolInfo
- NPT_String protocolInfo = GetProtocolInfo(CFileItem(subtitlePath, false), "http", context);
- upnp_server->AddSafeResourceUri(object, rooturi, ips, NPT_String(subtitlePath.c_str()), protocolInfo);
- // add subtitle resource with smi/caption protocol info (some devices)
- PLT_ProtocolInfo protInfo = PLT_ProtocolInfo(protocolInfo);
- protocolInfo = protInfo.GetProtocol() + ":" + protInfo.GetMask() + ":smi/caption:" + protInfo.GetExtra();
- upnp_server->AddSafeResourceUri(object, rooturi, ips, NPT_String(subtitlePath.c_str()), protocolInfo);
-
- ext = URIUtils::GetExtension(subtitlePath).c_str();
- ext = ext.substr(1);
- std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
-
- NPT_String subtitle_uri = object->m_Resources[object->m_Resources.GetItemCount() - 1].m_Uri;
-
- // add subtitle to video resource (the first one) (for some devices)
- object->m_Resources[0].m_CustomData["xmlns:pv"] = "http://www.pv.com/pvns/";
- object->m_Resources[0].m_CustomData["pv:subtitleFileUri"] = subtitle_uri;
- object->m_Resources[0].m_CustomData["pv:subtitleFileType"] = ext.c_str();
-
- // for samsung devices
- PLT_SecResource sec_res;
- sec_res.name = "CaptionInfoEx";
- sec_res.value = subtitle_uri;
- sec_res.attributes["type"] = ext.c_str();
- object->m_SecResources.Add(sec_res);
- sec_res.name = "CaptionInfo";
- object->m_SecResources.Add(sec_res);
-
- // adding subtitle uri for movie md5, for later use in http response
- NPT_String movie_md5 = object->m_Resources[0].m_Uri;
- movie_md5 = movie_md5.Right(movie_md5.GetLength() - movie_md5.Find("/%25/") - 5);
- upnp_server->AddSubtitleUriForSecResponse(movie_md5, subtitle_uri);
- }
+ // add subtitle resource with standard protocolInfo
+ NPT_String protocolInfo = GetProtocolInfo(CFileItem(subtitlePath, false), "http", context);
+ upnp_server->AddSafeResourceUri(object, rooturi, ips, NPT_String(subtitlePath.c_str()),
+ protocolInfo);
+ // add subtitle resource with smi/caption protocol info (some devices)
+ PLT_ProtocolInfo protInfo = PLT_ProtocolInfo(protocolInfo);
+ protocolInfo =
+ protInfo.GetProtocol() + ":" + protInfo.GetMask() + ":smi/caption:" + protInfo.GetExtra();
+ upnp_server->AddSafeResourceUri(object, rooturi, ips, NPT_String(subtitlePath.c_str()),
+ protocolInfo);
+
+ ext = URIUtils::GetExtension(subtitlePath).c_str();
+ ext = ext.substr(1);
+ std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
+
+ NPT_String subtitle_uri = object->m_Resources[object->m_Resources.GetItemCount() - 1].m_Uri;
+
+ // add subtitle to video resource (the first one) (for some devices)
+ object->m_Resources[0].m_CustomData["xmlns:pv"] = "http://www.pv.com/pvns/";
+ object->m_Resources[0].m_CustomData["pv:subtitleFileUri"] = subtitle_uri;
+ object->m_Resources[0].m_CustomData["pv:subtitleFileType"] = ext.c_str();
+
+ // for samsung devices
+ PLT_SecResource sec_res;
+ sec_res.name = "CaptionInfoEx";
+ sec_res.value = subtitle_uri;
+ sec_res.attributes["type"] = ext.c_str();
+ object->m_SecResources.Add(sec_res);
+ sec_res.name = "CaptionInfo";
+ object->m_SecResources.Add(sec_res);
+
+ // adding subtitle uri for movie md5, for later use in http response
+ NPT_String movie_md5 = object->m_Resources[0].m_Uri;
+ movie_md5 = movie_md5.Right(movie_md5.GetLength() - movie_md5.Find("/%25/") - 5);
+ upnp_server->AddSubtitleUriForSecResponse(movie_md5, subtitle_uri);
}
+ }
- return object;
+ return object;
failure:
- delete object;
- return NULL;
+ delete object;
+ return NULL;
}
/*----------------------------------------------------------------------
| CUPnPServer::CorrectAllItemsSortHack
+---------------------------------------------------------------------*/
-const std::string&
-CorrectAllItemsSortHack(const std::string &item)
+const std::string& CorrectAllItemsSortHack(const std::string& item)
{
- // This is required as in order for the "* All Albums" etc. items to sort
- // correctly, they must have fake artist/album etc. information generated.
- // This looks nasty if we attempt to render it to the GUI, thus this (further)
- // workaround
- if ((item.size() == 1 && item[0] == 0x01) || (item.size() > 1 && ((unsigned char) item[1]) == 0xff))
- return StringUtils::Empty;
-
- return item;
+ // This is required as in order for the "* All Albums" etc. items to sort
+ // correctly, they must have fake artist/album etc. information generated.
+ // This looks nasty if we attempt to render it to the GUI, thus this (further)
+ // workaround
+ if ((item.size() == 1 && item[0] == 0x01) ||
+ (item.size() > 1 && ((unsigned char)item[1]) == 0xff))
+ return StringUtils::Empty;
+
+ return item;
}
-int
-PopulateTagFromObject(CMusicInfoTag& tag,
- PLT_MediaObject& object,
- PLT_MediaItemResource* resource /* = NULL */,
- UPnPService service /* = UPnPServiceNone */)
+int PopulateTagFromObject(CMusicInfoTag& tag,
+ PLT_MediaObject& object,
+ PLT_MediaItemResource* resource /* = NULL */,
+ UPnPService service /* = UPnPServiceNone */)
{
- tag.SetTitle((const char*)object.m_Title);
- tag.SetArtist((const char*)object.m_Creator);
- for(PLT_PersonRoles::Iterator it = object.m_People.artists.GetFirstItem(); it; it++) {
- if (it->role == "") tag.SetArtist((const char*)it->name);
- else if(it->role == "Performer") tag.SetArtist((const char*)it->name);
- else if(it->role == "AlbumArtist") tag.SetAlbumArtist((const char*)it->name);
- }
- tag.SetTrackNumber(object.m_MiscInfo.original_track_number);
+ tag.SetTitle((const char*)object.m_Title);
+ tag.SetArtist((const char*)object.m_Creator);
+ for (PLT_PersonRoles::Iterator it = object.m_People.artists.GetFirstItem(); it; it++)
+ {
+ if (it->role == "")
+ tag.SetArtist((const char*)it->name);
+ else if (it->role == "Performer")
+ tag.SetArtist((const char*)it->name);
+ else if (it->role == "AlbumArtist")
+ tag.SetAlbumArtist((const char*)it->name);
+ }
+ tag.SetTrackNumber(object.m_MiscInfo.original_track_number);
- for (NPT_List<NPT_String>::Iterator it = object.m_Affiliation.genres.GetFirstItem(); it; it++) {
- // ignore single "Unknown" genre inserted by Platinum
- if (it == object.m_Affiliation.genres.GetFirstItem() && object.m_Affiliation.genres.GetItemCount() == 1 &&
- *it == "Unknown")
- break;
+ for (NPT_List<NPT_String>::Iterator it = object.m_Affiliation.genres.GetFirstItem(); it; it++)
+ {
+ // ignore single "Unknown" genre inserted by Platinum
+ if (it == object.m_Affiliation.genres.GetFirstItem() &&
+ object.m_Affiliation.genres.GetItemCount() == 1 && *it == "Unknown")
+ break;
- tag.SetGenre((const char*) *it);
- }
+ tag.SetGenre((const char*)*it);
+ }
- tag.SetAlbum((const char*)object.m_Affiliation.album);
- CDateTime last;
- last.SetFromW3CDateTime((const char*)object.m_MiscInfo.last_time);
- tag.SetLastPlayed(last);
- tag.SetPlayCount(object.m_MiscInfo.play_count);
- if(resource)
- tag.SetDuration(resource->m_Duration);
- tag.SetLoaded();
- return NPT_SUCCESS;
+ tag.SetAlbum((const char*)object.m_Affiliation.album);
+ CDateTime last;
+ last.SetFromW3CDateTime((const char*)object.m_MiscInfo.last_time);
+ tag.SetLastPlayed(last);
+ tag.SetPlayCount(object.m_MiscInfo.play_count);
+ if (resource)
+ tag.SetDuration(resource->m_Duration);
+ tag.SetLoaded();
+ return NPT_SUCCESS;
}
-int
-PopulateTagFromObject(CVideoInfoTag& tag,
- PLT_MediaObject& object,
- PLT_MediaItemResource* resource /* = NULL */,
- UPnPService service /* = UPnPServiceNone */)
+int PopulateTagFromObject(CVideoInfoTag& tag,
+ PLT_MediaObject& object,
+ PLT_MediaItemResource* resource /* = NULL */,
+ UPnPService service /* = UPnPServiceNone */)
{
- CDateTime date;
- date.SetFromW3CDate((const char*)object.m_Date);
+ CDateTime date;
+ date.SetFromW3CDate((const char*)object.m_Date);
- if(!object.m_Recorded.program_title.IsEmpty() || object.m_ObjectClass.type == "object.item.videoItem.videoBroadcast")
- {
- tag.m_type = MediaTypeEpisode;
- tag.m_strShowTitle = object.m_Recorded.series_title;
- if (date.IsValid())
- tag.m_firstAired = date;
+ if (!object.m_Recorded.program_title.IsEmpty() ||
+ object.m_ObjectClass.type == "object.item.videoItem.videoBroadcast")
+ {
+ tag.m_type = MediaTypeEpisode;
+ tag.m_strShowTitle = object.m_Recorded.series_title;
+ if (date.IsValid())
+ tag.m_firstAired = date;
- int title = object.m_Recorded.program_title.Find(" : ");
- if (title >= 0)
- tag.m_strTitle = object.m_Recorded.program_title.SubString(title + 3);
- else
- tag.m_strTitle = object.m_Recorded.program_title;
-
- int episode;
- int season;
- if (object.m_Recorded.episode_number > 0 && object.m_Recorded.episode_season < (NPT_UInt32)-1) {
- tag.m_iEpisode = object.m_Recorded.episode_number;
- tag.m_iSeason = object.m_Recorded.episode_season;
- } else if(sscanf(object.m_Recorded.program_title, "S%2dE%2d", &season, &episode) == 2 && title >= 0) {
- tag.m_iEpisode = episode;
- tag.m_iSeason = season;
- } else {
- tag.m_iSeason = object.m_Recorded.episode_number / 100;
- tag.m_iEpisode = object.m_Recorded.episode_number % 100;
- }
- }
- else {
- tag.m_strTitle = object.m_Title;
- if (date.IsValid())
- tag.m_premiered = date;
-
- if (!object.m_Recorded.series_title.IsEmpty()) {
- if (object.m_ObjectClass.type == "object.container.album.videoAlbum.videoBroadcastSeason") {
- tag.m_type = MediaTypeSeason;
- tag.m_iSeason = object.m_Recorded.episode_season;
- tag.m_strShowTitle = object.m_Recorded.series_title;
- }
- else {
- tag.m_type = MediaTypeTvShow;
- tag.m_strShowTitle = object.m_Title;
- }
-
- if (object.m_Recorded.episode_count > 0)
- tag.m_iEpisode = object.m_Recorded.episode_count;
- else
- tag.m_iEpisode = object.m_Recorded.episode_number;
- }
- else if(object.m_ObjectClass.type == "object.item.videoItem.musicVideoClip") {
- tag.m_type = MediaTypeMusicVideo;
-
- if (object.m_People.artists.GetItemCount() > 0) {
- for (unsigned int index = 0; index < object.m_People.artists.GetItemCount(); index++)
- tag.m_artist.emplace_back(object.m_People.artists.GetItem(index)->name.GetChars());
- }
- else if (!object.m_Creator.IsEmpty() && object.m_Creator != "Unknown")
- tag.m_artist = StringUtils::Split(object.m_Creator.GetChars(), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
- tag.m_strAlbum = object.m_Affiliation.album;
- }
- else
- tag.m_type = MediaTypeMovie;
+ int title = object.m_Recorded.program_title.Find(" : ");
+ if (title >= 0)
+ tag.m_strTitle = object.m_Recorded.program_title.SubString(title + 3);
+ else
+ tag.m_strTitle = object.m_Recorded.program_title;
- tag.m_strTitle = object.m_Title;
- if (date.IsValid())
- tag.SetPremiered(date);
+ int episode;
+ int season;
+ if (object.m_Recorded.episode_number > 0 && object.m_Recorded.episode_season < (NPT_UInt32)-1)
+ {
+ tag.m_iEpisode = object.m_Recorded.episode_number;
+ tag.m_iSeason = object.m_Recorded.episode_season;
}
-
- for (unsigned int index = 0; index < object.m_People.publisher.GetItemCount(); index++)
- tag.m_studio.emplace_back(object.m_People.publisher.GetItem(index)->GetChars());
-
- tag.m_dateAdded.SetFromW3CDate((const char*)object.m_XbmcInfo.date_added);
- tag.SetRating(object.m_XbmcInfo.rating, object.m_XbmcInfo.votes);
- tag.SetUniqueID(object.m_XbmcInfo.unique_identifier.GetChars());
- for (unsigned int index = 0; index < object.m_XbmcInfo.countries.GetItemCount(); index++)
- tag.m_country.emplace_back(object.m_XbmcInfo.countries.GetItem(index)->GetChars());
- tag.m_iUserRating = object.m_XbmcInfo.user_rating;
-
- for (unsigned int index = 0; index < object.m_Affiliation.genres.GetItemCount(); index++)
+ else if (sscanf(object.m_Recorded.program_title, "S%2dE%2d", &season, &episode) == 2 &&
+ title >= 0)
{
- // ignore single "Unknown" genre inserted by Platinum
- if (index == 0 && object.m_Affiliation.genres.GetItemCount() == 1 &&
- *object.m_Affiliation.genres.GetItem(index) == "Unknown")
- break;
-
- tag.m_genre.emplace_back(object.m_Affiliation.genres.GetItem(index)->GetChars());
+ tag.m_iEpisode = episode;
+ tag.m_iSeason = season;
}
- for (unsigned int index = 0; index < object.m_People.directors.GetItemCount(); index++)
- tag.m_director.emplace_back(object.m_People.directors.GetItem(index)->name.GetChars());
- for (unsigned int index = 0; index < object.m_People.authors.GetItemCount(); index++)
- tag.m_writingCredits.emplace_back(object.m_People.authors.GetItem(index)->name.GetChars());
- for (unsigned int index = 0; index < object.m_People.actors.GetItemCount(); index++)
+ else
{
- SActorInfo info;
- info.strName = object.m_People.actors.GetItem(index)->name;
- info.strRole = object.m_People.actors.GetItem(index)->role;
- tag.m_cast.push_back(info);
+ tag.m_iSeason = object.m_Recorded.episode_number / 100;
+ tag.m_iEpisode = object.m_Recorded.episode_number % 100;
}
- tag.m_strTagLine = object.m_Description.description;
- tag.m_strPlot = object.m_Description.long_description;
- tag.m_strMPAARating = object.m_Description.rating;
- tag.m_strShowTitle = object.m_Recorded.series_title;
- tag.m_lastPlayed.SetFromW3CDateTime((const char*)object.m_MiscInfo.last_time);
- tag.SetPlayCount(object.m_MiscInfo.play_count);
+ }
+ else
+ {
+ tag.m_strTitle = object.m_Title;
+ if (date.IsValid())
+ tag.m_premiered = date;
- if(resource)
+ if (!object.m_Recorded.series_title.IsEmpty())
{
- if (resource->m_Duration)
- tag.SetDuration(resource->m_Duration);
- if (object.m_MiscInfo.last_position > 0 )
+ if (object.m_ObjectClass.type == "object.container.album.videoAlbum.videoBroadcastSeason")
{
- tag.SetResumePoint(object.m_MiscInfo.last_position,
- resource->m_Duration,
- object.m_XbmcInfo.last_playerstate.GetChars());
+ tag.m_type = MediaTypeSeason;
+ tag.m_iSeason = object.m_Recorded.episode_season;
+ tag.m_strShowTitle = object.m_Recorded.series_title;
}
- if (!resource->m_Resolution.IsEmpty())
+ else
{
- int width, height;
- if (sscanf(resource->m_Resolution, "%dx%d", &width, &height) == 2)
- {
- CStreamDetailVideo* detail = new CStreamDetailVideo;
- detail->m_iWidth = width;
- detail->m_iHeight = height;
- detail->m_iDuration = tag.GetDuration();
- tag.m_streamDetails.AddStream(detail);
- }
+ tag.m_type = MediaTypeTvShow;
+ tag.m_strShowTitle = object.m_Title;
}
- if (resource->m_NbAudioChannels > 0)
+
+ if (object.m_Recorded.episode_count > 0)
+ tag.m_iEpisode = object.m_Recorded.episode_count;
+ else
+ tag.m_iEpisode = object.m_Recorded.episode_number;
+ }
+ else if (object.m_ObjectClass.type == "object.item.videoItem.musicVideoClip")
+ {
+ tag.m_type = MediaTypeMusicVideo;
+
+ if (object.m_People.artists.GetItemCount() > 0)
{
- CStreamDetailAudio* detail = new CStreamDetailAudio;
- detail->m_iChannels = resource->m_NbAudioChannels;
+ for (unsigned int index = 0; index < object.m_People.artists.GetItemCount(); index++)
+ tag.m_artist.emplace_back(object.m_People.artists.GetItem(index)->name.GetChars());
+ }
+ else if (!object.m_Creator.IsEmpty() && object.m_Creator != "Unknown")
+ tag.m_artist = StringUtils::Split(
+ object.m_Creator.GetChars(),
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
+ tag.m_strAlbum = object.m_Affiliation.album;
+ }
+ else
+ tag.m_type = MediaTypeMovie;
+
+ tag.m_strTitle = object.m_Title;
+ if (date.IsValid())
+ tag.SetPremiered(date);
+ }
+
+ for (unsigned int index = 0; index < object.m_People.publisher.GetItemCount(); index++)
+ tag.m_studio.emplace_back(object.m_People.publisher.GetItem(index)->GetChars());
+
+ tag.m_dateAdded.SetFromW3CDate((const char*)object.m_XbmcInfo.date_added);
+ tag.SetRating(object.m_XbmcInfo.rating, object.m_XbmcInfo.votes);
+ tag.SetUniqueID(object.m_XbmcInfo.unique_identifier.GetChars());
+ for (unsigned int index = 0; index < object.m_XbmcInfo.countries.GetItemCount(); index++)
+ tag.m_country.emplace_back(object.m_XbmcInfo.countries.GetItem(index)->GetChars());
+ tag.m_iUserRating = object.m_XbmcInfo.user_rating;
+
+ for (unsigned int index = 0; index < object.m_Affiliation.genres.GetItemCount(); index++)
+ {
+ // ignore single "Unknown" genre inserted by Platinum
+ if (index == 0 && object.m_Affiliation.genres.GetItemCount() == 1 &&
+ *object.m_Affiliation.genres.GetItem(index) == "Unknown")
+ break;
+
+ tag.m_genre.emplace_back(object.m_Affiliation.genres.GetItem(index)->GetChars());
+ }
+ for (unsigned int index = 0; index < object.m_People.directors.GetItemCount(); index++)
+ tag.m_director.emplace_back(object.m_People.directors.GetItem(index)->name.GetChars());
+ for (unsigned int index = 0; index < object.m_People.authors.GetItemCount(); index++)
+ tag.m_writingCredits.emplace_back(object.m_People.authors.GetItem(index)->name.GetChars());
+ for (unsigned int index = 0; index < object.m_People.actors.GetItemCount(); index++)
+ {
+ SActorInfo info;
+ info.strName = object.m_People.actors.GetItem(index)->name;
+ info.strRole = object.m_People.actors.GetItem(index)->role;
+ tag.m_cast.push_back(info);
+ }
+ tag.m_strTagLine = object.m_Description.description;
+ tag.m_strPlot = object.m_Description.long_description;
+ tag.m_strMPAARating = object.m_Description.rating;
+ tag.m_strShowTitle = object.m_Recorded.series_title;
+ tag.m_lastPlayed.SetFromW3CDateTime((const char*)object.m_MiscInfo.last_time);
+ tag.SetPlayCount(object.m_MiscInfo.play_count);
+
+ if (resource)
+ {
+ if (resource->m_Duration)
+ tag.SetDuration(resource->m_Duration);
+ if (object.m_MiscInfo.last_position > 0)
+ {
+ tag.SetResumePoint(object.m_MiscInfo.last_position, resource->m_Duration,
+ object.m_XbmcInfo.last_playerstate.GetChars());
+ }
+ if (!resource->m_Resolution.IsEmpty())
+ {
+ int width, height;
+ if (sscanf(resource->m_Resolution, "%dx%d", &width, &height) == 2)
+ {
+ CStreamDetailVideo* detail = new CStreamDetailVideo;
+ detail->m_iWidth = width;
+ detail->m_iHeight = height;
+ detail->m_iDuration = tag.GetDuration();
tag.m_streamDetails.AddStream(detail);
}
}
- return NPT_SUCCESS;
+ if (resource->m_NbAudioChannels > 0)
+ {
+ CStreamDetailAudio* detail = new CStreamDetailAudio;
+ detail->m_iChannels = resource->m_NbAudioChannels;
+ tag.m_streamDetails.AddStream(detail);
+ }
+ }
+ return NPT_SUCCESS;
}
std::shared_ptr<CFileItem> BuildObject(PLT_MediaObject* entry,
@@ -1005,38 +1155,44 @@ std::shared_ptr<CFileItem> BuildObject(PLT_MediaObject* entry,
pItem->m_bIsFolder = entry->IsContainer();
// if it's a container, format a string as upnp://uuid/object_id
- if (pItem->m_bIsFolder) {
+ if (pItem->m_bIsFolder)
+ {
// look for metadata
- if( ObjectClass.StartsWith("object.container.album.videoalbum") ) {
+ if (ObjectClass.StartsWith("object.container.album.videoalbum"))
+ {
pItem->SetLabelPreformatted(false);
UPNP::PopulateTagFromObject(*pItem->GetVideoInfoTag(), *entry, NULL, upnp_service);
-
- } else if( ObjectClass.StartsWith("object.container.album.photoalbum")) {
+ }
+ else if (ObjectClass.StartsWith("object.container.album.photoalbum"))
+ {
//CPictureInfoTag* tag = pItem->GetPictureInfoTag();
-
- } else if( ObjectClass.StartsWith("object.container.album") ) {
+ }
+ else if (ObjectClass.StartsWith("object.container.album"))
+ {
pItem->SetLabelPreformatted(false);
UPNP::PopulateTagFromObject(*pItem->GetMusicInfoTag(), *entry, NULL, upnp_service);
}
-
- } else {
- bool audio = false
- , image = false
- , video = false;
+ }
+ else
+ {
+ bool audio = false, image = false, video = false;
// set a general content type
const char* content = NULL;
- if (ObjectClass.StartsWith("object.item.videoitem")) {
+ if (ObjectClass.StartsWith("object.item.videoitem"))
+ {
pItem->SetMimeType("video/octet-stream");
content = "video";
video = true;
}
- else if(ObjectClass.StartsWith("object.item.audioitem")) {
+ else if (ObjectClass.StartsWith("object.item.audioitem"))
+ {
pItem->SetMimeType("audio/octet-stream");
content = "audio";
audio = true;
}
- else if(ObjectClass.StartsWith("object.item.imageitem")) {
+ else if (ObjectClass.StartsWith("object.item.imageitem"))
+ {
pItem->SetMimeType("image/octet-stream");
content = "image";
image = true;
@@ -1044,32 +1200,38 @@ std::shared_ptr<CFileItem> BuildObject(PLT_MediaObject* entry,
// attempt to find a valid resource (may be multiple)
PLT_MediaItemResource resource, *res = NULL;
- if(NPT_SUCCEEDED(NPT_ContainerFind(entry->m_Resources,
- CResourceFinder("http-get", content), resource))) {
+ if (NPT_SUCCEEDED(
+ NPT_ContainerFind(entry->m_Resources, CResourceFinder("http-get", content), resource)))
+ {
// set metadata
- if (resource.m_Size != (NPT_LargeSize)-1) {
- pItem->m_dwSize = resource.m_Size;
+ if (resource.m_Size != (NPT_LargeSize)-1)
+ {
+ pItem->m_dwSize = resource.m_Size;
}
res = &resource;
}
// look for metadata
- if(video) {
- pItem->SetLabelPreformatted(false);
- UPNP::PopulateTagFromObject(*pItem->GetVideoInfoTag(), *entry, res, upnp_service);
-
- } else if(audio) {
- pItem->SetLabelPreformatted(false);
- UPNP::PopulateTagFromObject(*pItem->GetMusicInfoTag(), *entry, res, upnp_service);
-
- } else if(image) {
+ if (video)
+ {
+ pItem->SetLabelPreformatted(false);
+ UPNP::PopulateTagFromObject(*pItem->GetVideoInfoTag(), *entry, res, upnp_service);
+ }
+ else if (audio)
+ {
+ pItem->SetLabelPreformatted(false);
+ UPNP::PopulateTagFromObject(*pItem->GetMusicInfoTag(), *entry, res, upnp_service);
+ }
+ else if (image)
+ {
//! @todo fill pictureinfotag?
GetResource(entry, *pItem);
}
}
// look for date?
- if(entry->m_Description.date.GetLength()) {
+ if (entry->m_Description.date.GetLength())
+ {
KODI::TIME::SystemTime time = {};
sscanf(entry->m_Description.date, "%hu-%hu-%huT%hu:%hu:%hu", &time.year, &time.month, &time.day,
&time.hour, &time.minute, &time.second);
@@ -1077,24 +1239,26 @@ std::shared_ptr<CFileItem> BuildObject(PLT_MediaObject* entry,
}
// if there is a thumbnail available set it here
- if(entry->m_ExtraInfo.album_arts.GetItem(0))
+ if (entry->m_ExtraInfo.album_arts.GetItem(0))
// only considers first album art
- pItem->SetArt("thumb", (const char*) entry->m_ExtraInfo.album_arts.GetItem(0)->uri);
- else if(entry->m_Description.icon_uri.GetLength())
- pItem->SetArt("thumb", (const char*) entry->m_Description.icon_uri);
+ pItem->SetArt("thumb", (const char*)entry->m_ExtraInfo.album_arts.GetItem(0)->uri);
+ else if (entry->m_Description.icon_uri.GetLength())
+ pItem->SetArt("thumb", (const char*)entry->m_Description.icon_uri);
for (unsigned int index = 0; index < entry->m_XbmcInfo.artwork.GetItemCount(); index++)
- pItem->SetArt(entry->m_XbmcInfo.artwork.GetItem(index)->type.GetChars(),
- entry->m_XbmcInfo.artwork.GetItem(index)->url.GetChars());
+ pItem->SetArt(entry->m_XbmcInfo.artwork.GetItem(index)->type.GetChars(),
+ entry->m_XbmcInfo.artwork.GetItem(index)->url.GetChars());
// set the watched overlay, as this will not be set later due to
// content set on file item list
- if (pItem->HasVideoInfoTag()) {
+ if (pItem->HasVideoInfoTag())
+ {
int episodes = pItem->GetVideoInfoTag()->m_iEpisode;
- int played = pItem->GetVideoInfoTag()->GetPlayCount();
+ int played = pItem->GetVideoInfoTag()->GetPlayCount();
const std::string& type = pItem->GetVideoInfoTag()->m_type;
bool watched(false);
- if (type == MediaTypeTvShow || type == MediaTypeSeason) {
+ if (type == MediaTypeTvShow || type == MediaTypeSeason)
+ {
pItem->SetProperty("totalepisodes", episodes);
pItem->SetProperty("numepisodes", episodes);
pItem->SetProperty("watchedepisodes", played);
@@ -1115,38 +1279,38 @@ struct ResourcePrioritySort
explicit ResourcePrioritySort(const PLT_MediaObject* entry)
{
if (entry->m_ObjectClass.type.StartsWith("object.item.audioItem"))
- m_content = "audio";
+ m_content = "audio";
else if (entry->m_ObjectClass.type.StartsWith("object.item.imageItem"))
- m_content = "image";
+ m_content = "image";
else if (entry->m_ObjectClass.type.StartsWith("object.item.videoItem"))
- m_content = "video";
+ m_content = "video";
}
- int GetPriority(const PLT_MediaItemResource& res) const
+ int GetPriority(const PLT_MediaItemResource& res) const
{
int prio = 0;
if (m_content != "" && res.m_ProtocolInfo.GetContentType().StartsWith(m_content))
- prio += 400;
+ prio += 400;
NPT_Url url(res.m_Uri);
if (URIUtils::IsHostOnLAN(url.GetHost().GetChars(), LanCheckMode::ONLY_LOCAL_SUBNET))
prio += 300;
if (res.m_ProtocolInfo.GetProtocol() == "xbmc-get")
- prio += 200;
+ prio += 200;
else if (res.m_ProtocolInfo.GetProtocol() == "http-get")
- prio += 100;
+ prio += 100;
return prio;
}
int operator()(const PLT_MediaItemResource& lh, const PLT_MediaItemResource& rh) const
{
- if(GetPriority(lh) < GetPriority(rh))
- return 1;
+ if (GetPriority(lh) < GetPriority(rh))
+ return 1;
else
- return 0;
+ return 0;
}
NPT_String m_content;
@@ -1159,17 +1323,18 @@ bool GetResource(const PLT_MediaObject* entry, CFileItem& item)
PLT_MediaItemResource resource;
// store original path so we remember it
- item.SetProperty("original_listitem_url", item.GetPath());
+ item.SetProperty("original_listitem_url", item.GetPath());
item.SetProperty("original_listitem_mime", item.GetMimeType());
// get a sorted list based on our preference
NPT_List<PLT_MediaItemResource> sorted;
- for (NPT_Cardinal i = 0; i < entry->m_Resources.GetItemCount(); ++i) {
- sorted.Add(entry->m_Resources[i]);
+ for (NPT_Cardinal i = 0; i < entry->m_Resources.GetItemCount(); ++i)
+ {
+ sorted.Add(entry->m_Resources[i]);
}
sorted.Sort(ResourcePrioritySort(entry));
- if(sorted.GetItemCount() == 0)
+ if (sorted.GetItemCount() == 0)
return false;
resource = *sorted.GetFirstItem();
@@ -1177,13 +1342,15 @@ bool GetResource(const PLT_MediaObject* entry, CFileItem& item)
// if it's an item, path is the first url to the item
// we hope the server made the first one reachable for us
// (it could be a format we dont know how to play however)
- item.SetDynPath((const char*) resource.m_Uri);
+ item.SetDynPath((const char*)resource.m_Uri);
// look for content type in protocol info
- if (resource.m_ProtocolInfo.IsValid()) {
+ if (resource.m_ProtocolInfo.IsValid())
+ {
logger->debug("resource protocol info '{}'", (const char*)(resource.m_ProtocolInfo.ToString()));
- if (resource.m_ProtocolInfo.GetContentType().Compare("application/octet-stream") != 0) {
+ if (resource.m_ProtocolInfo.GetContentType().Compare("application/octet-stream") != 0)
+ {
item.SetMimeType((const char*)resource.m_ProtocolInfo.GetContentType());
}
@@ -1192,17 +1359,19 @@ bool GetResource(const PLT_MediaObject* entry, CFileItem& item)
{
item.SetArt("thumb", std::string(resource.m_Uri));
}
- } else {
+ }
+ else
+ {
logger->error("invalid protocol info '{}'", (const char*)(resource.m_ProtocolInfo.ToString()));
}
// look for subtitles
unsigned subIdx = 0;
- for(unsigned r = 0; r < entry->m_Resources.GetItemCount(); r++)
+ for (unsigned r = 0; r < entry->m_Resources.GetItemCount(); r++)
{
- const PLT_MediaItemResource& res = entry->m_Resources[r];
- const PLT_ProtocolInfo& info = res.m_ProtocolInfo;
+ const PLT_MediaItemResource& res = entry->m_Resources[r];
+ const PLT_ProtocolInfo& info = res.m_ProtocolInfo;
for (std::string_view type : SupportedSubFormats)
{
@@ -1222,25 +1391,30 @@ bool GetResource(const PLT_MediaObject* entry, CFileItem& item)
std::shared_ptr<CFileItem> GetFileItem(const NPT_String& uri, const NPT_String& meta)
{
- PLT_MediaObjectListReference list;
- PLT_MediaObject* object = NULL;
- CFileItemPtr item;
+ PLT_MediaObjectListReference list;
+ PLT_MediaObject* object = NULL;
+ CFileItemPtr item;
- if (NPT_SUCCEEDED(PLT_Didl::FromDidl(meta, list))) {
- list->Get(0, object);
- }
+ if (NPT_SUCCEEDED(PLT_Didl::FromDidl(meta, list)))
+ {
+ list->Get(0, object);
+ }
- if (object) {
- item = BuildObject(object);
- }
+ if (object)
+ {
+ item = BuildObject(object);
+ }
- if (item) {
- item->SetPath((const char*)uri);
- GetResource(object, *item);
- } else {
- item.reset(new CFileItem((const char*)uri, false));
- }
- return item;
+ if (item)
+ {
+ item->SetPath((const char*)uri);
+ GetResource(object, *item);
+ }
+ else
+ {
+ item = std::make_shared<CFileItem>((const char*)uri, false);
+ }
+ return item;
}
NPT_String EncodeObjectId(const std::string& id)
@@ -1292,11 +1466,11 @@ NPT_String DecodeObjectId(const std::string& id)
const std::string decodedObjectId = Base64::Decode(id);
if (decodedObjectId.empty())
{
- CLog::LogF(LOGERROR, "Failed to decode object id {}, not properly Base64 encoded", decodedObjectId);
+ CLog::LogF(LOGERROR, "Failed to decode object id {}, not properly Base64 encoded",
+ decodedObjectId);
}
return decodedObjectId.c_str();
}
} /* namespace UPNP */
-
diff --git a/xbmc/network/upnp/UPnPPlayer.cpp b/xbmc/network/upnp/UPnPPlayer.cpp
index 3a4fe6b422..02520e80ce 100644
--- a/xbmc/network/upnp/UPnPPlayer.cpp
+++ b/xbmc/network/upnp/UPnPPlayer.cpp
@@ -160,7 +160,6 @@ public:
CUPnPPlayer::CUPnPPlayer(IPlayerCallback& callback, const char* uuid)
: IPlayer(callback),
m_control(NULL),
- m_delegate(NULL),
m_logger(CServiceBroker::GetLogging().GetLogger(StringUtils::Format("CUPnPPlayer[{}]", uuid)))
{
m_control = CUPnP::GetInstance()->m_MediaController;
@@ -168,8 +167,8 @@ CUPnPPlayer::CUPnPPlayer(IPlayerCallback& callback, const char* uuid)
PLT_DeviceDataReference device;
if(NPT_SUCCEEDED(m_control->FindRenderer(uuid, device)))
{
- m_delegate = new CUPnPPlayerController(m_control, device, callback);
- CUPnP::RegisterUserdata(m_delegate);
+ m_delegate = std::make_unique<CUPnPPlayerController>(m_control, device, callback);
+ CUPnP::RegisterUserdata(m_delegate.get());
}
else
m_logger->error("couldn't find device as {}", uuid);
@@ -181,8 +180,7 @@ CUPnPPlayer::~CUPnPPlayer()
{
CServiceBroker::GetWinSystem()->UnregisterRenderLoop(this);
CloseFile();
- CUPnP::UnregisterUserdata(m_delegate);
- delete m_delegate;
+ CUPnP::UnregisterUserdata(m_delegate.get());
}
static NPT_Result WaitOnEvent(CEvent& event, XbmcThreads::EndTime<>& timeout)
@@ -242,37 +240,35 @@ int CUPnPPlayer::PlayFile(const CFileItem& file,
// get the transport info to evaluate the TransportState to be able to
// determine whether we first need to call Stop()
timeout.Set(timeout.GetInitialTimeoutValue());
- NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed_gettransportinfo);
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->GetTransportInfo(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed_gettransportinfo);
NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout), failed_gettransportinfo);
if (m_delegate->m_trainfo.cur_transport_state != "NO_MEDIA_PRESENT" &&
m_delegate->m_trainfo.cur_transport_state != "STOPPED")
{
timeout.Set(timeout.GetInitialTimeoutValue());
- NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed_stop);
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->Stop(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed_stop);
NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout), failed_stop);
NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_stop);
}
timeout.Set(timeout.GetInitialTimeoutValue());
- NPT_CHECK_LABEL_SEVERE(m_control->SetAVTransportURI(m_delegate->m_device
- , m_delegate->m_instance
- , obj->m_Resources[res_index].m_Uri
- , (const char*)tmp
- , m_delegate), failed_setavtransporturi);
+ NPT_CHECK_LABEL_SEVERE(m_control->SetAVTransportURI(m_delegate->m_device, m_delegate->m_instance,
+ obj->m_Resources[res_index].m_Uri,
+ (const char*)tmp, m_delegate.get()),
+ failed_setavtransporturi);
NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout), failed_setavtransporturi);
NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_setavtransporturi);
timeout.Set(timeout.GetInitialTimeoutValue());
- NPT_CHECK_LABEL_SEVERE(m_control->Play(m_delegate->m_device
- , m_delegate->m_instance
- , "1"
- , m_delegate), failed_play);
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->Play(m_delegate->m_device, m_delegate->m_instance, "1", m_delegate.get()),
+ failed_play);
NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout), failed_play);
NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_play);
@@ -280,10 +276,9 @@ int CUPnPPlayer::PlayFile(const CFileItem& file,
/* wait for PLAYING state */
timeout.Set(timeout.GetInitialTimeoutValue());
do {
- NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed_waitplaying);
-
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->GetTransportInfo(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed_waitplaying);
{
std::unique_lock<CCriticalSection> lock(m_delegate->m_section);
@@ -306,11 +301,10 @@ int CUPnPPlayer::PlayFile(const CFileItem& file,
if(options.starttime > 0)
{
/* many upnp units won't load file properly until after play (including xbmc) */
- NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device
- , m_delegate->m_instance
- , "REL_TIME"
- , PLT_Didl::FormatTimeStamp((NPT_UInt32)options.starttime)
- , m_delegate), failed_seek);
+ NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device, m_delegate->m_instance, "REL_TIME",
+ PLT_Didl::FormatTimeStamp((NPT_UInt32)options.starttime),
+ m_delegate.get()),
+ failed_seek);
}
return NPT_SUCCESS;
@@ -350,9 +344,9 @@ bool CUPnPPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options)
/* if no path we want to attach to a already playing player */
if(file.GetPath() == "")
{
- NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed);
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->GetTransportInfo(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed);
NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout), failed);
@@ -371,12 +365,12 @@ bool CUPnPPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options)
m_started = true;
m_callback.OnPlayBackStarted(file);
m_callback.OnAVStarted(file);
- NPT_CHECK_LABEL_SEVERE(m_control->GetPositionInfo(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed);
- NPT_CHECK_LABEL_SEVERE(m_control->GetMediaInfo(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed);
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->GetPositionInfo(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed);
+ NPT_CHECK_LABEL_SEVERE(
+ m_control->GetMediaInfo(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed);
m_updateTimer.Set(0ms);
@@ -408,11 +402,10 @@ bool CUPnPPlayer::QueueNextFile(const CFileItem& file)
tmp.Append(didl_footer);
}
- NPT_CHECK_LABEL_WARNING(m_control->SetNextAVTransportURI(m_delegate->m_device
- , m_delegate->m_instance
- , file.GetPath().c_str()
- , (const char*)tmp
- , m_delegate), failed);
+ NPT_CHECK_LABEL_WARNING(
+ m_control->SetNextAVTransportURI(m_delegate->m_device, m_delegate->m_instance,
+ file.GetPath().c_str(), (const char*)tmp, m_delegate.get()),
+ failed);
if (!m_delegate->m_resevent.Wait(10000ms))
goto failed;
NPT_CHECK_LABEL_WARNING(m_delegate->m_resstatus, failed);
@@ -428,9 +421,8 @@ bool CUPnPPlayer::CloseFile(bool reopen)
NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
if(m_stopremote)
{
- NPT_CHECK_LABEL(m_control->Stop(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed);
+ NPT_CHECK_LABEL(m_control->Stop(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()),
+ failed);
if (!m_delegate->m_resevent.Wait(10000ms))
goto failed;
NPT_CHECK_LABEL(m_delegate->m_resstatus, failed);
@@ -452,17 +444,15 @@ void CUPnPPlayer::Pause()
{
if(IsPaused())
{
- NPT_CHECK_LABEL(m_control->Play(m_delegate->m_device
- , m_delegate->m_instance
- , "1"
- , m_delegate), failed);
+ NPT_CHECK_LABEL(
+ m_control->Play(m_delegate->m_device, m_delegate->m_instance, "1", m_delegate.get()),
+ failed);
CDataCacheCore::GetInstance().SetSpeed(1.0, 1.0);
}
else
{
- NPT_CHECK_LABEL(m_control->Pause(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed);
+ NPT_CHECK_LABEL(
+ m_control->Pause(m_delegate->m_device, m_delegate->m_instance, m_delegate.get()), failed);
CDataCacheCore::GetInstance().SetSpeed(1.0, 0.0);
}
@@ -473,10 +463,10 @@ failed:
void CUPnPPlayer::SeekTime(int64_t ms)
{
- NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device
- , m_delegate->m_instance
- , "REL_TIME", PLT_Didl::FormatTimeStamp((NPT_UInt32)(ms / 1000))
- , m_delegate), failed);
+ NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device, m_delegate->m_instance, "REL_TIME",
+ PLT_Didl::FormatTimeStamp((NPT_UInt32)(ms / 1000)),
+ m_delegate.get()),
+ failed);
CDataCacheCore::GetInstance().SeekFinished(0);
return;
@@ -560,10 +550,9 @@ failed:
void CUPnPPlayer::SetVolume(float volume)
{
NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);
- NPT_CHECK_LABEL(m_control->SetVolume(m_delegate->m_device
- , m_delegate->m_instance
- , "Master", (int)(volume * 100)
- , m_delegate), failed);
+ NPT_CHECK_LABEL(m_control->SetVolume(m_delegate->m_device, m_delegate->m_instance, "Master",
+ (int)(volume * 100), m_delegate.get()),
+ failed);
return;
failed:
m_logger->error("- unable to set volume");
diff --git a/xbmc/network/upnp/UPnPPlayer.h b/xbmc/network/upnp/UPnPPlayer.h
index 77e2a24cf8..c449b62e7c 100644
--- a/xbmc/network/upnp/UPnPPlayer.h
+++ b/xbmc/network/upnp/UPnPPlayer.h
@@ -14,6 +14,7 @@
#include "threads/SystemClock.h"
#include "utils/logtypes.h"
+#include <memory>
#include <string>
class PLT_MediaController;
@@ -64,7 +65,7 @@ private:
float GetPercentage();
PLT_MediaController* m_control;
- CUPnPPlayerController* m_delegate;
+ std::unique_ptr<CUPnPPlayerController> m_delegate;
std::string m_current_uri;
std::string m_current_meta;
bool m_started = false;
diff --git a/xbmc/network/upnp/UPnPServer.cpp b/xbmc/network/upnp/UPnPServer.cpp
index 7085c06599..03d2e90fd9 100644
--- a/xbmc/network/upnp/UPnPServer.cpp
+++ b/xbmc/network/upnp/UPnPServer.cpp
@@ -43,6 +43,8 @@
#include "view/GUIViewState.h"
#include "xbmc/interfaces/AnnouncementManager.h"
+#include <memory>
+
#include <Platinum/Source/Platinum/Platinum.h>
NPT_SET_LOCAL_LOGGER("xbmc.upnp.server")
@@ -408,8 +410,8 @@ PLT_MediaObject* CUPnPServer::Build(const std::shared_ptr<CFileItem>& item,
}
}
}
- // playlists are folders
- else if (item->IsPlayList())
+ // all playlist types are folders
+ else if (item->IsPlayList() || item->IsSmartPlayList())
{
item->m_bIsFolder = true;
}
@@ -601,7 +603,7 @@ CUPnPServer::OnBrowseMetadata(PLT_ActionReference& action,
id.TrimRight("/");
if (id == "virtualpath://upnproot") {
id += "/";
- item.reset(new CFileItem((const char*)id, true));
+ item = std::make_shared<CFileItem>((const char*)id, true);
item->SetLabel("Root");
item->SetLabelPreformatted(true);
object = Build(item, true, context, thumb_loader);
@@ -610,7 +612,7 @@ CUPnPServer::OnBrowseMetadata(PLT_ActionReference& action,
return NPT_FAILURE;
}
} else {
- item.reset(new CFileItem((const char*)id, false));
+ item = std::make_shared<CFileItem>((const char*)id, false);
// attempt to determine the parent of this item
std::string parent;
@@ -713,13 +715,13 @@ CUPnPServer::OnBrowseDirectChildren(PLT_ActionReference& action,
CFileItemPtr item;
// music library
- item.reset(new CFileItem("musicdb://", true));
+ item = std::make_shared<CFileItem>("musicdb://", true);
item->SetLabel("Music Library");
item->SetLabelPreformatted(true);
items.Add(item);
// video library
- item.reset(new CFileItem("library://video/", true));
+ item = std::make_shared<CFileItem>("library://video/", true);
item->SetLabel("Video Library");
item->SetLabelPreformatted(true);
items.Add(item);
diff --git a/xbmc/network/websocket/WebSocket.cpp b/xbmc/network/websocket/WebSocket.cpp
index ecec4efeee..5051c27f22 100644
--- a/xbmc/network/websocket/WebSocket.cpp
+++ b/xbmc/network/websocket/WebSocket.cpp
@@ -325,7 +325,7 @@ const CWebSocketMessage* CWebSocket::Handle(const char* &buffer, size_t &length,
case WebSocketPing:
msg = GetMessage();
if (msg != NULL)
- msg->AddFrame(Pong(frame->GetApplicationData()));
+ msg->AddFrame(Pong(frame->GetApplicationData(), frame->GetLength()));
break;
case WebSocketConnectionClose:
diff --git a/xbmc/network/websocket/WebSocket.h b/xbmc/network/websocket/WebSocket.h
index 8901edeca5..9b7ca7ff38 100644
--- a/xbmc/network/websocket/WebSocket.h
+++ b/xbmc/network/websocket/WebSocket.h
@@ -121,7 +121,7 @@ public:
virtual const CWebSocketMessage* Handle(const char* &buffer, size_t &length, bool &send);
virtual const CWebSocketMessage* Send(WebSocketFrameOpcode opcode, const char* data = NULL, uint32_t length = 0);
virtual const CWebSocketFrame* Ping(const char* data = NULL) const = 0;
- virtual const CWebSocketFrame* Pong(const char* data = NULL) const = 0;
+ virtual const CWebSocketFrame* Pong(const char* data, uint32_t length) const = 0;
virtual const CWebSocketFrame* Close(WebSocketCloseReason reason = WebSocketCloseNormal, const std::string &message = "") = 0;
virtual void Fail() = 0;
diff --git a/xbmc/network/websocket/WebSocketV8.h b/xbmc/network/websocket/WebSocketV8.h
index d86688cdcf..0214975279 100644
--- a/xbmc/network/websocket/WebSocketV8.h
+++ b/xbmc/network/websocket/WebSocketV8.h
@@ -19,7 +19,10 @@ public:
bool Handshake(const char* data, size_t length, std::string &response) override;
const CWebSocketFrame* Ping(const char* data = NULL) const override { return new CWebSocketFrame(WebSocketPing, data); }
- const CWebSocketFrame* Pong(const char* data = NULL) const override { return new CWebSocketFrame(WebSocketPong, data); }
+ const CWebSocketFrame* Pong(const char* data, uint32_t length) const override
+ {
+ return new CWebSocketFrame(WebSocketPong, data, length);
+ }
const CWebSocketFrame* Close(WebSocketCloseReason reason = WebSocketCloseNormal, const std::string &message = "") override;
void Fail() override;
diff --git a/xbmc/peripherals/addons/AddonButtonMapping.cpp b/xbmc/peripherals/addons/AddonButtonMapping.cpp
index af838aaf7f..68e324065b 100644
--- a/xbmc/peripherals/addons/AddonButtonMapping.cpp
+++ b/xbmc/peripherals/addons/AddonButtonMapping.cpp
@@ -14,6 +14,8 @@
#include "peripherals/addons/AddonButtonMap.h"
#include "utils/log.h"
+#include <memory>
+
using namespace KODI;
using namespace JOYSTICK;
using namespace PERIPHERALS;
@@ -31,11 +33,11 @@ CAddonButtonMapping::CAddonButtonMapping(CPeripherals& manager,
else
{
const std::string controllerId = mapper->ControllerID();
- m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, controllerId));
+ m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, controllerId);
if (m_buttonMap->Load())
{
IKeymap* keymap = peripheral->GetKeymap(controllerId);
- m_buttonMapping.reset(new CButtonMapping(mapper, m_buttonMap.get(), keymap));
+ m_buttonMapping = std::make_unique<CButtonMapping>(mapper, m_buttonMap.get(), keymap);
// Allow the mapper to save our button map
mapper->SetButtonMapCallback(peripheral->Location(), this);
diff --git a/xbmc/peripherals/addons/AddonInputHandling.cpp b/xbmc/peripherals/addons/AddonInputHandling.cpp
index 7ae7208a64..270c85de1c 100644
--- a/xbmc/peripherals/addons/AddonInputHandling.cpp
+++ b/xbmc/peripherals/addons/AddonInputHandling.cpp
@@ -20,6 +20,8 @@
#include "peripherals/addons/AddonButtonMap.h"
#include "utils/log.h"
+#include <memory>
+
using namespace KODI;
using namespace JOYSTICK;
using namespace PERIPHERALS;
@@ -37,14 +39,14 @@ CAddonInputHandling::CAddonInputHandling(CPeripherals& manager,
}
else if (!handler->ControllerID().empty())
{
- m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, handler->ControllerID()));
+ m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, handler->ControllerID());
if (m_buttonMap->Load())
{
- m_driverHandler.reset(new CInputHandling(handler, m_buttonMap.get()));
+ m_driverHandler = std::make_unique<CInputHandling>(handler, m_buttonMap.get());
if (receiver)
{
- m_inputReceiver.reset(new CDriverReceiving(receiver, m_buttonMap.get()));
+ m_inputReceiver = std::make_unique<CDriverReceiving>(receiver, m_buttonMap.get());
// Interfaces are connected here because they share button map as a common resource
handler->SetInputReceiver(m_inputReceiver.get());
@@ -69,10 +71,11 @@ CAddonInputHandling::CAddonInputHandling(CPeripherals& manager,
}
else if (!handler->ControllerID().empty())
{
- m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, handler->ControllerID()));
+ m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, handler->ControllerID());
if (m_buttonMap->Load())
{
- m_keyboardHandler.reset(new KEYBOARD::CKeyboardInputHandling(handler, m_buttonMap.get()));
+ m_keyboardHandler =
+ std::make_unique<KEYBOARD::CKeyboardInputHandling>(handler, m_buttonMap.get());
}
else
{
@@ -93,10 +96,10 @@ CAddonInputHandling::CAddonInputHandling(CPeripherals& manager,
}
else if (!handler->ControllerID().empty())
{
- m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, handler->ControllerID()));
+ m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, handler->ControllerID());
if (m_buttonMap->Load())
{
- m_mouseHandler.reset(new MOUSE::CMouseInputHandling(handler, m_buttonMap.get()));
+ m_mouseHandler = std::make_unique<MOUSE::CMouseInputHandling>(handler, m_buttonMap.get());
}
else
{
diff --git a/xbmc/peripherals/devices/PeripheralJoystick.cpp b/xbmc/peripherals/devices/PeripheralJoystick.cpp
index 4033e94f5f..0d1e1eaeda 100644
--- a/xbmc/peripherals/devices/PeripheralJoystick.cpp
+++ b/xbmc/peripherals/devices/PeripheralJoystick.cpp
@@ -29,6 +29,7 @@
#include "utils/log.h"
#include <algorithm>
+#include <memory>
#include <mutex>
using namespace KODI;
@@ -105,9 +106,9 @@ bool CPeripheralJoystick::InitialiseFeature(const PeripheralFeature feature)
}
// Give joystick monitor priority over default controller
- m_appInput.reset(
- new CKeymapHandling(this, false, m_manager.GetInputManager().KeymapEnvironment()));
- m_joystickMonitor.reset(new CJoystickMonitor);
+ m_appInput = std::make_unique<CKeymapHandling>(
+ this, false, m_manager.GetInputManager().KeymapEnvironment());
+ m_joystickMonitor = std::make_unique<CJoystickMonitor>();
RegisterInputHandler(m_joystickMonitor.get(), false);
}
}
@@ -126,7 +127,7 @@ bool CPeripheralJoystick::InitialiseFeature(const PeripheralFeature feature)
void CPeripheralJoystick::InitializeDeadzoneFiltering(IButtonMap& buttonMap)
{
- m_deadzoneFilter.reset(new CDeadzoneFilter(&buttonMap, this));
+ m_deadzoneFilter = std::make_unique<CDeadzoneFilter>(&buttonMap, this);
}
void CPeripheralJoystick::InitializeControllerProfile(IButtonMap& buttonMap)
diff --git a/xbmc/pictures/GUIWindowSlideShow.cpp b/xbmc/pictures/GUIWindowSlideShow.cpp
index e2f9db7bba..739fd892a3 100644
--- a/xbmc/pictures/GUIWindowSlideShow.cpp
+++ b/xbmc/pictures/GUIWindowSlideShow.cpp
@@ -40,6 +40,7 @@
#include "utils/XTimeUtils.h"
#include "utils/log.h"
+#include <memory>
#include <random>
using namespace XFILE;
@@ -345,7 +346,7 @@ void CGUIWindowSlideShow::Select(const std::string& strPicture)
void CGUIWindowSlideShow::GetSlideShowContents(CFileItemList &list)
{
for (size_t index = 0; index < m_slides.size(); index++)
- list.Add(CFileItemPtr(new CFileItem(*m_slides.at(index))));
+ list.Add(std::make_shared<CFileItem>(*m_slides.at(index)));
}
std::shared_ptr<const CFileItem> CGUIWindowSlideShow::GetCurrentSlide()
@@ -394,7 +395,9 @@ void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList &re
// if we haven't processed yet, we should mark the whole screen
if (!HasProcessed())
- regions.push_back(CDirtyRegion(CRect(0.0f, 0.0f, (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
+ regions.emplace_back(CRect(
+ 0.0f, 0.0f, static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
+ static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
if (m_iCurrentSlide < 0 || m_iCurrentSlide >= static_cast<int>(m_slides.size()))
m_iCurrentSlide = 0;
@@ -404,7 +407,7 @@ void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList &re
// Create our background loader if necessary
if (!m_pBackgroundLoader)
{
- m_pBackgroundLoader.reset(new CBackgroundPicLoader());
+ m_pBackgroundLoader = std::make_unique<CBackgroundPicLoader>();
m_pBackgroundLoader->Create(this);
}
@@ -477,7 +480,9 @@ void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList &re
if (m_bErrorMessage)
{ // hack, just mark it all
- regions.push_back(CDirtyRegion(CRect(0.0f, 0.0f, (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
+ regions.emplace_back(CRect(
+ 0.0f, 0.0f, static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
+ static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
return;
}
diff --git a/xbmc/pictures/Picture.cpp b/xbmc/pictures/Picture.cpp
index ceeb515a9d..a240f07b5a 100644
--- a/xbmc/pictures/Picture.cpp
+++ b/xbmc/pictures/Picture.cpp
@@ -169,13 +169,6 @@ bool CPicture::ResizeTexture(const std::string &image, uint8_t *pixels, uint32_t
uint32_t stride = dest_width_aligned * sizeof(uint32_t);
uint32_t* buffer = new uint32_t[dest_width_aligned * dest_height + 4];
- if (buffer == NULL)
- {
- result = NULL;
- result_size = 0;
- return false;
- }
-
if (!ScaleImage(pixels, width, height, pitch, AV_PIX_FMT_BGRA, (uint8_t*)buffer, dest_width,
dest_height, stride, AV_PIX_FMT_BGRA, scalingAlgorithm))
{
@@ -253,20 +246,17 @@ bool CPicture::CacheTexture(uint8_t *pixels, uint32_t width, uint32_t height, ui
uint32_t dest_width_aligned = ((dest_width + 15) & ~0x0f);
uint32_t stride = dest_width_aligned * sizeof(uint32_t);
- uint32_t* buffer = new uint32_t[dest_width_aligned * dest_height + 4];
- if (buffer)
+ auto buffer = std::make_unique<uint32_t[]>(dest_width_aligned * dest_height + 4);
+ if (ScaleImage(pixels, width, height, pitch, AV_PIX_FMT_BGRA,
+ reinterpret_cast<uint8_t*>(buffer.get()), dest_width, dest_height, stride,
+ AV_PIX_FMT_BGRA, scalingAlgorithm))
{
- if (ScaleImage(pixels, width, height, pitch, AV_PIX_FMT_BGRA, (uint8_t*)buffer, dest_width,
- dest_height, stride, AV_PIX_FMT_BGRA, scalingAlgorithm))
+ if (!orientation ||
+ OrientateImage(buffer, dest_width, dest_height, orientation, dest_width_aligned))
{
- if (!orientation ||
- OrientateImage(buffer, dest_width, dest_height, orientation, dest_width_aligned))
- {
- success = CreateThumbnailFromSurface((unsigned char*)buffer, dest_width, dest_height,
- dest_width_aligned * 4, dest);
- }
+ success = CreateThumbnailFromSurface(reinterpret_cast<unsigned char*>(buffer.get()),
+ dest_width, dest_height, dest_width_aligned * 4, dest);
}
- delete[] buffer;
}
return success;
}
@@ -297,8 +287,6 @@ std::unique_ptr<CTexture> CPicture::CreateTiledThumb(const std::vector<std::stri
// create a buffer for the resulting thumb
std::unique_ptr<uint32_t[]> buffer = std::make_unique<uint32_t[]>(imageRes * imageRes);
- if (!buffer)
- return {};
for (unsigned int i = 0; i < files.size(); ++i)
{
int x = i % num_across;
@@ -317,9 +305,8 @@ std::unique_ptr<CTexture> CPicture::CreateTiledThumb(const std::vector<std::stri
width, height, width * 4, AV_PIX_FMT_BGRA))
{
unsigned int stridePixels{width};
- uint32_t* scaledL = scaled.get();
if (!texture->GetOrientation() ||
- OrientateImage(scaledL, width, height, texture->GetOrientation(), stridePixels))
+ OrientateImage(scaled, width, height, texture->GetOrientation(), stridePixels))
{
success = true;
// drop into the texture
@@ -346,71 +333,6 @@ std::unique_ptr<CTexture> CPicture::CreateTiledThumb(const std::vector<std::stri
return result;
}
-bool CPicture::CreateTiledThumb(const std::vector<std::string> &files, const std::string &thumb)
-{
- if (!files.size())
- return false;
-
- unsigned int num_across = (unsigned int)ceil(sqrt((float)files.size()));
- unsigned int num_down = (files.size() + num_across - 1) / num_across;
-
- unsigned int imageRes = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes;
-
- unsigned int tile_width = imageRes / num_across;
- unsigned int tile_height = imageRes / num_down;
- unsigned int tile_gap = 1;
- bool success = false;
-
- // create a buffer for the resulting thumb
- uint32_t *buffer = static_cast<uint32_t *>(calloc(imageRes * imageRes, 4));
- if (!buffer)
- return false;
- for (unsigned int i = 0; i < files.size(); ++i)
- {
- int x = i % num_across;
- int y = i / num_across;
- // load in the image
- unsigned int width = tile_width - 2*tile_gap, height = tile_height - 2*tile_gap;
- std::unique_ptr<CTexture> texture = CTexture::LoadFromFile(files[i], width, height, true);
- if (texture && texture->GetWidth() && texture->GetHeight())
- {
- GetScale(texture->GetWidth(), texture->GetHeight(), width, height);
-
- // scale appropriately
- uint32_t *scaled = new uint32_t[width * height];
- if (ScaleImage(texture->GetPixels(), texture->GetWidth(), texture->GetHeight(),
- texture->GetPitch(), AV_PIX_FMT_BGRA, (uint8_t*)scaled, width, height,
- width * 4, AV_PIX_FMT_BGRA))
- {
- unsigned int stridePixels{width};
- if (!texture->GetOrientation() ||
- OrientateImage(scaled, width, height, texture->GetOrientation(), stridePixels))
- {
- success = true; // Flag that we at least had one successful image processed
- // drop into the texture
- unsigned int posX = x*tile_width + (tile_width - width)/2;
- unsigned int posY = y*tile_height + (tile_height - height)/2;
- uint32_t *dest = buffer + posX + posY * imageRes;
- uint32_t *src = scaled;
- for (unsigned int y = 0; y < height; ++y)
- {
- memcpy(dest, src, width*4);
- dest += imageRes;
- src += stridePixels;
- }
- }
- }
- delete[] scaled;
- }
- }
- // now save to a file
- if (success)
- success = CreateThumbnailFromSurface((uint8_t *)buffer, imageRes, imageRes, imageRes * 4, thumb);
-
- free(buffer);
- return success;
-}
-
void CPicture::GetScale(unsigned int width, unsigned int height, unsigned int &out_width, unsigned int &out_height)
{
float aspect = (float)width / height;
@@ -451,7 +373,7 @@ bool CPicture::ScaleImage(uint8_t* in_pixels,
return false;
}
-bool CPicture::OrientateImage(uint32_t*& pixels,
+bool CPicture::OrientateImage(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
int orientation,
@@ -489,7 +411,7 @@ bool CPicture::OrientateImage(uint32_t*& pixels,
return out;
}
-bool CPicture::FlipHorizontal(uint32_t*& pixels,
+bool CPicture::FlipHorizontal(std::unique_ptr<uint32_t[]>& pixels,
const unsigned int& width,
const unsigned int& height,
const unsigned int& stridePixels)
@@ -497,14 +419,14 @@ bool CPicture::FlipHorizontal(uint32_t*& pixels,
// this can be done in-place easily enough
for (unsigned int y = 0; y < height; ++y)
{
- uint32_t* line = pixels + y * stridePixels;
+ uint32_t* line = pixels.get() + y * stridePixels;
for (unsigned int x = 0; x < width / 2; ++x)
std::swap(line[x], line[width - 1 - x]);
}
return true;
}
-bool CPicture::FlipVertical(uint32_t*& pixels,
+bool CPicture::FlipVertical(std::unique_ptr<uint32_t[]>& pixels,
const unsigned int& width,
const unsigned int& height,
const unsigned int& stridePixels)
@@ -512,15 +434,15 @@ bool CPicture::FlipVertical(uint32_t*& pixels,
// this can be done in-place easily enough
for (unsigned int y = 0; y < height / 2; ++y)
{
- uint32_t* line1 = pixels + y * stridePixels;
- uint32_t* line2 = pixels + (height - 1 - y) * stridePixels;
+ uint32_t* line1 = pixels.get() + y * stridePixels;
+ uint32_t* line2 = pixels.get() + (height - 1 - y) * stridePixels;
for (unsigned int x = 0; x < width; ++x)
std::swap(*line1++, *line2++);
}
return true;
}
-bool CPicture::Rotate180CCW(uint32_t*& pixels,
+bool CPicture::Rotate180CCW(std::unique_ptr<uint32_t[]>& pixels,
const unsigned int& width,
const unsigned int& height,
const unsigned int& stridePixels)
@@ -528,63 +450,55 @@ bool CPicture::Rotate180CCW(uint32_t*& pixels,
// this can be done in-place easily enough
for (unsigned int y = 0; y < height / 2; ++y)
{
- uint32_t* line1 = pixels + y * stridePixels;
- uint32_t* line2 = pixels + (height - 1 - y) * stridePixels + width - 1;
+ uint32_t* line1 = pixels.get() + y * stridePixels;
+ uint32_t* line2 = pixels.get() + (height - 1 - y) * stridePixels + width - 1;
for (unsigned int x = 0; x < width; ++x)
std::swap(*line1++, *line2--);
}
if (height % 2)
{ // height is odd, so flip the middle row as well
- uint32_t* line = pixels + (height - 1) / 2 * stridePixels;
+ uint32_t* line = pixels.get() + (height - 1) / 2 * stridePixels;
for (unsigned int x = 0; x < width / 2; ++x)
std::swap(line[x], line[width - 1 - x]);
}
return true;
}
-bool CPicture::Rotate90CCW(uint32_t*& pixels,
+bool CPicture::Rotate90CCW(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels)
{
- uint32_t *dest = new uint32_t[width * height * 4];
- if (dest)
+ auto dest = std::make_unique<uint32_t[]>(width * height * 4);
+ unsigned int d_height = width, d_width = height;
+ for (unsigned int y = 0; y < d_height; y++)
{
- unsigned int d_height = width, d_width = height;
- for (unsigned int y = 0; y < d_height; y++)
+ const uint32_t* src = pixels.get() + (d_height - 1 - y); // y-th col from right, starting at top
+ uint32_t* dst = dest.get() + d_width * y; // y-th row from top, starting at left
+ for (unsigned int x = 0; x < d_width; x++)
{
- const uint32_t *src = pixels + (d_height - 1 - y); // y-th col from right, starting at top
- uint32_t *dst = dest + d_width * y; // y-th row from top, starting at left
- for (unsigned int x = 0; x < d_width; x++)
- {
- *dst++ = *src;
- src += stridePixels;
- }
+ *dst++ = *src;
+ src += stridePixels;
}
- delete[] pixels;
- pixels = dest;
- std::swap(width, height);
- stridePixels = width;
- return true;
}
- return false;
+ pixels = std::move(dest);
+ std::swap(width, height);
+ stridePixels = width;
+ return true;
}
-bool CPicture::Rotate270CCW(uint32_t*& pixels,
+bool CPicture::Rotate270CCW(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels)
{
- uint32_t *dest = new uint32_t[width * height * 4];
- if (!dest)
- return false;
-
+ auto dest = std::make_unique<uint32_t[]>(width * height * 4);
unsigned int d_height = width, d_width = height;
for (unsigned int y = 0; y < d_height; y++)
{
const uint32_t* src =
- pixels + stridePixels * (d_width - 1) + y; // y-th col from left, starting at bottom
- uint32_t* dst = dest + d_width * y; // y-th row from top, starting at left
+ pixels.get() + stridePixels * (d_width - 1) + y; // y-th col from left, starting at bottom
+ uint32_t* dst = dest.get() + d_width * y; // y-th row from top, starting at left
for (unsigned int x = 0; x < d_width; x++)
{
*dst++ = *src;
@@ -592,27 +506,23 @@ bool CPicture::Rotate270CCW(uint32_t*& pixels,
}
}
- delete[] pixels;
- pixels = dest;
+ pixels = std::move(dest);
std::swap(width, height);
stridePixels = width;
return true;
}
-bool CPicture::Transpose(uint32_t*& pixels,
+bool CPicture::Transpose(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels)
{
- uint32_t *dest = new uint32_t[width * height * 4];
- if (!dest)
- return false;
-
+ auto dest = std::make_unique<uint32_t[]>(width * height * 4);
unsigned int d_height = width, d_width = height;
for (unsigned int y = 0; y < d_height; y++)
{
- const uint32_t *src = pixels + y; // y-th col from left, starting at top
- uint32_t *dst = dest + d_width * y; // y-th row from top, starting at left
+ const uint32_t* src = pixels.get() + y; // y-th col from left, starting at top
+ uint32_t* dst = dest.get() + d_width * y; // y-th row from top, starting at left
for (unsigned int x = 0; x < d_width; x++)
{
*dst++ = *src;
@@ -620,28 +530,24 @@ bool CPicture::Transpose(uint32_t*& pixels,
}
}
- delete[] pixels;
- pixels = dest;
+ pixels = std::move(dest);
std::swap(width, height);
stridePixels = width;
return true;
}
-bool CPicture::TransposeOffAxis(uint32_t*& pixels,
+bool CPicture::TransposeOffAxis(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels)
{
- uint32_t *dest = new uint32_t[width * height * 4];
- if (!dest)
- return false;
-
+ auto dest = std::make_unique<uint32_t[]>(width * height * 4);
unsigned int d_height = width, d_width = height;
for (unsigned int y = 0; y < d_height; y++)
{
- const uint32_t* src = pixels + stridePixels * (d_width - 1) +
+ const uint32_t* src = pixels.get() + stridePixels * (d_width - 1) +
(d_height - 1 - y); // y-th col from right, starting at bottom
- uint32_t *dst = dest + d_width * y; // y-th row, starting at left
+ uint32_t* dst = dest.get() + d_width * y; // y-th row, starting at left
for (unsigned int x = 0; x < d_width; x++)
{
*dst++ = *src;
@@ -649,8 +555,7 @@ bool CPicture::TransposeOffAxis(uint32_t*& pixels,
}
}
- delete[] pixels;
- pixels = dest;
+ pixels = std::move(dest);
std::swap(width, height);
stridePixels = width;
return true;
diff --git a/xbmc/pictures/Picture.h b/xbmc/pictures/Picture.h
index e3d02011b5..390ff169f5 100644
--- a/xbmc/pictures/Picture.h
+++ b/xbmc/pictures/Picture.h
@@ -30,12 +30,6 @@ public:
static bool GetThumbnailFromSurface(const unsigned char* buffer, int width, int height, int stride, const std::string &thumbFile, uint8_t* &result, size_t& result_size);
static bool CreateThumbnailFromSurface(const unsigned char* buffer, int width, int height, int stride, const std::string &thumbFile);
- /*! \brief Create a tiled thumb of the given files
- \param files the files to create the thumb from
- \param thumb the filename of the thumb
- */
- static bool CreateTiledThumb(const std::vector<std::string> &files, const std::string &thumb);
-
static std::unique_ptr<CTexture> CreateTiledThumb(const std::vector<std::string>& files);
static bool ResizeTexture(
@@ -82,37 +76,37 @@ public:
CPictureScalingAlgorithm::Algorithm scalingAlgorithm = CPictureScalingAlgorithm::NoAlgorithm);
private:
- static bool OrientateImage(uint32_t*& pixels,
+ static bool OrientateImage(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
int orientation,
unsigned int& stridePixels);
- static bool FlipHorizontal(uint32_t*& pixels,
+ static bool FlipHorizontal(std::unique_ptr<uint32_t[]>& pixels,
const unsigned int& width,
const unsigned int& height,
const unsigned int& stridePixels);
- static bool FlipVertical(uint32_t*& pixels,
+ static bool FlipVertical(std::unique_ptr<uint32_t[]>& pixels,
const unsigned int& width,
const unsigned int& height,
const unsigned int& stridePixels);
- static bool Rotate90CCW(uint32_t*& pixels,
+ static bool Rotate90CCW(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels);
- static bool Rotate270CCW(uint32_t*& pixels,
+ static bool Rotate270CCW(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels);
- static bool Rotate180CCW(uint32_t*& pixels,
+ static bool Rotate180CCW(std::unique_ptr<uint32_t[]>& pixels,
const unsigned int& width,
const unsigned int& height,
const unsigned int& stridePixels);
- static bool Transpose(uint32_t*& pixels,
+ static bool Transpose(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& width_aligned);
- static bool TransposeOffAxis(uint32_t*& pixels,
+ static bool TransposeOffAxis(std::unique_ptr<uint32_t[]>& pixels,
unsigned int& width,
unsigned int& height,
unsigned int& stridePixels);
diff --git a/xbmc/pictures/SlideShowPicture.cpp b/xbmc/pictures/SlideShowPicture.cpp
index 8b99a27361..005d740654 100644
--- a/xbmc/pictures/SlideShowPicture.cpp
+++ b/xbmc/pictures/SlideShowPicture.cpp
@@ -263,8 +263,8 @@ void CSlideShowPic::UpdateVertices(float cur_x[4], float cur_y[4], const float n
|| memcmp(cur_y, new_y, count)
|| m_bIsDirty)
{
- dirtyregions.push_back(CDirtyRegion(GetRectangle(cur_x, cur_y)));
- dirtyregions.push_back(CDirtyRegion(GetRectangle(new_x, new_y)));
+ dirtyregions.emplace_back(GetRectangle(cur_x, cur_y));
+ dirtyregions.emplace_back(GetRectangle(new_x, new_y));
memcpy(cur_x, new_x, count);
memcpy(cur_y, new_y, count);
}
diff --git a/xbmc/platform/android/activity/JNIXBMCFile.cpp b/xbmc/platform/android/activity/JNIXBMCFile.cpp
index 0619490932..efff89a64e 100644
--- a/xbmc/platform/android/activity/JNIXBMCFile.cpp
+++ b/xbmc/platform/android/activity/JNIXBMCFile.cpp
@@ -12,6 +12,8 @@
#include "utils/FileUtils.h"
#include "utils/log.h"
+#include <memory>
+
#include <androidjni/jutils-details.hpp>
#define BUFFSIZE 8192
@@ -53,7 +55,7 @@ jboolean CJNIXBMCFile::_open(JNIEnv *env, jobject thiz, jstring path)
return false;
CJNIXBMCFile* file = new CJNIXBMCFile();
- file->m_file.reset(new XFILE::CFile());
+ file->m_file = std::make_unique<XFILE::CFile>();
bool ret = file->m_file->Open(strPath);
if (!ret)
{
diff --git a/xbmc/platform/android/activity/XBMCApp.cpp b/xbmc/platform/android/activity/XBMCApp.cpp
index e4fa080a9e..b04d63fb29 100644
--- a/xbmc/platform/android/activity/XBMCApp.cpp
+++ b/xbmc/platform/android/activity/XBMCApp.cpp
@@ -11,25 +11,58 @@
#include "AndroidKey.h"
#include "CompileInfo.h"
#include "FileItem.h"
+// Audio Engine includes for Factory and interfaces
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "TextureCache.h"
#include "application/AppEnvironment.h"
#include "application/AppParams.h"
#include "application/Application.h"
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
#include "application/ApplicationPowerHandling.h"
+#include "cores/AudioEngine/AESinkFactory.h"
+#include "cores/AudioEngine/Interfaces/AE.h"
+#include "cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
+#include "filesystem/SpecialProtocol.h"
+#include "filesystem/VideoDatabaseFile.h"
+#include "guilib/GUIComponent.h"
#include "guilib/GUIWindowManager.h"
+#include "guilib/guiinfo/GUIInfoLabels.h"
+#include "input/Key.h"
+#include "input/mouse/MouseStat.h"
#include "interfaces/AnnouncementManager.h"
#include "messaging/ApplicationMessenger.h"
+#include "platform/xbmc.h"
+#include "powermanagement/PowerManager.h"
#include "settings/AdvancedSettings.h"
#include "settings/DisplaySettings.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
+#include "threads/Event.h"
+#include "utils/StringUtils.h"
+#include "utils/TimeUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoInfoTag.h"
#include "windowing/GraphicContext.h"
+#include "windowing/WinEvents.h"
+#include "windowing/android/VideoSyncAndroid.h"
+#include "windowing/android/WinSystemAndroid.h"
+
+#include "platform/android/activity/IInputDeviceCallbacks.h"
+#include "platform/android/activity/IInputDeviceEventHandler.h"
+#include "platform/android/network/NetworkAndroid.h"
+#include "platform/android/powermanagement/AndroidPowerSyscall.h"
+#include <memory>
#include <mutex>
#include <sstream>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <android/bitmap.h>
#include <android/configuration.h>
@@ -70,37 +103,6 @@
#include <jni.h>
#include <rapidjson/document.h>
#include <unistd.h>
-// Audio Engine includes for Factory and interfaces
-#include "GUIInfoManager.h"
-#include "ServiceBroker.h"
-#include "TextureCache.h"
-#include "cores/AudioEngine/AESinkFactory.h"
-#include "cores/AudioEngine/Interfaces/AE.h"
-#include "cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h"
-#include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
-#include "filesystem/SpecialProtocol.h"
-#include "filesystem/VideoDatabaseFile.h"
-#include "guilib/GUIComponent.h"
-#include "guilib/GUIWindowManager.h"
-#include "guilib/guiinfo/GUIInfoLabels.h"
-#include "input/Key.h"
-#include "input/mouse/MouseStat.h"
-#include "platform/xbmc.h"
-#include "powermanagement/PowerManager.h"
-#include "utils/StringUtils.h"
-#include "utils/TimeUtils.h"
-#include "utils/URIUtils.h"
-#include "utils/Variant.h"
-#include "utils/log.h"
-#include "video/VideoInfoTag.h"
-#include "windowing/WinEvents.h"
-#include "windowing/android/VideoSyncAndroid.h"
-#include "windowing/android/WinSystemAndroid.h"
-
-#include "platform/android/activity/IInputDeviceCallbacks.h"
-#include "platform/android/activity/IInputDeviceEventHandler.h"
-#include "platform/android/network/NetworkAndroid.h"
-#include "platform/android/powermanagement/AndroidPowerSyscall.h"
#define GIGABYTES 1073741824
@@ -174,7 +176,7 @@ CXBMCApp::CXBMCApp(ANativeActivity* nativeActivity, IInputHandler& inputHandler)
exit(1);
return;
}
- m_mainView.reset(new CJNIXBMCMainView(this));
+ m_mainView = std::make_unique<CJNIXBMCMainView>(this);
m_hdmiSource = CJNISystemProperties::get("ro.hdmi.device_type", "") == "4";
android_printf("CXBMCApp: Created");
@@ -674,9 +676,6 @@ bool CXBMCApp::SetBuffersGeometry(int width, int height, int format)
return false;
}
-#include "threads/Event.h"
-#include <time.h>
-
void CXBMCApp::SetRefreshRateCallback(void* rateVariant)
{
CVariant* rateV = static_cast<CVariant*>(rateVariant);
diff --git a/xbmc/platform/darwin/ios-common/network/NetworkIOS.mm b/xbmc/platform/darwin/ios-common/network/NetworkIOS.mm
index 8b5e0b3602..da767a4f5b 100644
--- a/xbmc/platform/darwin/ios-common/network/NetworkIOS.mm
+++ b/xbmc/platform/darwin/ios-common/network/NetworkIOS.mm
@@ -392,7 +392,7 @@ std::vector<std::string> CNetworkIOS::GetNameServers()
static_cast<socklen_t>(s.sin.sin_len), static_cast<char*>(hostBuffer),
sizeof(hostBuffer), nullptr, 0, NI_NUMERICHOST) == 0)
{
- nameServers.push_back(hostBuffer);
+ nameServers.emplace_back(hostBuffer);
}
}
}
diff --git a/xbmc/platform/darwin/ios/LaunchScreen.storyboard b/xbmc/platform/darwin/ios/LaunchScreen.storyboard
index 35cdcde4f1..0674915722 100644
--- a/xbmc/platform/darwin/ios/LaunchScreen.storyboard
+++ b/xbmc/platform/darwin/ios/LaunchScreen.storyboard
@@ -18,7 +18,7 @@
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="splash.jpg" translatesAutoresizingMaskIntoConstraints="NO" id="kmc-Cc-145">
+ <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="applaunch_screen.png" translatesAutoresizingMaskIntoConstraints="NO" id="kmc-Cc-145">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
</imageView>
</subviews>
@@ -38,6 +38,6 @@
</scene>
</scenes>
<resources>
- <image name="splash.jpg" width="1920" height="1080"/>
+ <image name="applaunch_screen.png" width="1920" height="1080"/>
</resources>
</document>
diff --git a/xbmc/platform/darwin/osx/CMakeLists.txt b/xbmc/platform/darwin/osx/CMakeLists.txt
index 01db68f8e8..3824fd2545 100644
--- a/xbmc/platform/darwin/osx/CMakeLists.txt
+++ b/xbmc/platform/darwin/osx/CMakeLists.txt
@@ -2,15 +2,13 @@ set(SOURCES CocoaInterface.mm
CPUInfoOsx.cpp
GPUInfoMacOS.cpp
HotKeyController.m
- PlatformDarwinOSX.cpp
- smc.c)
+ PlatformDarwinOSX.cpp)
set(HEADERS CocoaInterface.h
CPUInfoOsx.h
GPUInfoMacOS.h
HotKeyController.h
- PlatformDarwinOSX.h
- smc.h)
+ PlatformDarwinOSX.h)
if(ENABLE_XBMCHELPER)
list(APPEND SOURCES XBMCHelper.cpp)
diff --git a/xbmc/platform/darwin/osx/CPUInfoOsx.cpp b/xbmc/platform/darwin/osx/CPUInfoOsx.cpp
index c100d3aac9..e1eb271ff4 100644
--- a/xbmc/platform/darwin/osx/CPUInfoOsx.cpp
+++ b/xbmc/platform/darwin/osx/CPUInfoOsx.cpp
@@ -8,14 +8,17 @@
#include "CPUInfoOsx.h"
+#include "ServiceBroker.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
#include "utils/Temperature.h"
-#include "platform/darwin/osx/smc.h"
#include "platform/posix/PosixResourceCounter.h"
#include <array>
#include <string>
+#include <smctemp.h>
#include <sys/sysctl.h>
#include <sys/types.h>
@@ -118,13 +121,19 @@ float CCPUInfoOsx::GetCPUFrequency()
bool CCPUInfoOsx::GetTemperature(CTemperature& temperature)
{
- int value = SMCGetTemperature(SMC_KEY_CPU_TEMP);
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cpuTempCmd.empty())
+ {
+ return CheckUserTemperatureCommand(temperature);
+ }
- temperature = CTemperature::CreateFromCelsius(value);
- if (temperature == CTemperature::CreateFromCelsius(0.0))
+ smctemp::SmcTemp smcTemp = smctemp::SmcTemp();
+ const double value = smcTemp.GetCpuTemp();
+ if (value <= 0.0)
{
temperature.SetValid(false);
+ return false;
}
+ temperature = CTemperature::CreateFromCelsius(value);
return true;
}
diff --git a/xbmc/platform/darwin/osx/CocoaInterface.h b/xbmc/platform/darwin/osx/CocoaInterface.h
index 1f76a5df0e..41d5788a23 100644
--- a/xbmc/platform/darwin/osx/CocoaInterface.h
+++ b/xbmc/platform/darwin/osx/CocoaInterface.h
@@ -30,11 +30,6 @@ extern "C"
char* Cocoa_MountPoint2DeviceName(char *path);
bool Cocoa_GetVolumeNameFromMountPoint(const std::string &mountPoint, std::string &volumeName);
- // Mouse.
- //
- void Cocoa_HideMouse();
- void Cocoa_ShowMouse();
-
const char *Cocoa_Paste() ;
#ifdef __cplusplus
diff --git a/xbmc/platform/darwin/osx/CocoaInterface.mm b/xbmc/platform/darwin/osx/CocoaInterface.mm
index a7129482a8..3e25da8744 100644
--- a/xbmc/platform/darwin/osx/CocoaInterface.mm
+++ b/xbmc/platform/darwin/osx/CocoaInterface.mm
@@ -12,11 +12,7 @@
#include "ServiceBroker.h"
#include "utils/StringUtils.h"
#include "utils/log.h"
-#if defined(HAS_SDL)
-#include "windowing/osx/SDL/WinSystemOSXSDL.h"
-#else
#include "windowing/osx/WinSystemOSX.h"
-#endif
#import <AudioToolbox/AudioToolbox.h>
#import <AudioUnit/AudioUnit.h>
@@ -34,16 +30,6 @@ static CVDisplayLinkRef displayLink = NULL;
CGDirectDisplayID Cocoa_GetDisplayIDFromScreen(NSScreen *screen);
-NSOpenGLContext* Cocoa_GL_GetCurrentContext(void)
-{
-#if defined(HAS_SDL)
- CWinSystemOSX *winSystem = dynamic_cast<CWinSystemOSX*>(CServiceBroker::GetWinSystem());
- return winSystem->GetNSOpenGLContext();
-#else
- return [NSOpenGLContext currentContext];
-#endif
-}
-
uint32_t Cocoa_GL_GetCurrentDisplayID(void)
{
// Find which display we are on from the current context (default to main display)
@@ -51,10 +37,8 @@ uint32_t Cocoa_GL_GetCurrentDisplayID(void)
NSNumber* __block screenID;
auto getScreenNumber = ^{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- screenID = Cocoa_GL_GetCurrentContext().view.window.screen.deviceDescription[@"NSScreenNumber"];
-#pragma clang diagnostic pop
+ screenID =
+ NSApplication.sharedApplication.keyWindow.screen.deviceDescription[@"NSScreenNumber"];
};
if (NSThread.isMainThread)
getScreenNumber();
@@ -73,10 +57,8 @@ bool Cocoa_CVDisplayLinkCreate(void *displayLinkcallback, void *displayLinkConte
// OpenGL Flush synchronised with vertical retrace
GLint swapInterval = 1;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
-#pragma clang diagnostic pop
+ [[NSOpenGLContext currentContext] setValues:&swapInterval
+ forParameter:NSOpenGLContextParameterSwapInterval];
display_id = (CGDirectDisplayID)Cocoa_GL_GetCurrentDisplayID();
if (!displayLink)
@@ -235,16 +217,6 @@ bool Cocoa_GetVolumeNameFromMountPoint(const std::string &mountPoint, std::strin
}
}
-void Cocoa_HideMouse()
-{
- [NSCursor hide];
-}
-
-void Cocoa_ShowMouse()
-{
- [NSCursor unhide];
-}
-
//---------------------------------------------------------------------------------
const char *Cocoa_Paste()
{
diff --git a/xbmc/platform/darwin/osx/GPUInfoMacOS.cpp b/xbmc/platform/darwin/osx/GPUInfoMacOS.cpp
index 5594d9bbf2..4d20a54c93 100644
--- a/xbmc/platform/darwin/osx/GPUInfoMacOS.cpp
+++ b/xbmc/platform/darwin/osx/GPUInfoMacOS.cpp
@@ -8,7 +8,7 @@
#include "GPUInfoMacOS.h"
-#include "platform/darwin/osx/smc.h"
+#include <smctemp.h>
std::unique_ptr<CGPUInfo> CGPUInfo::GetGPUInfo()
{
@@ -22,9 +22,11 @@ bool CGPUInfoMacOS::SupportsPlatformTemperature() const
bool CGPUInfoMacOS::GetGPUPlatformTemperature(CTemperature& temperature) const
{
- double temperatureValue = SMCGetTemperature(SMC_KEY_GPU_TEMP);
+ smctemp::SmcTemp smcTemp = smctemp::SmcTemp();
+ const double temperatureValue = smcTemp.GetGpuTemp();
if (temperatureValue <= 0.0)
{
+ temperature.SetValid(false);
return false;
}
temperature = CTemperature::CreateFromCelsius(temperatureValue);
diff --git a/xbmc/platform/darwin/osx/SDL/CMakeLists.txt b/xbmc/platform/darwin/osx/SDL/CMakeLists.txt
deleted file mode 100644
index a9badcf8c6..0000000000
--- a/xbmc/platform/darwin/osx/SDL/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-if(SDL_FOUND)
- set(SOURCES OSXTextInputResponder.mm)
-
- set(HEADERS OSXTextInputResponder.h)
-endif()
-
-if(SOURCES)
- core_add_library(platform_osx_SDL)
-endif()
diff --git a/xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.h b/xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.h
deleted file mode 100644
index 0448bdbd09..0000000000
--- a/xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2005-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.
- */
-
-#import <Cocoa/Cocoa.h>
-
-@interface OSXTextInputResponder : NSView <NSTextInputClient>
-{
- NSString *_markedText;
- NSRange _markedRange;
- NSRange _selectedRange;
- NSRect _inputRect;
-}
-- (void) setInputRect:(NSRect) rect;
-@end
diff --git a/xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.mm b/xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.mm
deleted file mode 100644
index 0f87beb96a..0000000000
--- a/xbmc/platform/darwin/osx/SDL/OSXTextInputResponder.mm
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2005-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.
- */
-
-#import "OSXTextInputResponder.h"
-
-#include "GUIUserMessages.h"
-#include "ServiceBroker.h"
-#include "guilib/GUIWindowManager.h"
-#include "input/Key.h"
-#include "messaging/ApplicationMessenger.h"
-#include "utils/log.h"
-
-void SendKeyboardText(const char *text)
-{
- // CLog::Log(LOGDEBUG, "SendKeyboardText({})", text);
-
- /* Don't post text events for unprintable characters */
- if ((unsigned char)*text < ' ' || *text == 127)
- return;
-
- CAction *action = new CAction(ACTION_INPUT_TEXT);
- action->SetText(text);
- CServiceBroker::GetAppMessenger()->PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
- static_cast<void*>(action));
-}
-
-void SendEditingText(const char *text, unsigned int location, unsigned int length)
-{
- //@todo fix this hack
- // CLog::Log(LOGDEBUG, "SendEditingText({}, {}, {})", text, location, length);
- // CGUIMessage msg(GUI_MSG_INPUT_TEXT_EDIT, 0, 0, location, length);
- // msg.SetLabel(text);
- // CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
-}
-
-@implementation OSXTextInputResponder
-
-- (id)initWithFrame:(NSRect)frame
-{
- self = [super initWithFrame:frame];
- if (self) {
- // Initialization code here.
- _selectedRange = NSMakeRange(0, 0);
- _markedRange = NSMakeRange(NSNotFound, 0);
- _inputRect = NSZeroRect;
- }
-
- return self;
-}
-
-- (void) setInputRect:(NSRect) rect
-{
- _inputRect = rect;
-}
-
-- (void) insertText:(id) aString replacementRange:(NSRange)replacementRange
-{
- const char *str;
-
- if (replacementRange.location == NSNotFound) {
- if (_markedRange.location != NSNotFound) {
- replacementRange = _markedRange;
- } else {
- replacementRange = _selectedRange;
- }
- }
-
- /* Could be NSString or NSAttributedString, so we have
- * to test and convert it */
- if ([aString isKindOfClass: [NSAttributedString class]])
- str = [[aString string] UTF8String];
- else
- str = [aString UTF8String];
-
- SendKeyboardText(str);
-
- [self unmarkText];
-}
-
-- (void) doCommandBySelector:(SEL) myselector
-{
- // No need to do anything since we are not using Cocoa
- // selectors to handle special keys, instead we use SDL
- // key events to do the same job.
-}
-
-- (BOOL) hasMarkedText
-{
- return _markedText != nil;
-}
-
-- (NSRange) markedRange
-{
- return _markedRange;
-}
-
-- (NSRange) selectedRange
-{
- return _selectedRange;
-}
-
-- (void) setMarkedText:(id) aString
- selectedRange:(NSRange) selRange
- replacementRange:(NSRange) replacementRange
-{
- if (replacementRange.location == NSNotFound) {
- if (_markedRange.location != NSNotFound)
- replacementRange = _markedRange;
- else
- replacementRange = _selectedRange;
- }
-
- if ([aString isKindOfClass: [NSAttributedString class]])
- aString = [aString string];
-
- if ([aString length] == 0)
- {
- [self unmarkText];
- return;
- }
-
- _markedText = aString;
- _selectedRange = selRange;
-// _markedRange = NSMakeRange(0, [aString length]);
- _markedRange = NSMakeRange(replacementRange.location, [aString length]);
-
- SendEditingText([aString UTF8String], selRange.location, selRange.length);
-}
-
-- (void) unmarkText
-{
- _markedText = nil;
- _markedRange = NSMakeRange(NSNotFound, 0);
-
- SendEditingText("", 0, 0);
-}
-
-- (NSRect) firstRectForCharacterRange: (NSRange) theRange
- actualRange:(NSRangePointer)actualRange
-{
- if (actualRange)
- *actualRange = theRange;
-
- NSWindow *window = [self window];
- NSRect contentRect = [window contentRectForFrameRect: [window frame]];
- double windowHeight = contentRect.size.height;
- NSRect rect = NSMakeRect(_inputRect.origin.x, windowHeight - _inputRect.origin.y - _inputRect.size.height,
- _inputRect.size.width, _inputRect.size.height);
-
- // CLog::Log(LOGDEBUG, "firstRectForCharacterRange: ({}, {}): windowHeight = {}, rect = {}",
- // theRange.location, theRange.length, windowHeight,
- // [NSStringFromRect(rect) UTF8String]);
- rect.origin = [[self window] convertRectToScreen:rect].origin;
-
- return rect;
-}
-
-- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)theRange
- actualRange:(NSRangePointer)actualRange
-{
- return nil;
-}
-
-// This method returns the index for character that is
-// nearest to thePoint. thPoint is in screen coordinate system.
-- (NSUInteger) characterIndexForPoint:(NSPoint) thePoint
-{
- return 0;
-}
-
-// This method is the key to attribute extension.
-// We could add new attributes through this method.
-// NSInputServer examines the return value of this
-// method & constructs appropriate attributed string.
-- (NSArray *) validAttributesForMarkedText
-{
- return [NSArray array];
-}
-
-@end
diff --git a/xbmc/platform/darwin/osx/SDL/SDLMain.mm b/xbmc/platform/darwin/osx/SDL/SDLMain.mm
deleted file mode 100644
index c57e3de0aa..0000000000
--- a/xbmc/platform/darwin/osx/SDL/SDLMain.mm
+++ /dev/null
@@ -1,580 +0,0 @@
-/*
- * SDLMain.mm - main entry point for our Cocoa-ized SDL app
- * Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
- * Non-NIB-Code & other changes: Max Horn <max@quendi.de>
- *
- * SPDX-License-Identifier: Unlicense
- * See LICENSES/README.md for more information.
- */
-
-#import "messaging/ApplicationMessenger.h"
-
-#import "platform/darwin/osx/CocoaInterface.h"
-#import "platform/darwin/osx/HotKeyController.h"
-#import "platform/darwin/osx/XBMCApplication.h"
-#import "platform/darwin/osx/storage/OSXStorageProvider.h"
-
-#import <SDL/SDL.h>
-#import <sys/param.h> /* for MAXPATHLEN */
-#import <unistd.h>
-
-#import "PlatformDefs.h"
-
-// For some reason, Apple removed setAppleMenu from the headers in 10.4,
-// but the method still is there and works. To avoid warnings, we declare
-// it ourselves here.
-@interface NSApplication(SDL_Missing_Methods)
-- (void)setAppleMenu:(NSMenu *)menu;
-@end
-
-// Use this flag to determine whether we use CPS (docking) or not
-#define SDL_USE_CPS 1
-#ifdef SDL_USE_CPS
-// Portions of CPS.h
-typedef struct CPSProcessSerNum
-{
- UInt32 lo;
- UInt32 hi;
-} CPSProcessSerNum;
-
-extern "C" {
-extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn);
-extern OSErr CPSEnableForegroundOperation(CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
-extern OSErr CPSSetFrontProcess(CPSProcessSerNum *psn);
-}
-#endif /* SDL_USE_CPS */
-
-static int gArgc;
-static char **gArgv;
-static BOOL gCalledAppMainline = FALSE;
-
-static NSString *getApplicationName(void)
-{
- NSDictionary *dict;
- NSString *appName = 0;
-
- // Determine the application name
- dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
- if (dict)
- appName = [dict objectForKey: @"CFBundleName"];
-
- if (![appName length])
- appName = [[NSProcessInfo processInfo] processName];
-
- return appName;
-}
-static void setupApplicationMenu(void)
-{
- // warning: this code is very odd
- NSMenu *appleMenu;
- NSMenuItem *menuItem;
- NSString *title;
- NSString *appName;
-
- appName = getApplicationName();
- appleMenu = [[NSMenu alloc] initWithTitle:@""];
-
- // Add menu items
- title = [@"About " stringByAppendingString:appName];
- [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
-
- [appleMenu addItem:[NSMenuItem separatorItem]];
-
- title = [@"Hide " stringByAppendingString:appName];
- [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
-
- menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
- [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
-
- [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
-
- [appleMenu addItem:[NSMenuItem separatorItem]];
-
- title = [@"Quit " stringByAppendingString:appName];
- [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
-
-
- // Put menu into the menubar
- menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
- [menuItem setSubmenu:appleMenu];
- [[NSApp mainMenu] addItem:menuItem];
-
- // Tell the application object that this is now the application menu
- [NSApp setAppleMenu:appleMenu];
-}
-
-// Create a window menu
-static void setupWindowMenu(void)
-{
- NSMenu *windowMenu;
- NSMenuItem *windowMenuItem;
- NSMenuItem *menuItem;
-
- windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
-
- // "Full/Windowed Toggle" item
- menuItem = [[NSMenuItem alloc] initWithTitle:@"Full/Windowed Toggle" action:@selector(fullScreenToggle:) keyEquivalent:@"f"];
- // this is just for display purposes, key handling is in CWinEventsSDL::ProcessOSXShortcuts()
- menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl;
- [windowMenu addItem:menuItem];
-
- // "Full/Windowed Toggle" item
- menuItem = [[NSMenuItem alloc] initWithTitle:@"Float on Top" action:@selector(floatOnTopToggle:) keyEquivalent:@"t"];
- [windowMenu addItem:menuItem];
-
- // "Minimize" item
- menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
- [windowMenu addItem:menuItem];
-
- // "Title Bar" item
- menuItem = [[NSMenuItem alloc] initWithTitle:@"Title Bar" action:@selector(titlebarToggle:) keyEquivalent:@""];
- [windowMenu addItem:menuItem];
- [menuItem setState: true];
-
- // Put menu into the menubar
- windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
- [windowMenuItem setSubmenu:windowMenu];
- [[NSApp mainMenu] addItem:windowMenuItem];
-
- // Tell the application object that this is now the window menu
- [NSApp setWindowsMenu:windowMenu];
-}
-
-@interface XBMCApplication : NSApplication
-@end
-
-@implementation XBMCApplication
-
-// Called before the internal event loop has started running.
-- (void) finishLaunching
-{
- [super finishLaunching];
-}
-
-// Invoked from the Quit menu item
-- (void)terminate:(id)sender
-{
- // remove any notification handlers
- [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- // Post a SDL_QUIT event
- SDL_Event event;
- event.type = SDL_QUIT;
- SDL_PushEvent(&event);
-}
-
-- (void)fullScreenToggle:(id)sender
-{
- // Post an toggle full-screen event to the application thread.
- SDL_Event event;
- memset(&event, 0, sizeof(event));
- event.type = SDL_USEREVENT;
- event.user.code = TMSG_TOGGLEFULLSCREEN;
- SDL_PushEvent(&event);
-}
-
-- (void)floatOnTopToggle:(id)sender
-{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- NSWindow* window = [[[NSOpenGLContext currentContext] view] window];
-#pragma clang diagnostic pop
- if ([window level] == NSFloatingWindowLevel)
- {
- [window setLevel:NSNormalWindowLevel];
- [sender setState:NSControlStateValueOff];
- }
- else
- {
- [window setLevel:NSFloatingWindowLevel];
- [sender setState:NSControlStateValueOn];
- }
-}
-
-- (void)titlebarToggle:(id)sender
-{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- NSWindow* window = [[[NSOpenGLContext currentContext] view] window];
-#pragma clang diagnostic pop
- [window setStyleMask: [window styleMask] ^ NSWindowStyleMaskTitled ];
- BOOL isSet = [window styleMask] & NSWindowStyleMaskTitled;
- [window setMovableByWindowBackground: !isSet];
- [sender setState: isSet];
-
-}
-
-
-@end
-
-// The main class of the application, the application's delegate
-@implementation XBMCDelegate
-
-// Set the working directory to the .app's parent directory
-- (void) setupWorkingDirectory
-{
- char parentdir[MAXPATHLEN];
- CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
- CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
- if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN))
- {
- assert( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
- }
- CFRelease(url);
- CFRelease(url2);
-}
-
-// Doesnt currently get called. Keep in case we fix the root cause
-- (void) applicationWillTerminate: (NSNotification *) note
-{
- // disabled as currently main() explicitly calls this.
- // [self appshutdownCleanup];
-}
-
-- (void) applicationWillResignActive:(NSNotification *) note
-{
- //[[HotKeyController sharedController] sysPower:NO];
- //[[HotKeyController sharedController] sysVolume:NO];
- [[HotKeyController sharedController] setActive:NO];
-}
-
-- (void) applicationWillBecomeActive:(NSNotification *) note
-{
- //[[HotKeyController sharedController] sysPower:YES];
- //[[HotKeyController sharedController] sysVolume:YES];
- [[HotKeyController sharedController] setActive:YES];
-}
-
-// To use Cocoa on secondary POSIX threads, your application must first detach
-// at least one NSThread object, which can immediately exit. Some info says this
-// is not required anymore, who knows ?
-- (void) kickstartMultiThreaded:(id)arg
-{
- @autoreleasepool
- {
- // empty
- }
-}
-
-- (void)appshutdownCleanup
-{
- [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self
- name:NSWorkspaceDidMountNotification
- object:nil];
-
- [[[NSWorkspace sharedWorkspace] notificationCenter]
- removeObserver:self
- name:NSWorkspaceDidUnmountNotification
- object:nil];
-
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
-
- [center removeObserver:self name:MediaKeyPower object:nil];
- [center removeObserver:self name:MediaKeySoundMute object:nil];
- [center removeObserver:self name:MediaKeySoundUp object:nil];
- [center removeObserver:self name:MediaKeySoundDown object:nil];
- [center removeObserver:self name:MediaKeyPlayPauseNotification object:nil];
- [center removeObserver:self name:MediaKeyFastNotification object:nil];
- [center removeObserver:self name:MediaKeyRewindNotification object:nil];
- [center removeObserver:self name:MediaKeyNextNotification object:nil];
- [center removeObserver:self name:MediaKeyPreviousNotification object:nil];
-
- [[HotKeyController sharedController] disableTap];
-}
-
-// Called after the internal event loop has started running.
-- (void) applicationDidFinishLaunching: (NSNotification *) note
-{
- // enable multithreading, we should NOT have to do this but as we are mixing NSThreads/pthreads...
- if (![NSThread isMultiThreaded])
- [NSThread detachNewThreadSelector:@selector(kickstartMultiThreaded:) toTarget:self withObject:nil];
-
- // Set the working directory to the .app's parent directory
- [self setupWorkingDirectory];
-
- [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
- selector:@selector(deviceDidMountNotification:)
- name:NSWorkspaceDidMountNotification
- object:nil];
-
- [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
- selector:@selector(deviceDidUnMountNotification:)
- name:NSWorkspaceDidUnmountNotification
- object:nil];
-
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
-
- // create media key handler singleton
- [[HotKeyController sharedController] enableTap];
- // add media key notifications
- [center addObserver:self
- selector:@selector(powerKeyNotification)
- name:MediaKeyPower object:nil];
- [center addObserver:self
- selector:@selector(muteKeyNotification)
- name:MediaKeySoundMute object:nil];
- [center addObserver:self
- selector:@selector(soundUpKeyNotification)
- name:MediaKeySoundUp object:nil];
- [center addObserver:self
- selector:@selector(soundDownKeyNotification)
- name:MediaKeySoundDown object:nil];
- [center addObserver:self
- selector:@selector(playPauseKeyNotification)
- name:MediaKeyPlayPauseNotification object:nil];
- [center addObserver:self
- selector:@selector(fastKeyNotification)
- name:MediaKeyFastNotification object:nil];
- [center addObserver:self
- selector:@selector(rewindKeyNotification)
- name:MediaKeyRewindNotification object:nil];
- [center addObserver:self
- selector:@selector(nextKeyNotification)
- name:MediaKeyNextNotification object:nil];
- [center addObserver:self
- selector:@selector(previousKeyNotification)
- name:MediaKeyPreviousNotification object:nil];
-
- // We're going to manually manage the screensaver.
- setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", true);
-
- // Hand off to main application code
- gCalledAppMainline = TRUE;
-
- // stop the main loop so we return to main (below) and can
- // call SDL_main there.
- [NSApp stop:nil];
-
- //post a NOP event, so the run loop actually stops
- //see http://www.cocoabuilder.com/archive/cocoa/219842-nsapp-stop.html
- NSEvent* event = [NSEvent otherEventWithType: NSEventTypeApplicationDefined
- location: NSMakePoint(0,0)
- modifierFlags: 0
- timestamp: 0.0
- windowNumber: 0
- context: nil
- subtype: 0
- data1: 0
- data2: 0];
- //
- [NSApp postEvent: event atStart: true];
-}
-
-/*
- * Catch document open requests...this lets us notice files when the app
- * was launched by double-clicking a document, or when a document was
- * dragged/dropped on the app's icon. You need to have a
- * CFBundleDocumentsType section in your Info.plist to get this message,
- * apparently.
- *
- * Files are added to gArgv, so to the app, they'll look like command line
- * arguments. Previously, apps launched from the finder had nothing but
- * an argv[0].
- *
- * This message may be received multiple times to open several docs on launch.
- *
- * This message is ignored once the app's mainline has been called.
- */
-- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
-{
- const char *temparg;
- size_t arglen;
- char *arg;
- char **newargv;
-
- // app has started, ignore this document.
- if (gCalledAppMainline)
- return FALSE;
-
- temparg = [filename UTF8String];
- arglen = SDL_strlen(temparg) + 1;
- arg = (char *) SDL_malloc(arglen);
- if (arg == NULL)
- return FALSE;
-
- newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
- if (newargv == NULL)
- {
- SDL_free(arg);
- return FALSE;
- }
- gArgv = newargv;
-
- SDL_strlcpy(arg, temparg, arglen);
- gArgv[gArgc++] = arg;
- gArgv[gArgc] = NULL;
-
- return TRUE;
-}
-
-- (void) deviceDidMountNotification:(NSNotification *) note
-{
- // calling into c++ code, need to use autorelease pools
- @autoreleasepool
- {
- NSString* volumeLabel = [note.userInfo objectForKey:@"NSWorkspaceVolumeLocalizedNameKey"];
- const char* label = [volumeLabel UTF8String];
-
- NSString* volumePath = [note.userInfo objectForKey:@"NSDevicePath"];
- const char* path = [volumePath UTF8String];
-
- COSXStorageProvider::VolumeMountNotification(label, path);
- }
-}
-
-- (void) deviceDidUnMountNotification:(NSNotification *) note
-{
- // calling into c++ code, need to use autorelease pools
- @autoreleasepool
- {
- NSString* volumeLabel = [note.userInfo objectForKey:@"NSWorkspaceVolumeLocalizedNameKey"];
- const char* label = [volumeLabel UTF8String];
-
- NSString* volumePath = [note.userInfo objectForKey:@"NSDevicePath"];
- const char* path = [volumePath UTF8String];
-
- COSXStorageProvider::VolumeUnmountNotification(label, path);
- }
-}
-
-static void keyPress(SDLKey key)
-{
- SDL_Event event;
- memset(&event, 0, sizeof(event));
- event.type = SDL_KEYDOWN;
- event.key.keysym.sym = key;
- SDL_PushEvent(&event);
- event.type = SDL_KEYUP;
- SDL_PushEvent(&event);
-}
-
-#define VK_SLEEP 0x143
-#define VK_VOLUME_MUTE 0xAD
-#define VK_VOLUME_DOWN 0xAE
-#define VK_VOLUME_UP 0xAF
-#define VK_MEDIA_NEXT_TRACK 0x9E
-#define VK_MEDIA_PREV_TRACK 0x9D
-#define VK_MEDIA_STOP 0xB2
-#define VK_MEDIA_PLAY_PAUSE 0xB3
-#define VK_REWIND 0xB1
-#define VK_FAST_FWD 0xB0
-
-- (void)powerKeyNotification
-{
- keyPress((SDLKey)VK_SLEEP);
-}
-
-- (void)muteKeyNotification
-{
- keyPress((SDLKey)VK_VOLUME_MUTE);
-}
-- (void)soundUpKeyNotification
-{
- keyPress((SDLKey)VK_VOLUME_UP);
-}
-- (void)soundDownKeyNotification
-{
- keyPress((SDLKey)VK_VOLUME_DOWN);
-}
-
-- (void)playPauseKeyNotification
-{
- keyPress((SDLKey)VK_MEDIA_PLAY_PAUSE);
-}
-
-- (void)fastKeyNotification
-{
- keyPress((SDLKey)VK_FAST_FWD);
-}
-
-- (void)rewindKeyNotification
-{
- keyPress((SDLKey)VK_REWIND);
-}
-
-- (void)nextKeyNotification
-{
- keyPress((SDLKey)VK_MEDIA_NEXT_TRACK);
-}
-
-- (void)previousKeyNotification
-{
- keyPress((SDLKey)VK_MEDIA_PREV_TRACK);
-}
-
-@end
-
-#ifdef main
-# undef main
-#endif
-/* Main entry point to executable - should *not* be SDL_main! */
-int main(int argc, char *argv[])
-{
- @autoreleasepool
- {
- XBMCDelegate* xbmc_delegate;
-
- // Block SIGPIPE
- // SIGPIPE repeatably kills us, turn it off
- {
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, SIGPIPE);
- sigprocmask(SIG_BLOCK, &set, NULL);
- }
-
- /* Copy the arguments into a global variable */
- /* This is passed if we are launched by double-clicking */
- if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0)
- {
- gArgv = (char**)SDL_malloc(sizeof(char*) * 2);
- gArgv[0] = argv[0];
- gArgv[1] = NULL;
- gArgc = 1;
- }
- else
- {
- gArgc = argc;
- gArgv = (char**)SDL_malloc(sizeof(char*) * (argc + 1));
- for (int i = 0; i <= argc; i++)
- gArgv[i] = argv[i];
- }
-
- // Ensure the application object is initialised
- [XBMCApplication sharedApplication];
-
-#ifdef SDL_USE_CPS
- {
- CPSProcessSerNum PSN;
- /* Tell the dock about us */
- if (!CPSGetCurrentProcess(&PSN))
- if (!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103))
- if (!CPSSetFrontProcess(&PSN))
- [XBMCApplication sharedApplication];
- }
-#endif
-
- // Set up the menubars
- [NSApp setMainMenu:[[NSMenu alloc] init]];
- setupApplicationMenu();
- setupWindowMenu();
-
- // Create XBMCDelegate and make it the app delegate
- xbmc_delegate = [[XBMCDelegate alloc] init];
- [[NSApplication sharedApplication] setDelegate:xbmc_delegate];
-
- // Start the main event loop
- [NSApp run];
-
- // call SDL_main which calls our real main in xbmc.cpp
- // see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2008-September/066542.html
- int status;
- status = SDL_main(gArgc, gArgv);
- SDL_Quit();
-
- [xbmc_delegate appshutdownCleanup];
-
- return status;
- }
-}
diff --git a/xbmc/platform/darwin/osx/smc.c b/xbmc/platform/darwin/osx/smc.c
deleted file mode 100644
index 559ceab11e..0000000000
--- a/xbmc/platform/darwin/osx/smc.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Apple System Management Control (SMC) Tool
- * Copyright (C) 2006 devnull
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- * See LICENSES/README.md for more information.
- */
-
-#include "smc.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include <IOKit/IOKitLib.h>
-
-static io_connect_t conn;
-
-UInt32 _strtoul(const char *str, int size, int base)
-{
- UInt32 total = 0;
- int i;
-
- for (i = 0; i < size; i++)
- {
- if (base == 16)
- total += str[i] << (size - 1 - i) * 8;
- else
- total += (unsigned char) (str[i] << (size - 1 - i) * 8);
- }
- return total;
-}
-
-void _ultostr(char *str, UInt32 val)
-{
- str[0] = '\0';
- sprintf(str, "%c%c%c%c",
- (unsigned int) val >> 24,
- (unsigned int) val >> 16,
- (unsigned int) val >> 8,
- (unsigned int) val);
-}
-
-kern_return_t SMCOpen(void)
-{
- mach_port_t masterPort;
- io_iterator_t iterator;
- io_object_t device;
-
- kern_return_t result = IOMasterPort(MACH_PORT_NULL, &masterPort);
-
- CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
- result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
- if (result != kIOReturnSuccess)
- {
- printf("Error: IOServiceGetMatchingServices() = %08x\n", result);
- return 1;
- }
-
- device = IOIteratorNext(iterator);
- IOObjectRelease(iterator);
- if (device == 0)
- {
- printf("Error: no SMC found\n");
- return 1;
- }
-
- result = IOServiceOpen(device, mach_task_self(), 0, &conn);
- IOObjectRelease(device);
- if (result != kIOReturnSuccess)
- {
- printf("Error: IOServiceOpen() = %08x\n", result);
- return 1;
- }
-
- return kIOReturnSuccess;
-}
-
-kern_return_t SMCClose()
-{
- return IOServiceClose(conn);
-}
-
-
-kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure)
-{
- size_t structureInputSize;
- size_t structureOutputSize;
-
- structureInputSize = sizeof(SMCKeyData_t);
- structureOutputSize = sizeof(SMCKeyData_t);
-
- return IOConnectCallStructMethod( conn, index,
- // inputStructure
- inputStructure, structureInputSize,
- // outputStructure
- outputStructure, &structureOutputSize );
-}
-
-kern_return_t SMCReadKey(UInt32ConstChar_t key, SMCVal_t *val)
-{
- kern_return_t result;
- SMCKeyData_t inputStructure = {};
- SMCKeyData_t outputStructure = {};
-
- memset(val, 0, sizeof(SMCVal_t));
-
- inputStructure.key = _strtoul(key, 4, 16);
- inputStructure.data8 = SMC_CMD_READ_KEYINFO;
-
- result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
- if (result != kIOReturnSuccess)
- return result;
-
- val->dataSize = outputStructure.keyInfo.dataSize;
- _ultostr(val->dataType, outputStructure.keyInfo.dataType);
- inputStructure.keyInfo.dataSize = val->dataSize;
- inputStructure.data8 = SMC_CMD_READ_BYTES;
-
- result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
- if (result != kIOReturnSuccess)
- return result;
-
- memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
-
- return kIOReturnSuccess;
-}
-
-double SMCGetTemperature(const char *key)
-{
- SMCVal_t val;
- kern_return_t result;
- SMCOpen();
- result = SMCReadKey(key, &val);
- SMCClose();
- if (result == kIOReturnSuccess) {
- // read succeeded - check returned value
- if (val.dataSize > 0) {
- if (strcmp(val.dataType, DATATYPE_SP78) == 0) {
- // convert fp78 value to temperature
- int intValue = (val.bytes[0] * 256 + val.bytes[1]) >> 2;
- return intValue / 64.0;
- }
- }
- }
- // read failed
- return 0.0;
-}
-
diff --git a/xbmc/platform/darwin/osx/smc.h b/xbmc/platform/darwin/osx/smc.h
deleted file mode 100644
index 64fe19418f..0000000000
--- a/xbmc/platform/darwin/osx/smc.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Apple System Management Control (SMC) Tool
- * Copyright (C) 2006 devnull
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- * See LICENSES/README.md for more information.
- */
-
-#pragma once
-
-#include <libkern/OSTypes.h>
-
-#define SMC_VERSION "0.01"
-
-#define OP_NONE 0
-#define OP_LIST 1
-#define OP_READ 2
-#define OP_READ_FAN 3
-#define OP_WRITE 4
-
-#define KERNEL_INDEX_SMC 2
-
-#define SMC_CMD_READ_BYTES 5
-#define SMC_CMD_WRITE_BYTES 6
-#define SMC_CMD_READ_INDEX 8
-#define SMC_CMD_READ_KEYINFO 9
-#define SMC_CMD_READ_PLIMIT 11
-#define SMC_CMD_READ_VERS 12
-
-#define DATATYPE_FPE2 "fpe2"
-#define DATATYPE_UINT8 "ui8 "
-#define DATATYPE_UINT16 "ui16"
-#define DATATYPE_UINT32 "ui32"
-#define DATATYPE_SP78 "sp78"
-
-// key values
-#define SMC_KEY_CPU_TEMP "TC0D"
-#define SMC_KEY_GPU_TEMP "TG0D"
-#define SMC_KEY_FAN0_RPM_MIN "F0Mn"
-#define SMC_KEY_FAN1_RPM_MIN "F1Mn"
-#define SMC_KEY_FAN0_RPM_CUR "F0Ac"
-#define SMC_KEY_FAN1_RPM_CUR "F1Ac"
-
-
-typedef struct {
- char major;
- char minor;
- char build;
- char reserved[1];
- UInt16 release;
-} SMCKeyData_vers_t;
-
-typedef struct {
- UInt16 version;
- UInt16 length;
- UInt32 cpuPLimit;
- UInt32 gpuPLimit;
- UInt32 memPLimit;
-} SMCKeyData_pLimitData_t;
-
-typedef struct {
- UInt32 dataSize;
- UInt32 dataType;
- char dataAttributes;
-} SMCKeyData_keyInfo_t;
-
-typedef char SMCBytes_t[32];
-
-typedef struct {
- UInt32 key;
- SMCKeyData_vers_t vers;
- SMCKeyData_pLimitData_t pLimitData;
- SMCKeyData_keyInfo_t keyInfo;
- char result;
- char status;
- char data8;
- UInt32 data32;
- SMCBytes_t bytes;
-} SMCKeyData_t;
-
-typedef const char UInt32ConstChar_t[5];
-typedef char UInt32Char_t[5];
-
-typedef struct {
- UInt32Char_t key;
- UInt32 dataSize;
- UInt32Char_t dataType;
- SMCBytes_t bytes;
-} SMCVal_t;
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-// prototypes
-double SMCGetTemperature(const char *key);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/xbmc/platform/darwin/osx/storage/OSXStorageProvider.cpp b/xbmc/platform/darwin/osx/storage/OSXStorageProvider.cpp
index 218a0bf03b..4702012812 100644
--- a/xbmc/platform/darwin/osx/storage/OSXStorageProvider.cpp
+++ b/xbmc/platform/darwin/osx/storage/OSXStorageProvider.cpp
@@ -325,11 +325,11 @@ bool COSXStorageProvider::PumpDriveChangeEvents(IStorageEventsCallback* callback
void COSXStorageProvider::VolumeMountNotification(const char* label, const char* mountpoint)
{
if (label && mountpoint)
- m_mountsToNotify.emplace_back(std::make_pair(label, mountpoint));
+ m_mountsToNotify.emplace_back(label, mountpoint);
}
void COSXStorageProvider::VolumeUnmountNotification(const char* label, const char* mountpoint)
{
if (label && mountpoint)
- m_unmountsToNotify.emplace_back(std::make_pair(label, mountpoint));
+ m_unmountsToNotify.emplace_back(label, mountpoint);
}
diff --git a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/.gitignore b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/.gitignore
index 93140602aa..a7eae87e67 100644
--- a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/.gitignore
+++ b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/.gitignore
@@ -1 +1 @@
-splash@2x.jpg
+applaunch_screen@2x.png
diff --git a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json
index 053b4a34bb..58afd02e59 100644
--- a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json
+++ b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -1,24 +1,24 @@
{
- "images" : [
+ "images": [
{
- "orientation" : "landscape",
- "idiom" : "tv",
- "filename" : "splash.jpg",
- "extent" : "full-screen",
- "minimum-system-version" : "9.0",
- "scale" : "1x"
+ "orientation": "landscape",
+ "idiom": "tv",
+ "filename": "applaunch_screen.png",
+ "extent": "full-screen",
+ "minimum-system-version": "9.0",
+ "scale": "1x"
},
{
- "orientation" : "landscape",
- "idiom" : "tv",
- "filename" : "splash@2x.jpg",
- "extent" : "full-screen",
- "minimum-system-version" : "11.0",
- "scale" : "2x"
+ "orientation": "landscape",
+ "idiom": "tv",
+ "filename": "applaunch_screen@2x.png",
+ "extent": "full-screen",
+ "minimum-system-version": "11.0",
+ "scale": "2x"
}
],
- "info" : {
- "version" : 1,
- "author" : "xcode"
+ "info": {
+ "version": 1,
+ "author": "xcode"
}
}
diff --git a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/applaunch_screen.png b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/applaunch_screen.png
new file mode 120000
index 0000000000..bf4c72d343
--- /dev/null
+++ b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/applaunch_screen.png
@@ -0,0 +1 @@
+../../../../../../media/applaunch_screen.png \ No newline at end of file
diff --git a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/splash.jpg b/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/splash.jpg
deleted file mode 120000
index 38457c1d9a..0000000000
--- a/xbmc/platform/darwin/tvos/Assets.xcassets/LaunchImage.launchimage/splash.jpg
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../media/splash.jpg \ No newline at end of file
diff --git a/xbmc/platform/freebsd/PlatformFreebsd.cpp b/xbmc/platform/freebsd/PlatformFreebsd.cpp
index b65501b522..c949a339cb 100644
--- a/xbmc/platform/freebsd/PlatformFreebsd.cpp
+++ b/xbmc/platform/freebsd/PlatformFreebsd.cpp
@@ -8,6 +8,8 @@
#include "PlatformFreebsd.h"
+#include "ServiceBroker.h"
+#include "application/AppParams.h"
#include "utils/StringUtils.h"
#include "platform/freebsd/OptionalsReg.h"
@@ -79,27 +81,25 @@ bool CPlatformFreebsd::InitStageOne()
CLinuxPowerSyscall::Register();
- std::string envSink;
- if (getenv("KODI_AE_SINK"))
- envSink = getenv("KODI_AE_SINK");
+ std::string_view sink = CServiceBroker::GetAppParams()->GetAudioBackend();
- if (StringUtils::EqualsNoCase(envSink, "ALSA"))
+ if (sink == "alsa")
{
OPTIONALS::ALSARegister();
}
- else if (StringUtils::EqualsNoCase(envSink, "PULSE"))
+ else if (sink == "pulseaudio")
{
OPTIONALS::PulseAudioRegister();
}
- else if (StringUtils::EqualsNoCase(envSink, "OSS"))
+ else if (sink == "oss")
{
OPTIONALS::OSSRegister();
}
- else if (StringUtils::EqualsNoCase(envSink, "SNDIO"))
+ else if (sink == "sndio")
{
OPTIONALS::SndioRegister();
}
- else if (StringUtils::EqualsNoCase(envSink, "ALSA+PULSE"))
+ else if (sink == "alsa+pulseaudio")
{
OPTIONALS::ALSARegister();
OPTIONALS::PulseAudioRegister();
diff --git a/xbmc/platform/linux/AppParamParserLinux.cpp b/xbmc/platform/linux/AppParamParserLinux.cpp
index fcc2ddde4e..ec83e5f470 100644
--- a/xbmc/platform/linux/AppParamParserLinux.cpp
+++ b/xbmc/platform/linux/AppParamParserLinux.cpp
@@ -22,6 +22,7 @@ namespace
std::vector<std::string> availableWindowSystems = CCompileInfo::GetAvailableWindowSystems();
std::array<std::string, 1> availableLogTargets = {"console"};
std::vector<std::string> availableAudioBackends = CCompileInfo::GetAvailableAudioBackends();
+std::vector<std::string> availableGlInterfaces = CCompileInfo::GetAvailableGlInterfaces();
constexpr const char* windowingText =
R"""(
@@ -41,6 +42,12 @@ Selected audio backend not available: {}
Available audio backends: {}
)""";
+constexpr const char* glInterfaceText =
+ R"""(
+Selected GL interface not available: {}
+ Available GL interfaces: {}
+)""";
+
constexpr const char* helpText =
R"""(
Linux Specific Arguments:
@@ -50,6 +57,8 @@ Linux Specific Arguments:
Available log targets are: {}
--audio-backend=<backend> Select which audio backend to use.
Available audio backends are: {}
+ --gl-interface=<interface> Select which GL interface to use (X11 only).
+ Available GL interfaces are: {}
)""";
} // namespace
@@ -107,6 +116,23 @@ void CAppParamParserLinux::ParseArg(const std::string& arg)
exit(0);
}
}
+ else if (arg.find("--gl-interface=") != std::string::npos)
+ {
+ const auto argValue = arg.substr(15);
+ const auto it =
+ std::find(availableGlInterfaces.cbegin(), availableGlInterfaces.cend(), argValue);
+ if (it != availableGlInterfaces.cend())
+ {
+ GetAppParams()->SetGlInterface(argValue);
+ }
+ else
+ {
+ std::cout << StringUtils::Format(glInterfaceText, argValue,
+ StringUtils::Join(availableGlInterfaces, ", "));
+
+ exit(0);
+ }
+ }
}
void CAppParamParserLinux::DisplayHelp()
@@ -115,5 +141,6 @@ void CAppParamParserLinux::DisplayHelp()
std::cout << StringUtils::Format(helpText, StringUtils::Join(availableWindowSystems, ", "),
StringUtils::Join(availableLogTargets, ", "),
- StringUtils::Join(availableAudioBackends, ", "));
+ StringUtils::Join(availableAudioBackends, ", "),
+ StringUtils::Join(availableGlInterfaces, ", "));
}
diff --git a/xbmc/platform/linux/CPUInfoLinux.cpp b/xbmc/platform/linux/CPUInfoLinux.cpp
index 728480ca25..a66f6ff7dd 100644
--- a/xbmc/platform/linux/CPUInfoLinux.cpp
+++ b/xbmc/platform/linux/CPUInfoLinux.cpp
@@ -279,17 +279,11 @@ CCPUInfoLinux::CCPUInfoLinux()
m_cpuModel = m_cpuModel.substr(0, m_cpuModel.find(char(0))); // remove extra null terminations
- // We exclude webOS here due to a bug in the toolchain leading to a SEGFAULT on webOS 6+ devices.
-#if defined(HAS_NEON) && defined(__arm__) && !defined(TARGET_WEBOS)
+#if defined(HAS_NEON) && defined(__arm__)
if (getauxval(AT_HWCAP) & HWCAP_NEON)
m_cpuFeatures |= CPU_FEATURE_NEON;
#endif
-#ifdef TARGET_WEBOS
- // We can assume that any webOS TV has NEON support
- m_cpuFeatures |= CPU_FEATURE_NEON;
-#endif
-
#if defined(HAS_NEON) && defined(__aarch64__)
if (getauxval(AT_HWCAP) & HWCAP_ASIMD)
m_cpuFeatures |= CPU_FEATURE_NEON;
diff --git a/xbmc/platform/linux/PlatformLinux.cpp b/xbmc/platform/linux/PlatformLinux.cpp
index a99ceefdfb..5189f09d8f 100644
--- a/xbmc/platform/linux/PlatformLinux.cpp
+++ b/xbmc/platform/linux/PlatformLinux.cpp
@@ -67,12 +67,12 @@ bool CPlatformLinux::InitStageOne()
#if defined(TARGET_WEBOS)
// WebOS ipks run in a chroot like environment. $HOME is set to the ipk dir and $LD_LIBRARY_PATH is lib
- auto HOME = std::string(getenv("HOME"));
+ const auto HOME = std::string(getenv("HOME"));
setenv("XDG_RUNTIME_DIR", "/tmp/xdg", 1);
setenv("XKB_CONFIG_ROOT", "/usr/share/X11/xkb", 1);
setenv("WAYLAND_DISPLAY", "wayland-0", 1);
- setenv("PYTHONHOME", HOME.append("/lib/python3").c_str(), 1);
- setenv("PYTHONPATH", HOME.append("/lib/python3").c_str(), 1);
+ setenv("PYTHONHOME", (HOME + "/lib/python3").c_str(), 1);
+ setenv("PYTHONPATH", (HOME + "/lib/python3").c_str(), 1);
setenv("PYTHONIOENCODING", "UTF-8", 1);
setenv("KODI_HOME", HOME.c_str(), 1);
setenv("SSL_CERT_FILE",
diff --git a/xbmc/platform/linux/input/LibInputHandler.cpp b/xbmc/platform/linux/input/LibInputHandler.cpp
index a8c39f5b91..bce9f78dc1 100644
--- a/xbmc/platform/linux/input/LibInputHandler.cpp
+++ b/xbmc/platform/linux/input/LibInputHandler.cpp
@@ -17,6 +17,7 @@
#include "utils/log.h"
#include <algorithm>
+#include <memory>
#include <string.h>
#include <fcntl.h>
@@ -93,10 +94,10 @@ CLibInputHandler::CLibInputHandler() : CThread("libinput")
m_liFd = libinput_get_fd(m_li);
- m_keyboard.reset(new CLibInputKeyboard());
- m_pointer.reset(new CLibInputPointer());
- m_touch.reset(new CLibInputTouch());
- m_settings.reset(new CLibInputSettings(this));
+ m_keyboard = std::make_unique<CLibInputKeyboard>();
+ m_pointer = std::make_unique<CLibInputPointer>();
+ m_touch = std::make_unique<CLibInputTouch>();
+ m_settings = std::make_unique<CLibInputSettings>(this);
CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
}
diff --git a/xbmc/platform/linux/input/LibInputKeyboard.cpp b/xbmc/platform/linux/input/LibInputKeyboard.cpp
index 657588adce..952528d6b6 100644
--- a/xbmc/platform/linux/input/LibInputKeyboard.cpp
+++ b/xbmc/platform/linux/input/LibInputKeyboard.cpp
@@ -8,15 +8,18 @@
#include "LibInputKeyboard.h"
+#include "LangInfo.h"
#include "LibInputSettings.h"
#include "ServiceBroker.h"
#include "application/AppInboundProtocol.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
+#include "utils/Map.h"
+#include "utils/StringUtils.h"
#include "utils/log.h"
#include <algorithm>
-#include <map>
+#include <optional>
#include <string.h>
#include <fcntl.h>
@@ -28,159 +31,265 @@
namespace
{
-constexpr int REPEAT_DELAY = 400;
-constexpr int REPEAT_RATE = 80;
+constexpr unsigned int REPEAT_DELAY = 400;
+constexpr unsigned int REPEAT_RATE = 80;
+
+constexpr auto xkbMap = make_map<xkb_keysym_t, XBMCKey>({
+ // Function keys before start of ASCII printable character range
+ {XKB_KEY_BackSpace, XBMCK_BACKSPACE},
+ {XKB_KEY_Tab, XBMCK_TAB},
+ {XKB_KEY_Clear, XBMCK_CLEAR},
+ {XKB_KEY_Return, XBMCK_RETURN},
+ {XKB_KEY_Pause, XBMCK_PAUSE},
+ {XKB_KEY_Escape, XBMCK_ESCAPE},
+
+ // ASCII printable range - not included here
+
+ // Function keys after end of ASCII printable character range
+ {XKB_KEY_Delete, XBMCK_DELETE},
+
+ // Multimedia keys
+ {XKB_KEY_XF86Back, XBMCK_BROWSER_BACK},
+ {XKB_KEY_XF86Forward, XBMCK_BROWSER_FORWARD},
+ {XKB_KEY_XF86Refresh, XBMCK_BROWSER_REFRESH},
+ {XKB_KEY_XF86Stop, XBMCK_BROWSER_STOP},
+ {XKB_KEY_XF86Search, XBMCK_BROWSER_SEARCH},
+ // XKB_KEY_XF86Favorites could be XBMCK_BROWSER_FAVORITES or XBMCK_FAVORITES,
+ // XBMCK_FAVORITES was chosen here because it is more general
+ {XKB_KEY_XF86HomePage, XBMCK_BROWSER_HOME},
+ {XKB_KEY_XF86AudioMute, XBMCK_VOLUME_MUTE},
+ {XKB_KEY_XF86AudioLowerVolume, XBMCK_VOLUME_DOWN},
+ {XKB_KEY_XF86AudioRaiseVolume, XBMCK_VOLUME_UP},
+ {XKB_KEY_XF86AudioNext, XBMCK_MEDIA_NEXT_TRACK},
+ {XKB_KEY_XF86AudioPrev, XBMCK_MEDIA_PREV_TRACK},
+ {XKB_KEY_XF86AudioStop, XBMCK_MEDIA_STOP},
+ {XKB_KEY_XF86AudioPause, XBMCK_MEDIA_PLAY_PAUSE},
+ {XKB_KEY_XF86Mail, XBMCK_LAUNCH_MAIL},
+ {XKB_KEY_XF86Select, XBMCK_LAUNCH_MEDIA_SELECT},
+ {XKB_KEY_XF86Launch0, XBMCK_LAUNCH_APP1},
+ {XKB_KEY_XF86Launch1, XBMCK_LAUNCH_APP2},
+ {XKB_KEY_XF86WWW, XBMCK_LAUNCH_FILE_BROWSER},
+ {XKB_KEY_XF86AudioMedia, XBMCK_LAUNCH_MEDIA_CENTER},
+ {XKB_KEY_XF86AudioRewind, XBMCK_MEDIA_REWIND},
+ {XKB_KEY_XF86AudioForward, XBMCK_MEDIA_FASTFORWARD},
+
+ // Numeric keypad
+ {XKB_KEY_KP_0, XBMCK_KP0},
+ {XKB_KEY_KP_1, XBMCK_KP1},
+ {XKB_KEY_KP_2, XBMCK_KP2},
+ {XKB_KEY_KP_3, XBMCK_KP3},
+ {XKB_KEY_KP_4, XBMCK_KP4},
+ {XKB_KEY_KP_5, XBMCK_KP5},
+ {XKB_KEY_KP_6, XBMCK_KP6},
+ {XKB_KEY_KP_7, XBMCK_KP7},
+ {XKB_KEY_KP_8, XBMCK_KP8},
+ {XKB_KEY_KP_9, XBMCK_KP9},
+ {XKB_KEY_KP_Decimal, XBMCK_KP_PERIOD},
+ {XKB_KEY_KP_Divide, XBMCK_KP_DIVIDE},
+ {XKB_KEY_KP_Multiply, XBMCK_KP_MULTIPLY},
+ {XKB_KEY_KP_Subtract, XBMCK_KP_MINUS},
+ {XKB_KEY_KP_Add, XBMCK_KP_PLUS},
+ {XKB_KEY_KP_Enter, XBMCK_KP_ENTER},
+ {XKB_KEY_KP_Equal, XBMCK_KP_EQUALS},
+
+ // Arrows + Home/End pad
+ {XKB_KEY_Up, XBMCK_UP},
+ {XKB_KEY_Down, XBMCK_DOWN},
+ {XKB_KEY_Right, XBMCK_RIGHT},
+ {XKB_KEY_Left, XBMCK_LEFT},
+ {XKB_KEY_Insert, XBMCK_INSERT},
+ {XKB_KEY_Home, XBMCK_HOME},
+ {XKB_KEY_End, XBMCK_END},
+ {XKB_KEY_Page_Up, XBMCK_PAGEUP},
+ {XKB_KEY_Page_Down, XBMCK_PAGEDOWN},
+
+ // Key state modifier keys
+ {XKB_KEY_Num_Lock, XBMCK_NUMLOCK},
+ {XKB_KEY_Caps_Lock, XBMCK_CAPSLOCK},
+ {XKB_KEY_Scroll_Lock, XBMCK_SCROLLOCK},
+ {XKB_KEY_Shift_R, XBMCK_RSHIFT},
+ {XKB_KEY_Shift_L, XBMCK_LSHIFT},
+ {XKB_KEY_Control_R, XBMCK_RCTRL},
+ {XKB_KEY_Control_L, XBMCK_LCTRL},
+ {XKB_KEY_Alt_R, XBMCK_RALT},
+ {XKB_KEY_Alt_L, XBMCK_LALT},
+ {XKB_KEY_Meta_R, XBMCK_RMETA},
+ {XKB_KEY_Meta_L, XBMCK_LMETA},
+ {XKB_KEY_Super_R, XBMCK_RSUPER},
+ {XKB_KEY_Super_L, XBMCK_LSUPER},
+ // XKB does not have XBMCK_MODE/"Alt Gr" - probably equal to XKB_KEY_Alt_R
+ {XKB_KEY_Multi_key, XBMCK_COMPOSE},
+
+ // Miscellaneous function keys
+ {XKB_KEY_Help, XBMCK_HELP},
+ {XKB_KEY_Print, XBMCK_PRINT},
+ {XKB_KEY_Sys_Req, XBMCK_SYSREQ},
+ {XKB_KEY_Break, XBMCK_BREAK},
+ {XKB_KEY_Menu, XBMCK_MENU},
+ {XKB_KEY_XF86PowerOff, XBMCK_POWER},
+ {XKB_KEY_EcuSign, XBMCK_EURO},
+ {XKB_KEY_Undo, XBMCK_UNDO},
+ {XKB_KEY_XF86Sleep, XBMCK_SLEEP},
+ // Unmapped: XBMCK_GUIDE, XBMCK_SETTINGS, XBMCK_INFO
+ {XKB_KEY_XF86Red, XBMCK_RED},
+ {XKB_KEY_XF86Green, XBMCK_GREEN},
+ {XKB_KEY_XF86Yellow, XBMCK_YELLOW},
+ {XKB_KEY_XF86Blue, XBMCK_BLUE},
+ // Unmapped: XBMCK_ZOOM, XBMCK_TEXT
+ {XKB_KEY_XF86Favorites, XBMCK_FAVORITES},
+ {XKB_KEY_XF86HomePage, XBMCK_HOMEPAGE},
+ // Unmapped: XBMCK_CONFIG, XBMCK_EPG
+
+ // Media keys
+ {XKB_KEY_XF86Eject, XBMCK_EJECT},
+ {XKB_KEY_Cancel, XBMCK_STOP},
+ {XKB_KEY_XF86AudioRecord, XBMCK_RECORD},
+ // XBMCK_REWIND clashes with XBMCK_MEDIA_REWIND
+ {XKB_KEY_XF86Phone, XBMCK_PHONE},
+ {XKB_KEY_XF86AudioPlay, XBMCK_PLAY},
+ {XKB_KEY_XF86AudioRandomPlay, XBMCK_SHUFFLE}
+ // XBMCK_FASTFORWARD clashes with XBMCK_MEDIA_FASTFORWARD
+});
+
+constexpr auto logLevelMap = make_map<xkb_log_level, int>({{XKB_LOG_LEVEL_CRITICAL, LOGERROR},
+ {XKB_LOG_LEVEL_ERROR, LOGERROR},
+ {XKB_LOG_LEVEL_WARNING, LOGWARNING},
+ {XKB_LOG_LEVEL_INFO, LOGINFO},
+ {XKB_LOG_LEVEL_DEBUG, LOGDEBUG}});
+
+constexpr auto XkbDeadKeyXBMCMapping =
+ make_map<xkb_keycode_t, XBMCKey>({{XKB_KEY_dead_grave, XBMCK_GRAVE},
+ {XKB_KEY_dead_tilde, XBMCK_TILDE},
+ {XKB_KEY_dead_acute, XBMCK_ACUTE},
+ {XKB_KEY_dead_circumflex, XBMCK_CIRCUMFLEX},
+ {XKB_KEY_dead_perispomeni, XBMCK_PERISPOMENI},
+ {XKB_KEY_dead_macron, XBMCK_MACRON},
+ {XKB_KEY_dead_breve, XBMCK_BREVE},
+ {XKB_KEY_dead_abovedot, XBMCK_ABOVEDOT},
+ {XKB_KEY_dead_diaeresis, XBMCK_DIAERESIS},
+ {XKB_KEY_dead_abovering, XBMCK_ABOVERING},
+ {XKB_KEY_dead_doubleacute, XBMCK_DOUBLEACUTE},
+ {XKB_KEY_dead_caron, XBMCK_CARON},
+ {XKB_KEY_dead_cedilla, XBMCK_CEDILLA},
+ {XKB_KEY_dead_ogonek, XBMCK_OGONEK},
+ {XKB_KEY_dead_iota, XBMCK_IOTA},
+ {XKB_KEY_dead_voiced_sound, XBMCK_VOICESOUND},
+ {XKB_KEY_dead_semivoiced_sound, XBMCK_SEMIVOICESOUND},
+ {XKB_KEY_dead_belowdot, XBMCK_BELOWDOT},
+ {XKB_KEY_dead_hook, XBMCK_HOOK},
+ {XKB_KEY_dead_horn, XBMCK_HORN},
+ {XKB_KEY_dead_stroke, XBMCK_STROKE},
+ {XKB_KEY_dead_abovecomma, XBMCK_ABOVECOMMA},
+ {XKB_KEY_dead_psili, XBMCK_ABOVECOMMA},
+ {XKB_KEY_dead_abovereversedcomma, XBMCK_ABOVEREVERSEDCOMMA},
+ {XKB_KEY_dead_dasia, XBMCK_OGONEK},
+ {XKB_KEY_dead_doublegrave, XBMCK_DOUBLEGRAVE},
+ {XKB_KEY_dead_belowring, XBMCK_BELOWRING},
+ {XKB_KEY_dead_belowmacron, XBMCK_BELOWMACRON},
+ {XKB_KEY_dead_belowcircumflex, XBMCK_BELOWCIRCUMFLEX},
+ {XKB_KEY_dead_belowtilde, XBMCK_BELOWTILDE},
+ {XKB_KEY_dead_belowbreve, XBMCK_BELOWBREVE},
+ {XKB_KEY_dead_belowdiaeresis, XBMCK_BELOWDIAERESIS},
+ {XKB_KEY_dead_invertedbreve, XBMCK_INVERTEDBREVE},
+ {XKB_KEY_dead_belowcomma, XBMCK_BELOWCOMMA},
+ {XKB_KEY_dead_a, XBMCK_DEAD_A},
+ {XKB_KEY_dead_A, XBMCK_DEAD_A},
+ {XKB_KEY_dead_e, XBMCK_DEAD_E},
+ {XKB_KEY_dead_E, XBMCK_DEAD_E},
+ {XKB_KEY_dead_i, XBMCK_DEAD_I},
+ {XKB_KEY_dead_I, XBMCK_DEAD_I},
+ {XKB_KEY_dead_o, XBMCK_DEAD_O},
+ {XKB_KEY_dead_O, XBMCK_DEAD_O},
+ {XKB_KEY_dead_u, XBMCK_DEAD_U},
+ {XKB_KEY_dead_U, XBMCK_DEAD_U},
+ {XKB_KEY_dead_small_schwa, XBMCK_SCHWA},
+ {XKB_KEY_dead_capital_schwa, XBMCK_SCHWA}});
+
+std::optional<XBMCKey> TranslateDeadKey(uint32_t keySym)
+{
+ auto mapping = XkbDeadKeyXBMCMapping.find(keySym);
+ return mapping != XkbDeadKeyXBMCMapping.cend() ? std::optional<XBMCKey>(mapping->second)
+ : std::nullopt;
+}
-static const std::map<xkb_keysym_t, XBMCKey> xkbMap =
+static void xkbLogger(xkb_context* context,
+ xkb_log_level priority,
+ const char* format,
+ va_list args)
{
- // Function keys before start of ASCII printable character range
- { XKB_KEY_BackSpace, XBMCK_BACKSPACE },
- { XKB_KEY_Tab, XBMCK_TAB },
- { XKB_KEY_Clear, XBMCK_CLEAR },
- { XKB_KEY_Return, XBMCK_RETURN },
- { XKB_KEY_Pause, XBMCK_PAUSE },
- { XKB_KEY_Escape, XBMCK_ESCAPE },
-
- // ASCII printable range - not included here
-
- // Function keys after end of ASCII printable character range
- { XKB_KEY_Delete, XBMCK_DELETE },
-
- // Multimedia keys
- { XKB_KEY_XF86Back, XBMCK_BROWSER_BACK },
- { XKB_KEY_XF86Forward, XBMCK_BROWSER_FORWARD },
- { XKB_KEY_XF86Refresh, XBMCK_BROWSER_REFRESH },
- { XKB_KEY_XF86Stop, XBMCK_BROWSER_STOP },
- { XKB_KEY_XF86Search, XBMCK_BROWSER_SEARCH },
- // XKB_KEY_XF86Favorites could be XBMCK_BROWSER_FAVORITES or XBMCK_FAVORITES,
- // XBMCK_FAVORITES was chosen here because it is more general
- { XKB_KEY_XF86HomePage, XBMCK_BROWSER_HOME },
- { XKB_KEY_XF86AudioMute, XBMCK_VOLUME_MUTE },
- { XKB_KEY_XF86AudioLowerVolume, XBMCK_VOLUME_DOWN },
- { XKB_KEY_XF86AudioRaiseVolume, XBMCK_VOLUME_UP },
- { XKB_KEY_XF86AudioNext, XBMCK_MEDIA_NEXT_TRACK },
- { XKB_KEY_XF86AudioPrev, XBMCK_MEDIA_PREV_TRACK },
- { XKB_KEY_XF86AudioStop, XBMCK_MEDIA_STOP },
- { XKB_KEY_XF86AudioPause, XBMCK_MEDIA_PLAY_PAUSE },
- { XKB_KEY_XF86Mail, XBMCK_LAUNCH_MAIL },
- { XKB_KEY_XF86Select, XBMCK_LAUNCH_MEDIA_SELECT },
- { XKB_KEY_XF86Launch0, XBMCK_LAUNCH_APP1 },
- { XKB_KEY_XF86Launch1, XBMCK_LAUNCH_APP2 },
- { XKB_KEY_XF86WWW, XBMCK_LAUNCH_FILE_BROWSER },
- { XKB_KEY_XF86AudioMedia, XBMCK_LAUNCH_MEDIA_CENTER },
- { XKB_KEY_XF86AudioRewind, XBMCK_MEDIA_REWIND },
- { XKB_KEY_XF86AudioForward, XBMCK_MEDIA_FASTFORWARD },
-
- // Numeric keypad
- { XKB_KEY_KP_0, XBMCK_KP0 },
- { XKB_KEY_KP_1, XBMCK_KP1 },
- { XKB_KEY_KP_2, XBMCK_KP2 },
- { XKB_KEY_KP_3, XBMCK_KP3 },
- { XKB_KEY_KP_4, XBMCK_KP4 },
- { XKB_KEY_KP_5, XBMCK_KP5 },
- { XKB_KEY_KP_6, XBMCK_KP6 },
- { XKB_KEY_KP_7, XBMCK_KP7 },
- { XKB_KEY_KP_8, XBMCK_KP8 },
- { XKB_KEY_KP_9, XBMCK_KP9 },
- { XKB_KEY_KP_Decimal, XBMCK_KP_PERIOD },
- { XKB_KEY_KP_Divide, XBMCK_KP_DIVIDE },
- { XKB_KEY_KP_Multiply, XBMCK_KP_MULTIPLY },
- { XKB_KEY_KP_Subtract, XBMCK_KP_MINUS },
- { XKB_KEY_KP_Add, XBMCK_KP_PLUS },
- { XKB_KEY_KP_Enter, XBMCK_KP_ENTER },
- { XKB_KEY_KP_Equal, XBMCK_KP_EQUALS },
-
- // Arrows + Home/End pad
- { XKB_KEY_Up, XBMCK_UP },
- { XKB_KEY_Down, XBMCK_DOWN },
- { XKB_KEY_Right, XBMCK_RIGHT },
- { XKB_KEY_Left, XBMCK_LEFT },
- { XKB_KEY_Insert, XBMCK_INSERT },
- { XKB_KEY_Home, XBMCK_HOME },
- { XKB_KEY_End, XBMCK_END },
- { XKB_KEY_Page_Up, XBMCK_PAGEUP },
- { XKB_KEY_Page_Down, XBMCK_PAGEDOWN },
-
- // Key state modifier keys
- { XKB_KEY_Num_Lock, XBMCK_NUMLOCK },
- { XKB_KEY_Caps_Lock, XBMCK_CAPSLOCK },
- { XKB_KEY_Scroll_Lock, XBMCK_SCROLLOCK },
- { XKB_KEY_Shift_R, XBMCK_RSHIFT },
- { XKB_KEY_Shift_L, XBMCK_LSHIFT },
- { XKB_KEY_Control_R, XBMCK_RCTRL },
- { XKB_KEY_Control_L, XBMCK_LCTRL },
- { XKB_KEY_Alt_R, XBMCK_RALT },
- { XKB_KEY_Alt_L, XBMCK_LALT },
- { XKB_KEY_Meta_R, XBMCK_RMETA },
- { XKB_KEY_Meta_L, XBMCK_LMETA },
- { XKB_KEY_Super_R, XBMCK_RSUPER },
- { XKB_KEY_Super_L, XBMCK_LSUPER },
- // XKB does not have XBMCK_MODE/"Alt Gr" - probably equal to XKB_KEY_Alt_R
- { XKB_KEY_Multi_key, XBMCK_COMPOSE },
-
- // Miscellaneous function keys
- { XKB_KEY_Help, XBMCK_HELP },
- { XKB_KEY_Print, XBMCK_PRINT },
- { XKB_KEY_Sys_Req, XBMCK_SYSREQ},
- { XKB_KEY_Break, XBMCK_BREAK },
- { XKB_KEY_Menu, XBMCK_MENU },
- { XKB_KEY_XF86PowerOff, XBMCK_POWER },
- { XKB_KEY_EcuSign, XBMCK_EURO },
- { XKB_KEY_Undo, XBMCK_UNDO },
- { XKB_KEY_XF86Sleep, XBMCK_SLEEP },
- // Unmapped: XBMCK_GUIDE, XBMCK_SETTINGS, XBMCK_INFO
- { XKB_KEY_XF86Red, XBMCK_RED },
- { XKB_KEY_XF86Green, XBMCK_GREEN },
- { XKB_KEY_XF86Yellow, XBMCK_YELLOW },
- { XKB_KEY_XF86Blue, XBMCK_BLUE },
- // Unmapped: XBMCK_ZOOM, XBMCK_TEXT
- { XKB_KEY_XF86Favorites, XBMCK_FAVORITES },
- { XKB_KEY_XF86HomePage, XBMCK_HOMEPAGE },
- // Unmapped: XBMCK_CONFIG, XBMCK_EPG
-
- // Media keys
- { XKB_KEY_XF86Eject, XBMCK_EJECT },
- { XKB_KEY_Cancel, XBMCK_STOP },
- { XKB_KEY_XF86AudioRecord, XBMCK_RECORD },
- // XBMCK_REWIND clashes with XBMCK_MEDIA_REWIND
- { XKB_KEY_XF86Phone, XBMCK_PHONE },
- { XKB_KEY_XF86AudioPlay, XBMCK_PLAY },
- { XKB_KEY_XF86AudioRandomPlay, XBMCK_SHUFFLE }
- // XBMCK_FASTFORWARD clashes with XBMCK_MEDIA_FASTFORWARD
-};
+ const std::string message = StringUtils::FormatV(format, args);
+ auto logLevel = logLevelMap.find(priority);
+ CLog::Log(logLevel != logLevelMap.cend() ? logLevel->second : LOGDEBUG, "[xkb] {}", message);
+}
} // namespace
CLibInputKeyboard::CLibInputKeyboard()
: m_repeatTimer(std::bind(&CLibInputKeyboard::KeyRepeatTimeout, this))
{
- m_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ m_ctx = std::unique_ptr<xkb_context, XkbContextDeleter>{xkb_context_new(XKB_CONTEXT_NO_FLAGS)};
if (!m_ctx)
{
CLog::Log(LOGERROR, "CLibInputKeyboard::{} - failed to create xkb context", __FUNCTION__);
return;
}
- std::string layout = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CLibInputSettings::SETTING_INPUT_LIBINPUTKEYBOARDLAYOUT);
+ xkb_context_set_log_level(m_ctx.get(), XKB_LOG_LEVEL_DEBUG);
+ xkb_context_set_log_fn(m_ctx.get(), &xkbLogger);
+ std::string layout = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CLibInputSettings::SETTING_INPUT_LIBINPUTKEYBOARDLAYOUT);
if (!SetKeymap(layout))
{
CLog::Log(LOGERROR, "CLibInputKeyboard::{} - failed set default keymap", __FUNCTION__);
return;
}
+
+ m_composeTable =
+ std::unique_ptr<xkb_compose_table, XkbComposeTableDeleter>{xkb_compose_table_new_from_locale(
+ m_ctx.get(), g_langInfo.GetSystemLocale().name().c_str(), XKB_COMPOSE_COMPILE_NO_FLAGS)};
+ if (!m_composeTable)
+ {
+ CLog::LogF(LOGWARNING,
+ "Failed to compile localized compose table, composed key support will be disabled");
+ return;
+ }
+
+ m_composedState = std::unique_ptr<xkb_compose_state, XkbComposeStateDeleter>{
+ xkb_compose_state_new(m_composeTable.get(), XKB_COMPOSE_STATE_NO_FLAGS)};
+
+ if (!m_composedState)
+ {
+ throw std::runtime_error("Failed to create keyboard composer");
+ }
}
-CLibInputKeyboard::~CLibInputKeyboard()
+void CLibInputKeyboard::XkbContextDeleter::operator()(xkb_context* ctx) const
{
- xkb_state_unref(m_state);
- xkb_keymap_unref(m_keymap);
- xkb_context_unref(m_ctx);
+ xkb_context_unref(ctx);
}
-bool CLibInputKeyboard::SetKeymap(const std::string& layout)
+void CLibInputKeyboard::XkbKeymapDeleter::operator()(xkb_keymap* keymap) const
+{
+ xkb_keymap_unref(keymap);
+}
+
+void CLibInputKeyboard::XkbStateDeleter::operator()(xkb_state* state) const
{
- xkb_state_unref(m_state);
- xkb_keymap_unref(m_keymap);
+ xkb_state_unref(state);
+}
+void CLibInputKeyboard::XkbComposeTableDeleter::operator()(xkb_compose_table* table) const
+{
+ xkb_compose_table_unref(table);
+}
+
+void CLibInputKeyboard::XkbComposeStateDeleter::operator()(xkb_compose_state* state) const
+{
+ xkb_compose_state_unref(state);
+}
+
+bool CLibInputKeyboard::SetKeymap(const std::string& layout)
+{
xkb_rule_names names;
names.rules = nullptr;
@@ -189,28 +298,29 @@ bool CLibInputKeyboard::SetKeymap(const std::string& layout)
names.variant = nullptr;
names.options = nullptr;
- m_keymap = xkb_keymap_new_from_names(m_ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ m_keymap = std::unique_ptr<xkb_keymap, XkbKeymapDeleter>{
+ xkb_keymap_new_from_names(m_ctx.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS)};
if (!m_keymap)
{
CLog::Log(LOGERROR, "CLibInputKeyboard::{} - failed to compile keymap", __FUNCTION__);
return false;
}
- m_state = xkb_state_new(m_keymap);
+ m_state = std::unique_ptr<xkb_state, XkbStateDeleter>{xkb_state_new(m_keymap.get())};
if (!m_state)
{
CLog::Log(LOGERROR, "CLibInputKeyboard::{} - failed to create xkb state", __FUNCTION__);
return false;
}
- m_modindex[0] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
- m_modindex[1] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
- m_modindex[2] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
- m_modindex[3] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
+ m_modindex[0] = xkb_keymap_mod_get_index(m_keymap.get(), XKB_MOD_NAME_CTRL);
+ m_modindex[1] = xkb_keymap_mod_get_index(m_keymap.get(), XKB_MOD_NAME_ALT);
+ m_modindex[2] = xkb_keymap_mod_get_index(m_keymap.get(), XKB_MOD_NAME_SHIFT);
+ m_modindex[3] = xkb_keymap_mod_get_index(m_keymap.get(), XKB_MOD_NAME_LOGO);
- m_ledindex[0] = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM);
- m_ledindex[1] = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS);
- m_ledindex[2] = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_SCROLL);
+ m_ledindex[0] = xkb_keymap_led_get_index(m_keymap.get(), XKB_LED_NAME_NUM);
+ m_ledindex[1] = xkb_keymap_led_get_index(m_keymap.get(), XKB_LED_NAME_CAPS);
+ m_ledindex[2] = xkb_keymap_led_get_index(m_keymap.get(), XKB_LED_NAME_SCROLL);
m_leds = 0;
@@ -222,55 +332,101 @@ void CLibInputKeyboard::ProcessKey(libinput_event_keyboard *e)
if (!m_ctx || !m_keymap || !m_state)
return;
- XBMC_Event event = {};
-
const uint32_t xkbkey = libinput_event_keyboard_get_key(e) + 8;
+ const xkb_keysym_t keysym = xkb_state_key_get_one_sym(m_state.get(), xkbkey);
const bool pressed = libinput_event_keyboard_get_key_state(e) == LIBINPUT_KEY_STATE_PRESSED;
+ xkb_state_update_key(m_state.get(), xkbkey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
- event.type = pressed ? XBMC_KEYDOWN : XBMC_KEYUP;
- xkb_state_update_key(m_state, xkbkey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
-
- const xkb_keysym_t keysym = xkb_state_key_get_one_sym(m_state, xkbkey);
+ bool flushComposer{false};
+ if (pressed && SupportsKeyComposition())
+ {
+ xkb_compose_state_feed(m_composedState.get(), keysym);
+ const xkb_compose_status composeStatus = xkb_compose_state_get_status(m_composedState.get());
+ switch (composeStatus)
+ {
+ case XKB_COMPOSE_COMPOSING:
+ {
+ const std::optional<XBMCKey> xbmcKey = TranslateDeadKey(keysym);
+ if (xbmcKey)
+ {
+ NotifyKeyComposingEvent(XBMC_KEYCOMPOSING_COMPOSING, xbmcKey.value());
+ }
+ return;
+ }
+ case XKB_COMPOSE_COMPOSED:
+ {
+ flushComposer = true;
+ NotifyKeyComposingEvent(XBMC_KEYCOMPOSING_FINISHED, XBMCK_UNKNOWN);
+ break;
+ }
+ case XKB_COMPOSE_CANCELLED:
+ {
+ const std::uint32_t unicodeCodePointCancellationKey{UnicodeCodepointForKeycode(xkbkey)};
+ NotifyKeyComposingEvent(XBMC_KEYCOMPOSING_CANCELLED, unicodeCodePointCancellationKey);
+ xkb_compose_state_reset(m_composedState.get());
+ // do not allow key fallthrough if we are simply cancelling with a backspace (we are cancelling the
+ // composition behavior not really removing any character)
+ if (unicodeCodePointCancellationKey == XBMCK_BACKSPACE)
+ {
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
int mod = XBMCKMOD_NONE;
xkb_state_component modtype = xkb_state_component(XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
- if (xkb_state_mod_index_is_active(m_state, m_modindex[0], modtype) && ((keysym != XBMCK_LCTRL) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[0], modtype) &&
+ ((keysym != XBMCK_LCTRL) || !pressed))
mod |= XBMCKMOD_CTRL;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[0], modtype) && ((keysym != XBMCK_RCTRL) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[0], modtype) &&
+ ((keysym != XBMCK_RCTRL) || !pressed))
mod |= XBMCKMOD_CTRL;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[1], modtype) && ((keysym != XBMCK_LALT) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[1], modtype) &&
+ ((keysym != XBMCK_LALT) || !pressed))
mod |= XBMCKMOD_ALT;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[1], modtype) && ((keysym != XBMCK_RALT) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[1], modtype) &&
+ ((keysym != XBMCK_RALT) || !pressed))
mod |= XBMCKMOD_ALT;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[2], modtype) && ((keysym != XBMCK_LSHIFT) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[2], modtype) &&
+ ((keysym != XBMCK_LSHIFT) || !pressed))
mod |= XBMCKMOD_SHIFT;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[2], modtype) && ((keysym != XBMCK_RSHIFT) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[2], modtype) &&
+ ((keysym != XBMCK_RSHIFT) || !pressed))
mod |= XBMCKMOD_SHIFT;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[3], modtype) && ((keysym != XBMCK_LMETA) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[3], modtype) &&
+ ((keysym != XBMCK_LMETA) || !pressed))
mod |= XBMCKMOD_META;
- if (xkb_state_mod_index_is_active(m_state, m_modindex[3], modtype) && ((keysym != XBMCK_RMETA) || !pressed))
+ if (xkb_state_mod_index_is_active(m_state.get(), m_modindex[3], modtype) &&
+ ((keysym != XBMCK_RMETA) || !pressed))
mod |= XBMCKMOD_META;
m_leds = 0;
- if (xkb_state_led_index_is_active(m_state, m_ledindex[0]) && ((keysym != XBMCK_NUMLOCK) || !pressed))
+ if (xkb_state_led_index_is_active(m_state.get(), m_ledindex[0]) &&
+ ((keysym != XBMCK_NUMLOCK) || !pressed))
{
m_leds |= LIBINPUT_LED_NUM_LOCK;
mod |= XBMCKMOD_NUM;
}
- if (xkb_state_led_index_is_active(m_state, m_ledindex[1]) && ((keysym != XBMCK_CAPSLOCK) || !pressed))
+ if (xkb_state_led_index_is_active(m_state.get(), m_ledindex[1]) &&
+ ((keysym != XBMCK_CAPSLOCK) || !pressed))
{
m_leds |= LIBINPUT_LED_CAPS_LOCK;
mod |= XBMCKMOD_CAPS;
}
- if (xkb_state_led_index_is_active(m_state, m_ledindex[2]) && ((keysym != XBMCK_SCROLLOCK) || !pressed))
+ if (xkb_state_led_index_is_active(m_state.get(), m_ledindex[2]) &&
+ ((keysym != XBMCK_SCROLLOCK) || !pressed))
{
m_leds |= LIBINPUT_LED_SCROLL_LOCK;
//mod |= XBMCK_SCROLLOCK;
}
- uint32_t unicode = xkb_state_key_get_utf32(m_state, xkbkey);
+ uint32_t unicode = UnicodeCodepointForKeycode(xkbkey);
if (unicode > std::numeric_limits<std::uint16_t>::max())
{
// Kodi event system only supports UTF16, so ignore the codepoint if it does not fit
@@ -284,6 +440,14 @@ void CLibInputKeyboard::ProcessKey(libinput_event_keyboard *e)
scancode = 0;
}
+ // flush composer if set (after a finished sequence)
+ if (flushComposer)
+ {
+ xkb_compose_state_reset(m_composedState.get());
+ }
+
+ XBMC_Event event = {};
+ event.type = pressed ? XBMC_KEYDOWN : XBMC_KEYUP;
event.key.keysym.mod = XBMCMod(mod);
event.key.keysym.sym = XBMCKeyForKeysym(keysym, scancode);
event.key.keysym.scancode = scancode;
@@ -293,7 +457,7 @@ void CLibInputKeyboard::ProcessKey(libinput_event_keyboard *e)
if (appPort)
appPort->OnEvent(event);
- if (pressed && xkb_keymap_key_repeats(m_keymap, xkbkey))
+ if (pressed && xkb_keymap_key_repeats(m_keymap.get(), xkbkey))
{
libinput_event *ev = libinput_event_keyboard_get_base_event(e);
libinput_device *dev = libinput_event_get_device(ev);
@@ -331,7 +495,7 @@ XBMCKey CLibInputKeyboard::XBMCKeyForKeysym(xkb_keysym_t sym, uint32_t scancode)
}
auto xkbmapping = xkbMap.find(sym);
- if (xkbmapping != xkbMap.end())
+ if (xkbmapping != xkbMap.cend())
return xkbmapping->second;
return XBMCK_UNKNOWN;
@@ -385,3 +549,55 @@ void CLibInputKeyboard::GetRepeat(libinput_device *dev)
m_repeatData.insert(std::make_pair(dev, kbdrepvec));
}
}
+
+bool CLibInputKeyboard::SupportsKeyComposition() const
+{
+ return m_composedState != nullptr;
+}
+
+void CLibInputKeyboard::NotifyKeyComposingEvent(uint8_t eventType, std::uint16_t unicodeCodepoint)
+{
+ XBMC_Event event{};
+ event.type = eventType;
+ event.key.keysym.unicode = unicodeCodepoint;
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+ if (appPort)
+ {
+ appPort->OnEvent(event);
+ }
+}
+
+std::uint32_t CLibInputKeyboard::UnicodeCodepointForKeycode(xkb_keycode_t code) const
+{
+ uint32_t unicode;
+
+ if (SupportsKeyComposition())
+ {
+ const xkb_compose_status composerStatus = xkb_compose_state_get_status(m_composedState.get());
+ if (composerStatus == XKB_COMPOSE_COMPOSED)
+ {
+ const uint32_t keysym = xkb_compose_state_get_one_sym(m_composedState.get());
+ unicode = xkb_keysym_to_utf32(keysym);
+ }
+ else
+ {
+ unicode = xkb_state_key_get_utf32(m_state.get(), code);
+ }
+ }
+ else
+ {
+ unicode = xkb_state_key_get_utf32(m_state.get(), code);
+ }
+
+ // check if it is a dead key and try to translate
+ if (unicode == XBMCK_UNKNOWN)
+ {
+ const uint32_t keysym = xkb_state_key_get_one_sym(m_state.get(), code);
+ const std::optional<XBMCKey> xbmcKey = TranslateDeadKey(keysym);
+ if (xbmcKey)
+ {
+ unicode = xbmcKey.value();
+ }
+ }
+ return unicode;
+}
diff --git a/xbmc/platform/linux/input/LibInputKeyboard.h b/xbmc/platform/linux/input/LibInputKeyboard.h
index 76aaeff54f..913d9b94a2 100644
--- a/xbmc/platform/linux/input/LibInputKeyboard.h
+++ b/xbmc/platform/linux/input/LibInputKeyboard.h
@@ -12,16 +12,18 @@
#include "windowing/XBMC_events.h"
#include <map>
+#include <memory>
#include <vector>
#include <libinput.h>
+#include <xkbcommon/xkbcommon-compose.h>
#include <xkbcommon/xkbcommon.h>
class CLibInputKeyboard
{
public:
CLibInputKeyboard();
- ~CLibInputKeyboard();
+ ~CLibInputKeyboard() = default;
void ProcessKey(libinput_event_keyboard *e);
void UpdateLeds(libinput_device *dev);
@@ -32,10 +34,52 @@ public:
private:
XBMCKey XBMCKeyForKeysym(xkb_keysym_t sym, uint32_t scancode);
void KeyRepeatTimeout();
+ /**
+ * Check if the system supports key composition
+ * \return true if composition is supported, false otherwise
+ */
+ bool SupportsKeyComposition() const;
+ /**
+ * Notify the outside world about key composing events
+ *
+ * \param eventType - the key composition event type
+ * \param unicodeCodepoint - unicode codepoint of the pressed dead key
+ */
+ void NotifyKeyComposingEvent(uint8_t eventType, std::uint16_t unicodeCodepoint);
+ /**
+ * Get Unicode codepoint/UTF32 code for provided keycode
+ */
+ std::uint32_t UnicodeCodepointForKeycode(xkb_keycode_t code) const;
+ struct XkbContextDeleter
+ {
+ void operator()(xkb_context* ctx) const;
+ };
+ std::unique_ptr<xkb_context, XkbContextDeleter> m_ctx;
+
+ struct XkbKeymapDeleter
+ {
+ void operator()(xkb_keymap* keymap) const;
+ };
+ std::unique_ptr<xkb_keymap, XkbKeymapDeleter> m_keymap;
+
+ struct XkbStateDeleter
+ {
+ void operator()(xkb_state* state) const;
+ };
+ std::unique_ptr<xkb_state, XkbStateDeleter> m_state;
+
+ struct XkbComposeTableDeleter
+ {
+ void operator()(xkb_compose_table* composeTable) const;
+ };
+ std::unique_ptr<xkb_compose_table, XkbComposeTableDeleter> m_composeTable;
+
+ struct XkbComposeStateDeleter
+ {
+ void operator()(xkb_compose_state* state) const;
+ };
+ std::unique_ptr<xkb_compose_state, XkbComposeStateDeleter> m_composedState;
- xkb_context *m_ctx = nullptr;
- xkb_keymap *m_keymap = nullptr;
- xkb_state *m_state = nullptr;
xkb_mod_index_t m_modindex[4];
xkb_led_index_t m_ledindex[3];
diff --git a/xbmc/platform/linux/input/LibInputSettings.cpp b/xbmc/platform/linux/input/LibInputSettings.cpp
index ff7558bed6..a7e7855bf5 100644
--- a/xbmc/platform/linux/input/LibInputSettings.cpp
+++ b/xbmc/platform/linux/input/LibInputSettings.cpp
@@ -124,7 +124,7 @@ CLibInputSettings::CLibInputSettings(CLibInputHandler *handler) :
std::string layoutDescription = descriptionElement->GetText();
if (!layout.empty() && !layoutDescription.empty())
- layouts.emplace_back(StringSettingOption(layoutDescription, layout));
+ layouts.emplace_back(layoutDescription, layout);
layoutElement = layoutElement->NextSiblingElement();
}
diff --git a/xbmc/platform/linux/network/NetworkLinux.cpp b/xbmc/platform/linux/network/NetworkLinux.cpp
index 4e9eda0d8d..7afeb9de61 100644
--- a/xbmc/platform/linux/network/NetworkLinux.cpp
+++ b/xbmc/platform/linux/network/NetworkLinux.cpp
@@ -8,17 +8,63 @@
#include "NetworkLinux.h"
+#include "utils/FileHandle.h"
#include "utils/StringUtils.h"
#include "utils/log.h"
+#include <chrono>
#include <errno.h>
#include <utility>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_arp.h>
+#include <netinet/ip_icmp.h>
#include <resolv.h>
+#include <sys/epoll.h>
#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+using namespace KODI::UTILS::POSIX;
+
+namespace
+{
+
+constexpr unsigned int ICMP_PACKET_SIZE{64};
+constexpr unsigned int TTL{64};
+
+struct IcmpPacket
+{
+ icmphdr header;
+ uint8_t data[ICMP_PACKET_SIZE - sizeof(icmphdr)];
+
+ uint16_t Checksum()
+ {
+ auto data = reinterpret_cast<const uint16_t*>(&header);
+ unsigned int length = sizeof(header) + sizeof(data);
+
+ unsigned int sum;
+
+ for (sum = 0; length > 1; length -= 2)
+ {
+ sum += *data++;
+ }
+
+ if (length == 1)
+ {
+ sum += *data;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+
+ sum += (sum >> 16);
+
+ return ~sum;
+ }
+};
+
+} // namespace
CNetworkInterfaceLinux::CNetworkInterfaceLinux(CNetworkPosix* network,
std::string interfaceName,
@@ -199,25 +245,100 @@ std::vector<std::string> CNetworkLinux::GetNameServers()
bool CNetworkLinux::PingHost(unsigned long remote_ip, unsigned int timeout_ms)
{
- char cmd_line[64];
+ CFileHandle fd(socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_ICMP));
+ if (!fd)
+ {
+ CLog::Log(LOGERROR, "socket failed: {} ({})", strerror(errno), errno);
+ return false;
+ }
+
+ int ret = setsockopt(fd, SOL_IP, IP_TTL, &TTL, sizeof(TTL));
+ if (ret != 0)
+ {
+ CLog::Log(LOGERROR, "setsockopt failed: {} ({})", strerror(errno), errno);
+ return false;
+ }
+
+ CFileHandle epfd(epoll_create1(EPOLL_CLOEXEC));
+ if (!epfd)
+ {
+ CLog::Log(LOGERROR, "epoll_create1 failed: {} ({})", strerror(errno), errno);
+ return false;
+ }
+
+ epoll_event event;
+ event.events = EPOLLIN;
+ event.data.fd = fd;
+ ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "epoll_ctl failed: {} ({})", strerror(errno), errno);
+ return false;
+ }
+
+ IcmpPacket packet = {};
+
+ packet.header.type = ICMP_ECHO;
+ packet.header.un.echo.id = getpid();
+
+ packet.header.un.echo.sequence = 0;
+ packet.header.checksum = packet.Checksum();
- struct in_addr host_ip;
- host_ip.s_addr = remote_ip;
+ sockaddr_in addr = {};
+ addr.sin_addr.s_addr = remote_ip;
+ addr.sin_family = AF_INET;
+
+ const auto start = std::chrono::steady_clock::now();
+
+ ret = sendto(fd, &packet, sizeof(packet), 0, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (ret < 1)
+ {
+ CLog::Log(LOGERROR, "sendto failed: {} ({})", strerror(errno), errno);
+ return false;
+ }
+
+ event = {};
+ ret = epoll_wait(epfd, &event, 1, timeout_ms);
+ if (ret < 1)
+ {
+ if (ret == 0)
+ {
+ CLog::Log(LOGERROR, "timed out while waiting to receive ({} ms)", timeout_ms);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "epoll_wait failed: {} ({})", strerror(errno), errno);
+ }
- snprintf(cmd_line, sizeof(cmd_line), "ping -c 1 -w %d %s",
- timeout_ms / 1000 + (timeout_ms % 1000) != 0, inet_ntoa(host_ip));
+ return false;
+ }
- int status = -1;
- status = system(cmd_line);
- int result = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ if (event.events & EPOLLIN)
+ {
+ socklen_t length = sizeof(addr);
+ ret = recvfrom(fd, &packet, sizeof(packet), 0, reinterpret_cast<sockaddr*>(&addr), &length);
+ if (ret < 1)
+ {
+ CLog::Log(LOGERROR, "recvfrom failed: {} ({})", strerror(errno), errno);
+ return false;
+ }
+ }
- // http://linux.about.com/od/commands/l/blcmdl8_ping.htm ;
- // 0 reply
- // 1 no reply
- // else some error
+ const auto end = std::chrono::steady_clock::now();
- if (result < 0 || result > 1)
- CLog::Log(LOGERROR, "Ping fail : status = {}, errno = {} : '{}'", status, errno, cmd_line);
+ if (!(packet.header.type == ICMP_ECHOREPLY && packet.header.code == 0))
+ {
+ CLog::Log(LOGERROR, "unexpected ping reply: type={} code={}", packet.header.type,
+ packet.header.code);
+ return false;
+ }
+ else
+ {
+ const auto duration =
+ std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(end - start);
- return result == 0;
+ CLog::Log(LOGDEBUG, "PING {}: icmp_seq={} ttl={} time={:0.3f} ms", inet_ntoa(addr.sin_addr),
+ packet.header.un.echo.sequence, TTL, duration.count());
+ return true;
+ }
}
diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp
index 1a1f7d59fb..e76fd722e9 100644
--- a/xbmc/platform/posix/PosixTimezone.cpp
+++ b/xbmc/platform/posix/PosixTimezone.cpp
@@ -6,21 +6,23 @@
* See LICENSES/README.md for more information.
*/
-#include <time.h>
-#include "PlatformDefs.h"
#include "PosixTimezone.h"
-#include "utils/SystemInfo.h"
#include "ServiceBroker.h"
-#include "utils/StringUtils.h"
#include "XBDateTime.h"
-#include "settings/lib/Setting.h"
-#include "settings/lib/SettingDefinitions.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
-#include <stdlib.h>
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
#include <algorithm>
+#include <climits>
+#include <cstdlib>
+#include <ctime>
+
+#include "PlatformDefs.h"
CPosixTimezone::CPosixTimezone()
{
@@ -195,44 +197,58 @@ void CPosixTimezone::SetTimezone(const std::string& timezoneName)
std::string CPosixTimezone::GetOSConfiguredTimezone()
{
- char timezoneName[255];
+ std::string timezoneName;
- // try Slackware approach first
- ssize_t rlrc = readlink("/etc/localtime-copied-from"
- , timezoneName, sizeof(timezoneName)-1);
+ // try Slackware approach first
+ timezoneName = ReadFromLocaltime("/etc/localtime-copied-from");
- // RHEL and maybe other distros make /etc/localtime a symlink
- if (rlrc == -1)
- rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName)-1);
+ // RHEL and maybe other distros make /etc/localtime a symlink
+ if (timezoneName.empty())
+ timezoneName = ReadFromLocaltime("/etc/localtime");
- if (rlrc != -1)
- {
- timezoneName[rlrc] = '\0';
-
- char* p = strrchr(timezoneName,'/');
- if (p)
- { // we want the previous '/'
- char* q = p;
- *q = 0;
- p = strrchr(timezoneName,'/');
- *q = '/';
- if (p)
- p++;
- }
- return p;
- }
+ // now try Debian approach
+ if (timezoneName.empty())
+ timezoneName = ReadFromTimezone("/etc/timezone");
- // now try Debian approach
- timezoneName[0] = 0;
- FILE* fp = fopen("/etc/timezone", "r");
- if (fp)
- {
- if (fgets(timezoneName, sizeof(timezoneName), fp))
- timezoneName[strlen(timezoneName)-1] = '\0';
- fclose(fp);
- }
+ return timezoneName;
+}
+
+std::string CPosixTimezone::ReadFromLocaltime(const std::string_view filename)
+{
+ char path[PATH_MAX];
+ if(realpath(filename.data(), path) == nullptr)
+ return "";
+
+ // Read the timezone starting from the second last occurrence of /
+ std::string str = path;
+ size_t pos = str.rfind('/');
+ if (pos == std::string::npos)
+ return "";
+
+ pos = str.rfind('/', pos - 1);
+ if (pos == std::string::npos)
+ return "";
+
+ return str.substr(pos + 1);
+}
+
+std::string CPosixTimezone::ReadFromTimezone(const std::string_view filename)
+{
+ std::string timezoneName;
+ std::FILE* file = std::fopen(filename.data(), "r");
+
+ if (file != nullptr)
+ {
+ char tz[255];
+ if (std::fgets(tz, sizeof(tz), file) != nullptr)
+ {
+ timezoneName = tz;
+ }
+
+ std::fclose(file);
+ }
- return timezoneName;
+ return timezoneName;
}
void CPosixTimezone::SettingOptionsTimezoneCountriesFiller(
diff --git a/xbmc/platform/posix/PosixTimezone.h b/xbmc/platform/posix/PosixTimezone.h
index 076e87fa51..26a7f09340 100644
--- a/xbmc/platform/posix/PosixTimezone.h
+++ b/xbmc/platform/posix/PosixTimezone.h
@@ -46,12 +46,14 @@ public:
void* data);
private:
- std::vector<std::string> m_counties;
- std::map<std::string, std::string> m_countryByCode;
- std::map<std::string, std::string> m_countryByName;
-
- std::map<std::string, std::vector<std::string> > m_timezonesByCountryCode;
- std::map<std::string, std::string> m_countriesByTimezoneName;
+ std::string ReadFromLocaltime(std::string_view filename);
+ std::string ReadFromTimezone(std::string_view filename);
+ std::vector<std::string> m_counties;
+ std::map<std::string, std::string> m_countryByCode;
+ std::map<std::string, std::string> m_countryByName;
+
+ std::map<std::string, std::vector<std::string>> m_timezonesByCountryCode;
+ std::map<std::string, std::string> m_countriesByTimezoneName;
};
extern CPosixTimezone g_timezone;
diff --git a/xbmc/platform/posix/filesystem/SMBFile.cpp b/xbmc/platform/posix/filesystem/SMBFile.cpp
index 38a82c3114..db02e177a2 100644
--- a/xbmc/platform/posix/filesystem/SMBFile.cpp
+++ b/xbmc/platform/posix/filesystem/SMBFile.cpp
@@ -719,3 +719,9 @@ int CSMBFile::IoControl(EIoControl request, void* param)
return -1;
}
+int CSMBFile::GetChunkSize()
+{
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ return settings ? (settings->GetInt(CSettings::SETTING_SMB_CHUNKSIZE) * 1024) : (128 * 1024);
+}
diff --git a/xbmc/platform/posix/filesystem/SMBFile.h b/xbmc/platform/posix/filesystem/SMBFile.h
index 8cc960e8e1..74d263588c 100644
--- a/xbmc/platform/posix/filesystem/SMBFile.h
+++ b/xbmc/platform/posix/filesystem/SMBFile.h
@@ -79,7 +79,7 @@ public:
bool OpenForWrite(const CURL& url, bool bOverWrite = false) override;
bool Delete(const CURL& url) override;
bool Rename(const CURL& url, const CURL& urlnew) override;
- int GetChunkSize() override { return 64*1024; }
+ int GetChunkSize() override;
int IoControl(EIoControl request, void* param) override;
protected:
diff --git a/xbmc/platform/posix/main.cpp b/xbmc/platform/posix/main.cpp
index 969a8f9dc1..acac58699e 100644
--- a/xbmc/platform/posix/main.cpp
+++ b/xbmc/platform/posix/main.cpp
@@ -6,13 +6,6 @@
* See LICENSES/README.md for more information.
*/
-#if defined(TARGET_DARWIN_OSX)
-// SDL redefines main as SDL_main
-#ifdef HAS_SDL
-#include <SDL/SDL.h>
-#endif
-#endif
-
#include "PlatformPosix.h"
#include "application/AppEnvironment.h"
#include "application/AppParamParser.h"
diff --git a/xbmc/platform/win32/PlatformDefs.h b/xbmc/platform/win32/PlatformDefs.h
index a7ff4ba261..213bccc848 100644
--- a/xbmc/platform/win32/PlatformDefs.h
+++ b/xbmc/platform/win32/PlatformDefs.h
@@ -9,6 +9,7 @@
#pragma once
#include <windows.h>
+#include <winsock2.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
diff --git a/xbmc/platform/win32/WIN32Util.cpp b/xbmc/platform/win32/WIN32Util.cpp
index 74c1f83912..c8d3b4cea8 100644
--- a/xbmc/platform/win32/WIN32Util.cpp
+++ b/xbmc/platform/win32/WIN32Util.cpp
@@ -839,7 +839,7 @@ extern "C" {
case 'k': /* The hour (24-hour clock representation). */
LEGAL_ALT(0);
- /* FALLTHROUGH */
+ [[fallthrough]];
case 'H':
bp = conv_num(bp, &tm->tm_hour, 0, 23);
LEGAL_ALT(ALT_O);
@@ -847,7 +847,7 @@ extern "C" {
case 'l': /* The hour (12-hour clock representation). */
LEGAL_ALT(0);
- /* FALLTHROUGH */
+ [[fallthrough]];
case 'I':
bp = conv_num(bp, &tm->tm_hour, 1, 12);
if (tm->tm_hour == 12)
@@ -1196,7 +1196,36 @@ HDR_STATUS CWIN32Util::ToggleWindowsHDR(DXGI_MODE_DESC& modeDesc)
HDR_STATUS status = HDR_STATUS::HDR_TOGGLE_FAILED;
#ifdef TARGET_WINDOWS_STORE
- // Not supported - not implemented yet
+ auto hdmi = HdmiDisplayInformation::GetForCurrentView();
+
+ if (!hdmi)
+ return status;
+
+ const auto current = hdmi.GetCurrentDisplayMode();
+
+ for (const auto& mode : hdmi.GetSupportedDisplayModes())
+ {
+ if (mode.IsSmpte2084Supported() != current.IsSmpte2084Supported() &&
+ mode.ResolutionHeightInRawPixels() == current.ResolutionHeightInRawPixels() &&
+ mode.ResolutionWidthInRawPixels() == current.ResolutionWidthInRawPixels() &&
+ mode.StereoEnabled() == false &&
+ fabs(mode.RefreshRate() - current.RefreshRate()) <= 0.00001)
+ {
+ if (current.IsSmpte2084Supported()) // HDR is ON
+ {
+ CLog::LogF(LOGINFO, "Toggle Windows HDR Off (ON => OFF).");
+ if (Wait(hdmi.RequestSetCurrentDisplayModeAsync(mode, HdmiDisplayHdrOption::None)))
+ status = HDR_STATUS::HDR_OFF;
+ }
+ else // HDR is OFF
+ {
+ CLog::LogF(LOGINFO, "Toggle Windows HDR On (OFF => ON).");
+ if (Wait(hdmi.RequestSetCurrentDisplayModeAsync(mode, HdmiDisplayHdrOption::Eotf2084)))
+ status = HDR_STATUS::HDR_ON;
+ }
+ break;
+ }
+ }
#else
uint32_t pathCount = 0;
uint32_t modeCount = 0;
diff --git a/xbmc/platform/win32/WinMain.cpp b/xbmc/platform/win32/WinMain.cpp
index c9ee17d950..87dde67ce9 100644
--- a/xbmc/platform/win32/WinMain.cpp
+++ b/xbmc/platform/win32/WinMain.cpp
@@ -33,16 +33,39 @@ LONG WINAPI CreateMiniDump(EXCEPTION_POINTERS* pEp)
return pEp->ExceptionRecord->ExceptionCode;
}
-//-----------------------------------------------------------------------------
-// Name: WinMain()
-// Desc: The application's entry point
-//-----------------------------------------------------------------------------
-INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT)
+static bool isConsoleAttached{false};
+
+/*!
+ * \brief Basic error reporting before the log subsystem is initialized
+ *
+ * The message is formatted using printf and output to debugger and cmd.exe, as applicable.
+ *
+ * \param[in] format printf-style format string
+ * \param[in] ... optional parameters for the format string.
+ */
+template<typename... Args>
+static void LogError(const wchar_t* format, Args&&... args)
+{
+ const int count = _snwprintf(nullptr, 0, format, args...);
+ // terminating null character not included in count
+ auto buf = std::make_unique<wchar_t[]>(count + 1);
+ swprintf(buf.get(), format, args...);
+
+ OutputDebugString(buf.get());
+
+ if (!isConsoleAttached && AttachConsole(ATTACH_PARENT_PROCESS))
+ {
+ (void)freopen("CONOUT$", "w", stdout);
+ wprintf(L"\n");
+ isConsoleAttached = true;
+ }
+ wprintf(buf.get());
+}
+
+static std::shared_ptr<CAppParams> ParseCommandLine()
{
- // parse command line parameters
int argc = 0;
LPWSTR* argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
-
char** argv = new char*[argc];
for (int i = 0; i < argc; ++i)
@@ -58,7 +81,21 @@ INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT)
CAppParamParser appParamParser;
appParamParser.Parse(argv, argc);
- const auto params = appParamParser.GetAppParams();
+ for (int i = 0; i < argc; ++i)
+ delete[] argv[i];
+ delete[] argv;
+
+ return appParamParser.GetAppParams();
+}
+
+//-----------------------------------------------------------------------------
+// Name: WinMain()
+// Desc: The application's entry point
+//-----------------------------------------------------------------------------
+_Use_decl_annotations_ INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
+{
+ // parse command line parameters
+ const auto params = ParseCommandLine();
// this fixes crash if OPENSSL_CONF is set to existed openssl.cfg
// need to set it as soon as possible
@@ -80,11 +117,16 @@ INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT)
SetUnhandledExceptionFilter(CreateMiniDump);
}
+ int status{0};
+ HRESULT hrCOM{E_FAIL};
+ int rcWinsock{WSANOTINITIALISED};
+ WSADATA wd{};
+
// check if Kodi is already running
using KODI::PLATFORM::WINDOWS::ToW;
std::string appName = CCompileInfo::GetAppName();
HANDLE appRunningMutex = CreateMutex(nullptr, FALSE, ToW(appName + " Media Center").c_str());
- if (GetLastError() == ERROR_ALREADY_EXISTS)
+ if (appRunningMutex != nullptr && GetLastError() == ERROR_ALREADY_EXISTS)
{
auto appNameW = ToW(appName);
HWND hwnd = FindWindow(appNameW.c_str(), appNameW.c_str());
@@ -94,16 +136,25 @@ INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT)
ShowWindow(hwnd, SW_RESTORE);
SetForegroundWindow(hwnd);
}
- ReleaseMutex(appRunningMutex);
- return 0;
+ status = 0;
+ goto cleanup;
}
//Initialize COM
- CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ if ((hrCOM = CoInitializeEx(nullptr, COINIT_MULTITHREADED)) != S_OK)
+ {
+ LogError(L"unable to initialize COM, error %ld\n", hrCOM);
+ status = -2;
+ goto cleanup;
+ }
// Initialise Winsock
- WSADATA wd;
- WSAStartup(MAKEWORD(2, 2), &wd);
+ if ((rcWinsock = WSAStartup(MAKEWORD(2, 2), &wd)) != 0)
+ {
+ LogError(L"unable to initialize Windows Sockets, error %i\n", rcWinsock);
+ status = -3;
+ goto cleanup;
+ }
// use 1 ms timer precision - like SDL initialization used to do
timeBeginPeriod(1);
@@ -116,20 +167,21 @@ INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR commandLine, INT)
CAppEnvironment::SetUp(params);
// Create and run the app
- int status = XBMC_Run(true);
+ status = XBMC_Run(true);
CAppEnvironment::TearDown();
- for (int i = 0; i < argc; ++i)
- delete[] argv[i];
- delete[] argv;
-
// clear previously set timer resolution
timeEndPeriod(1);
- WSACleanup();
- CoUninitialize();
- ReleaseMutex(appRunningMutex);
+cleanup:
+
+ if (rcWinsock == 0)
+ WSACleanup();
+ if (hrCOM == S_OK)
+ CoUninitialize();
+ if (appRunningMutex)
+ CloseHandle(appRunningMutex);
return status;
}
diff --git a/xbmc/platform/win32/filesystem/Win32File.cpp b/xbmc/platform/win32/filesystem/Win32File.cpp
index ae92fd89af..8b2af8ad9a 100644
--- a/xbmc/platform/win32/filesystem/Win32File.cpp
+++ b/xbmc/platform/win32/filesystem/Win32File.cpp
@@ -8,6 +8,9 @@
#include "Win32File.h"
+#include "ServiceBroker.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
#include "utils/log.h"
#include "platform/win32/CharsetConverter.h"
@@ -20,10 +23,8 @@
#include <intsafe.h>
#include <sys/stat.h>
-
using namespace XFILE;
-
CWin32File::CWin32File() : m_smbFile(false)
{
m_hFile = INVALID_HANDLE_VALUE;
@@ -40,7 +41,6 @@ CWin32File::CWin32File(bool asSmbFile) : m_smbFile(asSmbFile)
m_lastSMBFileErr = ERROR_SUCCESS;
}
-
CWin32File::~CWin32File()
{
if (m_hFile != INVALID_HANDLE_VALUE)
@@ -765,7 +765,11 @@ int CWin32File::Stat(struct __stat64* statData)
int CWin32File::GetChunkSize()
{
if (m_smbFile)
- return 64 * 1024;
+ {
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ return settings ? (settings->GetInt(CSettings::SETTING_SMB_CHUNKSIZE) * 1024) : (128 * 1024);
+ }
return 0;
}
diff --git a/xbmc/playlists/PlayList.cpp b/xbmc/playlists/PlayList.cpp
index 7c038842de..82031bad2e 100644
--- a/xbmc/playlists/PlayList.cpp
+++ b/xbmc/playlists/PlayList.cpp
@@ -475,7 +475,7 @@ bool CPlayList::Expand(int position)
for (int i = 0;i<playlist->size();i++)
{
(*playlist)[i]->SetDynPath((*playlist)[i]->GetPath());
- (*playlist)[i]->SetPath(item->GetPath());
+ (*playlist)[i]->SetPath(item->GetDynPath());
(*playlist)[i]->SetStartOffset(item->GetStartOffset());
}
diff --git a/xbmc/playlists/PlayListFactory.cpp b/xbmc/playlists/PlayListFactory.cpp
index 66c508633b..dc45bcce2d 100644
--- a/xbmc/playlists/PlayListFactory.cpp
+++ b/xbmc/playlists/PlayListFactory.cpp
@@ -134,12 +134,12 @@ bool CPlayListFactory::IsPlaylist(const CFileItem& item)
bool CPlayListFactory::IsPlaylist(const CURL& url)
{
return URIUtils::HasExtension(url,
- ".m3u|.m3u8|.b4s|.pls|.strm|.wpl|.asx|.ram|.url|.pxml|.xspf|.xsp");
+ ".m3u|.m3u8|.b4s|.pls|.strm|.wpl|.asx|.ram|.url|.pxml|.xspf");
}
bool CPlayListFactory::IsPlaylist(const std::string& filename)
{
return URIUtils::HasExtension(filename,
- ".m3u|.m3u8|.b4s|.pls|.strm|.wpl|.asx|.ram|.url|.pxml|.xspf|.xsp");
+ ".m3u|.m3u8|.b4s|.pls|.strm|.wpl|.asx|.ram|.url|.pxml|.xspf");
}
diff --git a/xbmc/powermanagement/PowerManager.cpp b/xbmc/powermanagement/PowerManager.cpp
index 3c30817331..fb04187763 100644
--- a/xbmc/powermanagement/PowerManager.cpp
+++ b/xbmc/powermanagement/PowerManager.cpp
@@ -259,7 +259,7 @@ void CPowerManager::StorePlayerState()
if (appPlayer->IsPlaying())
{
m_lastUsedPlayer = appPlayer->GetCurrentPlayer();
- m_lastPlayedFileItem.reset(new CFileItem(g_application.CurrentFileItem()));
+ m_lastPlayedFileItem = std::make_unique<CFileItem>(g_application.CurrentFileItem());
// set the actual offset instead of store and load it from database
m_lastPlayedFileItem->SetStartOffset(appPlayer->GetTime());
// in case of regular stack, correct the start offset by adding current part start time
diff --git a/xbmc/profiles/ProfileManager.cpp b/xbmc/profiles/ProfileManager.cpp
index 600c812cf1..827bfb112f 100644
--- a/xbmc/profiles/ProfileManager.cpp
+++ b/xbmc/profiles/ProfileManager.cpp
@@ -8,13 +8,17 @@
#include "ProfileManager.h"
+#include "ContextMenuManager.h" //! @todo Remove me
#include "DatabaseManager.h"
#include "FileItem.h"
#include "GUIInfoManager.h"
#include "GUIPassword.h"
#include "PasswordManager.h"
+#include "PlayListPlayer.h" //! @todo Remove me
#include "ServiceBroker.h"
#include "Util.h"
+#include "addons/AddonManager.h" //! @todo Remove me
+#include "addons/Service.h" //! @todo Remove me
#include "addons/Skin.h"
#include "application/Application.h" //! @todo Remove me
#include "application/ApplicationComponents.h"
@@ -23,6 +27,7 @@
#include "dialogs/GUIDialogYesNo.h"
#include "events/EventLog.h"
#include "events/EventLogManager.h"
+#include "favourites/FavouritesService.h" //! @todo Remove me
#include "filesystem/Directory.h"
#include "filesystem/DirectoryCache.h"
#include "filesystem/File.h"
@@ -30,31 +35,20 @@
#include "guilib/GUIComponent.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
+#include "guilib/StereoscopicsManager.h" //! @todo Remove me
#include "input/InputManager.h"
+#include "interfaces/json-rpc/JSONRPC.h" //! @todo Remove me
#include "music/MusicLibraryQueue.h"
+#include "network/Network.h" //! @todo Remove me
+#include "network/NetworkServices.h" //! @todo Remove me
+#include "pvr/PVRManager.h" //! @todo Remove me
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "settings/lib/SettingsManager.h"
-#include "threads/SingleLock.h"
-
-#include <algorithm>
-#include <mutex>
-#include <string>
-#include <vector>
#if !defined(TARGET_WINDOWS) && defined(HAS_OPTICAL_DRIVE)
#include "storage/DetectDVDType.h"
#endif
-#include "ContextMenuManager.h" //! @todo Remove me
-#include "PlayListPlayer.h" //! @todo Remove me
-#include "addons/AddonManager.h" //! @todo Remove me
-#include "addons/Service.h" //! @todo Remove me
-#include "application/Application.h" //! @todo Remove me
-#include "favourites/FavouritesService.h" //! @todo Remove me
-#include "guilib/StereoscopicsManager.h" //! @todo Remove me
-#include "interfaces/json-rpc/JSONRPC.h" //! @todo Remove me
-#include "network/Network.h" //! @todo Remove me
-#include "network/NetworkServices.h" //! @todo Remove me
-#include "pvr/PVRManager.h" //! @todo Remove me
+#include "threads/SingleLock.h"
#include "utils/FileUtils.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
@@ -64,6 +58,12 @@
#include "video/VideoLibraryQueue.h" //! @todo Remove me
#include "weather/WeatherManager.h" //! @todo Remove me
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
//! @todo
//! eventually the profile should dictate where special://masterprofile/ is
//! but for now it makes sense to leave all the profile settings in a user
@@ -506,7 +506,8 @@ bool CProfileManager::DeleteProfile(unsigned int index)
m_settings->Save();
}
- CFileItemPtr item = CFileItemPtr(new CFileItem(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory)));
+ CFileItemPtr item =
+ std::make_shared<CFileItem>(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory));
item->SetPath(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory + "/"));
item->m_bIsFolder = true;
item->Select(true);
diff --git a/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp b/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp
index 52eabc4a22..80a3b70c7e 100644
--- a/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp
+++ b/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp
@@ -276,11 +276,11 @@ void CGUIDialogLockSettings::InitializeSettings()
AddToggle(groupDetails, SETTING_LOCK_FILEMANAGER, 20042, SettingLevel::Basic, m_locks.files);
TranslatableIntegerSettingOptions settingsLevelOptions;
- settingsLevelOptions.push_back(TranslatableIntegerSettingOption(106, LOCK_LEVEL::NONE));
- settingsLevelOptions.push_back(TranslatableIntegerSettingOption(593, LOCK_LEVEL::ALL));
- settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10037, LOCK_LEVEL::STANDARD));
- settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10038, LOCK_LEVEL::ADVANCED));
- settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10039, LOCK_LEVEL::EXPERT));
+ settingsLevelOptions.emplace_back(106, LOCK_LEVEL::NONE);
+ settingsLevelOptions.emplace_back(593, LOCK_LEVEL::ALL);
+ settingsLevelOptions.emplace_back(10037, LOCK_LEVEL::STANDARD);
+ settingsLevelOptions.emplace_back(10038, LOCK_LEVEL::ADVANCED);
+ settingsLevelOptions.emplace_back(10039, LOCK_LEVEL::EXPERT);
AddList(groupDetails, SETTING_LOCK_SETTINGS, 20043, SettingLevel::Basic, static_cast<int>(m_locks.settings), settingsLevelOptions, 20043);
AddToggle(groupDetails, SETTING_LOCK_ADDONMANAGER, 24090, SettingLevel::Basic, m_locks.addonManager);
diff --git a/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp b/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
index d7ce0dabdf..dcdf8c47a4 100644
--- a/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
+++ b/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
@@ -347,11 +347,11 @@ void CGUIDialogProfileSettings::InitializeSettings()
}
TranslatableIntegerSettingOptions entries;
- entries.push_back(TranslatableIntegerSettingOption(20062, 0));
- entries.push_back(TranslatableIntegerSettingOption(20063, 1));
- entries.push_back(TranslatableIntegerSettingOption(20061, 2));
+ entries.emplace_back(20062, 0);
+ entries.emplace_back(20063, 1);
+ entries.emplace_back(20061, 2);
if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
- entries.push_back(TranslatableIntegerSettingOption(20107, 3));
+ entries.emplace_back(20107, 3);
AddSpinner(groupMedia, SETTING_PROFILE_MEDIA, 20060, SettingLevel::Basic, m_dbMode, entries);
AddSpinner(groupMedia, SETTING_PROFILE_MEDIA_SOURCES, 20094, SettingLevel::Basic, m_sourcesMode, entries);
diff --git a/xbmc/pvr/CMakeLists.txt b/xbmc/pvr/CMakeLists.txt
index e7a95a6515..5bf1b80697 100644
--- a/xbmc/pvr/CMakeLists.txt
+++ b/xbmc/pvr/CMakeLists.txt
@@ -1,5 +1,6 @@
set(SOURCES PVRCachedImage.cpp
PVRCachedImages.cpp
+ PVRChannelGroupImageFileLoader.cpp
PVRChannelNumberInputHandler.cpp
PVRComponentRegistration.cpp
PVRContextMenus.cpp
@@ -15,6 +16,7 @@ set(SOURCES PVRCachedImage.cpp
set(HEADERS IPVRComponent.h
PVRCachedImage.h
PVRCachedImages.h
+ PVRChannelGroupImageFileLoader.h
PVRChannelNumberInputHandler.h
PVRComponentRegistration.h
PVRContextMenus.h
diff --git a/xbmc/pvr/PVRChannelGroupImageFileLoader.cpp b/xbmc/pvr/PVRChannelGroupImageFileLoader.cpp
new file mode 100644
index 0000000000..e539ca55bd
--- /dev/null
+++ b/xbmc/pvr/PVRChannelGroupImageFileLoader.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 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 "PVRChannelGroupImageFileLoader.h"
+
+#include "FileItem.h"
+#include "filesystem/PVRGUIDirectory.h"
+#include "guilib/Texture.h"
+#include "pictures/Picture.h"
+#include "pvr/PVRCachedImages.h"
+#include "utils/log.h"
+
+bool PVR::CPVRChannelGroupImageFileLoader::CanLoad(const std::string& specialType) const
+{
+ return specialType == "pvr";
+}
+
+std::unique_ptr<CTexture> PVR::CPVRChannelGroupImageFileLoader::Load(const std::string& specialType,
+ const std::string& filePath,
+ unsigned int,
+ unsigned int) const
+{
+ const CPVRGUIDirectory channelGroupDir(filePath);
+ CFileItemList channels;
+ if (!channelGroupDir.GetChannelsDirectory(channels))
+ {
+ return {};
+ }
+
+ std::vector<std::string> channelIcons;
+ for (const auto& channel : channels)
+ {
+ const std::string& icon = channel->GetArt("icon");
+ if (!icon.empty())
+ channelIcons.emplace_back(CPVRCachedImages::UnwrapImageURL(icon));
+
+ if (channelIcons.size() == 9) // limit number of tiles
+ break;
+ }
+
+ return CPicture::CreateTiledThumb(channelIcons);
+}
diff --git a/xbmc/pvr/PVRChannelGroupImageFileLoader.h b/xbmc/pvr/PVRChannelGroupImageFileLoader.h
new file mode 100644
index 0000000000..dcb1ed92bc
--- /dev/null
+++ b/xbmc/pvr/PVRChannelGroupImageFileLoader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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 "imagefiles/SpecialImageFileLoader.h"
+
+namespace PVR
+{
+/*!
+ * @brief Generates a thumbnail for a PVR channel group; tile up to 9 channel icons in the group.
+*/
+class CPVRChannelGroupImageFileLoader : public IMAGE_FILES::ISpecialImageFileLoader
+{
+public:
+ CPVRChannelGroupImageFileLoader() = default;
+ ~CPVRChannelGroupImageFileLoader() override = default;
+
+ bool CanLoad(const std::string& specialType) const override;
+ std::unique_ptr<CTexture> Load(const std::string& specialType,
+ const std::string& goofyChapterPath,
+ unsigned int preferredWidth,
+ unsigned int preferredHeight) const override;
+};
+
+} // namespace PVR
diff --git a/xbmc/pvr/PVRContextMenus.cpp b/xbmc/pvr/PVRContextMenus.cpp
index 724872d202..d88ee9a4e7 100644
--- a/xbmc/pvr/PVRContextMenus.cpp
+++ b/xbmc/pvr/PVRContextMenus.cpp
@@ -98,7 +98,7 @@ std::shared_ptr<CPVRTimerInfoTag> GetTimerInfoTagFromItem(const CFileItem& item)
{
std::shared_ptr<CPVRTimerInfoTag> timer;
- const std::shared_ptr<CPVREpgInfoTag> epg(item.GetEPGInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> epg(item.GetEPGInfoTag());
if (epg)
timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg);
@@ -113,7 +113,7 @@ std::shared_ptr<CPVRTimerInfoTag> GetTimerInfoTagFromItem(const CFileItem& item)
bool PlayEpgTag::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVREpgInfoTag> epg(item.GetEPGInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> epg(item.GetEPGInfoTag());
if (epg)
return epg->IsPlayable();
@@ -130,7 +130,7 @@ bool PlayEpgTag::Execute(const CFileItemPtr& item) const
bool PlayRecording::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRRecording> recording =
+ const std::shared_ptr<const CPVRRecording> recording =
CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(item.GetEPGInfoTag());
if (recording)
return !recording->IsDeleted();
@@ -157,14 +157,14 @@ std::string ShowInformation::GetLabel(const CFileItem& item) const
bool ShowInformation::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRChannel> channel(item.GetPVRChannelInfoTag());
+ const std::shared_ptr<const CPVRChannel> channel(item.GetPVRChannelInfoTag());
if (channel)
return channel->GetEPGNow().get() != nullptr;
if (item.HasEPGInfoTag())
return !item.GetEPGInfoTag()->IsGapTag();
- const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
return timer->GetEpgInfoTag().get() != nullptr;
@@ -187,7 +187,7 @@ bool ShowInformation::Execute(const CFileItemPtr& item) const
bool ShowChannelGuide::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRChannel> channel(item.GetPVRChannelInfoTag());
+ const std::shared_ptr<const CPVRChannel> channel(item.GetPVRChannelInfoTag());
if (channel)
return channel->GetEPGNow().get() != nullptr;
@@ -204,18 +204,18 @@ bool ShowChannelGuide::Execute(const CFileItemPtr& item) const
bool FindSimilar::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRChannel> channel(item.GetPVRChannelInfoTag());
+ const std::shared_ptr<const CPVRChannel> channel(item.GetPVRChannelInfoTag());
if (channel)
return channel->GetEPGNow().get() != nullptr;
if (item.HasEPGInfoTag())
return !item.GetEPGInfoTag()->IsGapTag();
- const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
return timer->GetEpgInfoTag().get() != nullptr;
- const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item.GetPVRRecordingInfoTag());
if (recording)
return !recording->IsDeleted();
@@ -232,14 +232,14 @@ bool FindSimilar::Execute(const CFileItemPtr& item) const
bool StartRecording::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
+ const std::shared_ptr<const CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
std::shared_ptr<CPVRChannel> channel = item.GetPVRChannelInfoTag();
if (channel)
return client && client->GetClientCapabilities().SupportsTimers() &&
!CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel);
- const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag();
if (epg && epg->IsRecordable())
{
if (epg->IsGapTag())
@@ -262,7 +262,7 @@ bool StartRecording::IsVisible(const CFileItem& item) const
bool StartRecording::Execute(const CFileItemPtr& item) const
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
if (!epgTag || epgTag->IsActive())
{
// instant recording
@@ -286,7 +286,7 @@ bool StartRecording::Execute(const CFileItemPtr& item) const
bool StopRecording::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item.GetPVRRecordingInfoTag());
if (recording && recording->IsInProgress())
return true;
@@ -294,11 +294,11 @@ bool StopRecording::IsVisible(const CFileItem& item) const
if (channel)
return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel);
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
return timer->IsRecording();
- const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag();
if (epg && epg->IsGapTag())
{
channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epg);
@@ -311,7 +311,7 @@ bool StopRecording::IsVisible(const CFileItem& item) const
bool StopRecording::Execute(const CFileItemPtr& item) const
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
if (epgTag && epgTag->IsGapTag())
{
// instance recording
@@ -330,7 +330,7 @@ bool StopRecording::Execute(const CFileItemPtr& item) const
bool EditRecording::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item.GetPVRRecordingInfoTag());
if (recording && !recording->IsDeleted() && !recording->IsInProgress())
{
return CServiceBroker::GetPVRManager().Get<PVR::GUI::Recordings>().CanEditRecording(item);
@@ -348,7 +348,7 @@ bool EditRecording::Execute(const CFileItemPtr& item) const
std::string DeleteRecording::GetLabel(const CFileItem& item) const
{
- const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item.GetPVRRecordingInfoTag());
if (recording && recording->IsDeleted())
return g_localizeStrings.Get(19291); /* Delete permanently */
@@ -357,11 +357,11 @@ std::string DeleteRecording::GetLabel(const CFileItem& item) const
bool DeleteRecording::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
+ const std::shared_ptr<const CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
if (client && !client->GetClientCapabilities().SupportsRecordingsDelete())
return false;
- const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item.GetPVRRecordingInfoTag());
if (recording && !recording->IsInProgress())
return true;
@@ -386,7 +386,7 @@ bool DeleteRecording::Execute(const CFileItemPtr& item) const
bool UndeleteRecording::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item.GetPVRRecordingInfoTag());
if (recording && recording->IsDeleted())
return true;
@@ -420,7 +420,7 @@ bool DeleteWatchedRecordings::Execute(const std::shared_ptr<CFileItem>& item) co
bool AddReminder::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag();
if (epg && !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg) &&
epg->StartAsLocalTime() > CDateTime::GetCurrentDateTime())
return true;
@@ -438,7 +438,7 @@ bool AddReminder::Execute(const std::shared_ptr<CFileItem>& item) const
std::string ToggleTimerState::GetLabel(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
if (timer && !timer->IsDisabled())
return g_localizeStrings.Get(844); /* Deactivate */
@@ -447,7 +447,7 @@ std::string ToggleTimerState::GetLabel(const CFileItem& item) const
bool ToggleTimerState::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
if (!timer || URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER) ||
timer->IsBroken())
return false;
@@ -465,7 +465,7 @@ bool ToggleTimerState::Execute(const CFileItemPtr& item) const
bool AddTimerRule::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag();
return (epg && !epg->IsGapTag() &&
!CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg));
}
@@ -480,10 +480,10 @@ bool AddTimerRule::Execute(const CFileItemPtr& item) const
std::string EditTimerRule::GetLabel(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
{
- const std::shared_ptr<CPVRTimerInfoTag> parentTimer(
+ const std::shared_ptr<const CPVRTimerInfoTag> parentTimer(
CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer));
if (parentTimer)
{
@@ -497,7 +497,7 @@ std::string EditTimerRule::GetLabel(const CFileItem& item) const
bool EditTimerRule::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
return timer->HasParent();
@@ -514,10 +514,10 @@ bool EditTimerRule::Execute(const CFileItemPtr& item) const
bool DeleteTimerRule::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
{
- const std::shared_ptr<CPVRTimerInfoTag> parentTimer(
+ const std::shared_ptr<const CPVRTimerInfoTag> parentTimer(
CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer));
if (parentTimer)
return parentTimer->GetTimerType()->AllowsDelete();
@@ -541,10 +541,10 @@ bool DeleteTimerRule::Execute(const CFileItemPtr& item) const
std::string EditTimer::GetLabel(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
if (timer)
{
- const std::shared_ptr<CPVRTimerType> timerType = timer->GetTimerType();
+ const std::shared_ptr<const CPVRTimerType> timerType = timer->GetTimerType();
if (item.GetEPGInfoTag())
{
if (timerType->IsReminder())
@@ -562,7 +562,7 @@ std::string EditTimer::GetLabel(const CFileItem& item) const
bool EditTimer::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
return timer && (!item.GetEPGInfoTag() ||
!URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER));
}
@@ -580,10 +580,10 @@ std::string DeleteTimer::GetLabel(const CFileItem& item) const
if (item.GetPVRTimerInfoTag())
return g_localizeStrings.Get(117); /* Delete */
- const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag();
if (epg)
{
- const std::shared_ptr<CPVRTimerInfoTag> timer =
+ const std::shared_ptr<const CPVRTimerInfoTag> timer =
CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg);
if (timer && timer->IsReminder())
return g_localizeStrings.Get(827); /* Delete reminder */
@@ -593,7 +593,7 @@ std::string DeleteTimer::GetLabel(const CFileItem& item) const
bool DeleteTimer::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
if (timer &&
(!item.GetEPGInfoTag() ||
!URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER)) &&
@@ -618,7 +618,7 @@ std::string PVRClientMenuHook::GetLabel(const CFileItem& item) const
bool PVRClientMenuHook::IsVisible(const CFileItem& item) const
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
+ const std::shared_ptr<const CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
if (!client || m_hook.GetAddonId() != client->ID())
return false;
diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp
index d1ea29a122..a26bd5f636 100644
--- a/xbmc/pvr/PVRDatabase.cpp
+++ b/xbmc/pvr/PVRDatabase.cpp
@@ -155,7 +155,8 @@ void CPVRDatabase::CreateTables()
"idEpg integer, "
"bHasArchive bool, "
"iClientProviderUid integer, "
- "bIsUserSetHidden bool"
+ "bIsUserSetHidden bool, "
+ "iLastWatchedGroupId integer"
")");
CLog::LogFC(LOGDEBUG, LOGPVR, "Creating table 'channelgroups'");
@@ -361,6 +362,12 @@ void CPVRDatabase::UpdateTables(int iVersion)
// Should mostly be the order the groups appeared from backend or were created by user locally.
m_pDS->exec("UPDATE channelgroups SET iPosition = idGroup");
}
+
+ if (iVersion < 44)
+ {
+ m_pDS->exec("ALTER TABLE channels ADD iLastWatchedGroupId integer");
+ m_pDS->exec("UPDATE channels SET iLastWatchedGroupId = -1");
+ }
}
/********** Client methods **********/
@@ -403,7 +410,7 @@ bool CPVRDatabase::Delete(const CPVRClient& client)
return DeleteValues("clients", filter);
}
-int CPVRDatabase::GetPriority(const CPVRClient& client)
+int CPVRDatabase::GetPriority(const CPVRClient& client) const
{
if (client.GetID() == PVR_INVALID_CLIENT_ID)
return 0;
@@ -537,7 +544,7 @@ bool CPVRDatabase::Get(CPVRProviders& results,
return bReturn;
}
-int CPVRDatabase::GetMaxProviderId()
+int CPVRDatabase::GetMaxProviderId() const
{
std::string strQuery = PrepareSQL("SELECT max(idProvider) as maxProviderId from providers");
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -584,6 +591,7 @@ int CPVRDatabase::Get(bool bRadio,
channel->m_bHasArchive = m_pDS->fv("bHasArchive").get_asBool();
channel->m_iClientProviderUid = m_pDS->fv("iClientProviderUid").get_asInt();
channel->m_bIsUserSetHidden = m_pDS->fv("bIsUserSetHidden").get_asBool();
+ channel->m_lastWatchedGroupId = m_pDS->fv("iLastWatchedGroupId").get_asInt();
channel->UpdateEncryptionName();
@@ -1029,14 +1037,15 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit)
"INSERT INTO channels ("
"iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, "
"sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
- "idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden) "
- "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i)",
+ "idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId) "
+ "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i)",
channel.UniqueID(), (channel.IsRadio() ? 1 : 0), (channel.IsHidden() ? 1 : 0),
(channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0),
(channel.IsLocked() ? 1 : 0), channel.IconPath().c_str(), channel.ChannelName().c_str(), 0,
(channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(),
static_cast<unsigned int>(channel.LastWatched()), channel.ClientID(), channel.EpgID(),
- channel.HasArchive(), channel.ClientProviderUid(), channel.IsUserSetHidden() ? 1 : 0);
+ channel.HasArchive(), channel.ClientProviderUid(), channel.IsUserSetHidden() ? 1 : 0,
+ channel.LastWatchedGroupId());
}
else
{
@@ -1045,15 +1054,16 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit)
"REPLACE INTO channels ("
"iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, "
"sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
- "idChannel, idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden) "
- "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i, %i, %i)",
+ "idChannel, idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId) "
+ "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i, %i, %i, %i)",
channel.UniqueID(), (channel.IsRadio() ? 1 : 0), (channel.IsHidden() ? 1 : 0),
(channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0),
(channel.IsLocked() ? 1 : 0), channel.ClientIconPath().c_str(),
channel.ChannelName().c_str(), 0, (channel.EPGEnabled() ? 1 : 0),
channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()),
channel.ClientID(), strValue.c_str(), channel.EpgID(), channel.HasArchive(),
- channel.ClientProviderUid(), channel.IsUserSetHidden() ? 1 : 0);
+ channel.ClientProviderUid(), channel.IsUserSetHidden() ? 1 : 0,
+ channel.LastWatchedGroupId());
}
if (QueueInsertQuery(strQuery))
@@ -1067,12 +1077,12 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit)
return bReturn;
}
-bool CPVRDatabase::UpdateLastWatched(const CPVRChannel& channel)
+bool CPVRDatabase::UpdateLastWatched(const CPVRChannel& channel, int groupId)
{
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::string strQuery =
- PrepareSQL("UPDATE channels SET iLastWatched = %u WHERE idChannel = %i",
- static_cast<unsigned int>(channel.LastWatched()), channel.ChannelID());
+ const std::string strQuery = PrepareSQL(
+ "UPDATE channels SET iLastWatched = %u, iLastWatchedGroupId = %i WHERE idChannel = %i",
+ static_cast<unsigned int>(channel.LastWatched()), groupId, channel.ChannelID());
return ExecuteQuery(strQuery);
}
diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h
index 61b620b8e9..7a8932e95d 100644
--- a/xbmc/pvr/PVRDatabase.h
+++ b/xbmc/pvr/PVRDatabase.h
@@ -64,7 +64,7 @@ namespace PVR
* @brief Get the minimal database version that is required to operate correctly.
* @return The minimal database version.
*/
- int GetSchemaVersion() const override { return 43; }
+ int GetSchemaVersion() const override { return 44; }
/*!
* @brief Get the default sqlite database filename.
@@ -100,7 +100,7 @@ namespace PVR
* @param client The client.
* @return The priority.
*/
- int GetPriority(const CPVRClient& client);
+ int GetPriority(const CPVRClient& client) const;
/*! @name Channel methods */
//@{
@@ -187,7 +187,7 @@ namespace PVR
* @brief Get the maximum provider id in the database
* @return The maximum provider id in the database
*/
- int GetMaxProviderId();
+ int GetMaxProviderId() const;
//@}
@@ -285,9 +285,10 @@ namespace PVR
/*!
* @brief Updates the last watched timestamp for the channel
* @param channel the channel
+ * @param groupId the id of the group used to watch the channel
* @return whether the update was successful
*/
- bool UpdateLastWatched(const CPVRChannel& channel);
+ bool UpdateLastWatched(const CPVRChannel& channel, int groupId);
/*!
* @brief Updates the last watched timestamp for the channel group
diff --git a/xbmc/pvr/PVREventLogJob.cpp b/xbmc/pvr/PVREventLogJob.cpp
index b70ad1cb62..f6f6667b41 100644
--- a/xbmc/pvr/PVREventLogJob.cpp
+++ b/xbmc/pvr/PVREventLogJob.cpp
@@ -31,7 +31,7 @@ void CPVREventLogJob::AddEvent(bool bNotifyUser,
const std::string& msg,
const std::string& icon)
{
- m_events.emplace_back(Event(bNotifyUser, eLevel, label, msg, icon));
+ m_events.emplace_back(bNotifyUser, eLevel, label, msg, icon);
}
bool CPVREventLogJob::DoWork()
diff --git a/xbmc/pvr/PVRItem.cpp b/xbmc/pvr/PVRItem.cpp
index 5eee911bdb..7bf13228db 100644
--- a/xbmc/pvr/PVRItem.cpp
+++ b/xbmc/pvr/PVRItem.cpp
@@ -50,7 +50,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVRItem::GetNextEpgInfoTag() const
{
if (m_item->IsEPG())
{
- const std::shared_ptr<CPVRChannel> channel =
+ const std::shared_ptr<const CPVRChannel> channel =
CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(
m_item->GetEPGInfoTag());
if (channel)
@@ -62,7 +62,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVRItem::GetNextEpgInfoTag() const
}
else if (m_item->IsPVRTimer())
{
- const std::shared_ptr<CPVRChannel> channel = m_item->GetPVRTimerInfoTag()->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = m_item->GetPVRTimerInfoTag()->Channel();
if (channel)
return channel->GetEPGNext();
}
diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp
index dc950a7148..62ede89911 100644
--- a/xbmc/pvr/PVRManager.cpp
+++ b/xbmc/pvr/PVRManager.cpp
@@ -293,7 +293,7 @@ std::shared_ptr<CPVRClient> CPVRManager::GetClient(const CFileItem& item) const
iClientID = item.GetEPGInfoTag()->ClientID();
else if (URIUtils::IsPVRChannel(item.GetPath()))
{
- const std::shared_ptr<CPVRChannel> channel = m_channelGroups->GetByPath(item.GetPath());
+ const std::shared_ptr<const CPVRChannel> channel = m_channelGroups->GetByPath(item.GetPath());
if (channel)
iClientID = channel->ClientID();
}
@@ -304,7 +304,7 @@ std::shared_ptr<CPVRClient> CPVRManager::GetClient(const CFileItem& item) const
}
else if (URIUtils::IsPVRRecording(item.GetPath()))
{
- const std::shared_ptr<CPVRRecording> recording = m_recordings->GetByPath(item.GetPath());
+ const std::shared_ptr<const CPVRRecording> recording = m_recordings->GetByPath(item.GetPath());
if (recording)
iClientID = recording->ClientID();
}
@@ -349,13 +349,13 @@ void CPVRManager::ResetProperties()
std::unique_lock<CCriticalSection> lock(m_critSection);
Clear();
- m_database.reset(new CPVRDatabase);
- m_providers.reset(new CPVRProviders);
- m_channelGroups.reset(new CPVRChannelGroupsContainer);
- m_recordings.reset(new CPVRRecordings);
- m_timers.reset(new CPVRTimers);
- m_guiInfo.reset(new CPVRGUIInfo);
- m_parentalTimer.reset(new CStopWatch);
+ m_database = std::make_shared<CPVRDatabase>();
+ m_providers = std::make_shared<CPVRProviders>();
+ m_channelGroups = std::make_shared<CPVRChannelGroupsContainer>();
+ m_recordings = std::make_shared<CPVRRecordings>();
+ m_timers = std::make_shared<CPVRTimers>();
+ m_guiInfo = std::make_unique<CPVRGUIInfo>();
+ m_parentalTimer = std::make_unique<CStopWatch>();
m_knownClients.clear();
}
@@ -793,7 +793,7 @@ void CPVRManager::RestartParentalTimer()
m_parentalTimer->StartZero();
}
-bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRManager::IsParentalLocked(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
return m_channelGroups && epgTag &&
IsCurrentlyParentalLocked(
@@ -801,12 +801,12 @@ bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVREpgInfoTag>& epgTag
epgTag->IsParentalLocked());
}
-bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVRChannel>& channel) const
+bool CPVRManager::IsParentalLocked(const std::shared_ptr<const CPVRChannel>& channel) const
{
return channel && IsCurrentlyParentalLocked(channel, channel->IsLocked());
}
-bool CPVRManager::IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel>& channel,
+bool CPVRManager::IsCurrentlyParentalLocked(const std::shared_ptr<const CPVRChannel>& channel,
bool bGenerallyLocked) const
{
bool bReturn = false;
@@ -814,7 +814,7 @@ bool CPVRManager::IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel>&
if (!channel || !bGenerallyLocked)
return bReturn;
- const std::shared_ptr<CPVRChannel> currentChannel = m_playbackState->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> currentChannel = m_playbackState->GetPlayingChannel();
if ( // if channel in question is currently playing it must be currently unlocked.
(!currentChannel || channel != currentChannel) &&
diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h
index e92f3a8fc6..9297035111 100644
--- a/xbmc/pvr/PVRManager.h
+++ b/xbmc/pvr/PVRManager.h
@@ -308,14 +308,14 @@ public:
* @param channel The channel to check.
* @return True if parental lock is overridden, false otherwise.
*/
- bool IsParentalLocked(const std::shared_ptr<CPVRChannel>& channel) const;
+ bool IsParentalLocked(const std::shared_ptr<const 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 std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ bool IsParentalLocked(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
/*!
* @brief Restart the parental timer.
@@ -427,7 +427,7 @@ private:
*/
void TriggerPlayChannelOnStartup();
- bool IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel>& channel,
+ bool IsCurrentlyParentalLocked(const std::shared_ptr<const CPVRChannel>& channel,
bool bGenerallyLocked) const;
CEventSource<PVREvent> m_events;
diff --git a/xbmc/pvr/PVRPlaybackState.cpp b/xbmc/pvr/PVRPlaybackState.cpp
index df63e1130e..d136ad0a51 100644
--- a/xbmc/pvr/PVRPlaybackState.cpp
+++ b/xbmc/pvr/PVRPlaybackState.cpp
@@ -31,6 +31,7 @@
#include "utils/log.h"
#include <algorithm>
+#include <memory>
#include <mutex>
using namespace PVR;
@@ -70,7 +71,7 @@ void CPVRPlaybackState::ReInit()
{
if (m_playingChannelUniqueId != -1)
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
CServiceBroker::GetPVRManager().ChannelGroups()->GetByIdFromAll(m_playingGroupId);
if (group)
m_playingChannel = group->GetByUniqueID({m_playingClientId, m_playingChannelUniqueId});
@@ -84,7 +85,7 @@ void CPVRPlaybackState::ReInit()
}
else if (m_playingEpgTagChannelUniqueId != -1 && m_playingEpgTagUniqueId != 0)
{
- const std::shared_ptr<CPVREpg> epg =
+ const std::shared_ptr<const CPVREpg> epg =
CServiceBroker::GetPVRManager().EpgContainer().GetByChannelUid(
m_playingClientId, m_playingEpgTagChannelUniqueId);
if (epg)
@@ -92,7 +93,7 @@ void CPVRPlaybackState::ReInit()
}
}
- const std::shared_ptr<CPVRChannelGroupsContainer> groups =
+ const std::shared_ptr<const CPVRChannelGroupsContainer> groups =
CServiceBroker::GetPVRManager().ChannelGroups();
const CPVRChannelGroups* groupsTV = groups->GetTV();
const CPVRChannelGroups* groupsRadio = groups->GetRadio();
@@ -186,8 +187,8 @@ void CPVRPlaybackState::OnPlaybackStarted(const CFileItem& item)
if (m_lastWatchedUpdateTimer)
m_lastWatchedUpdateTimer->Stop(true);
- m_lastWatchedUpdateTimer.reset(
- new CLastWatchedUpdateTimer(*this, channel, CDateTime::GetUTCDateTime()));
+ m_lastWatchedUpdateTimer =
+ std::make_unique<CLastWatchedUpdateTimer>(*this, channel, CDateTime::GetUTCDateTime());
m_lastWatchedUpdateTimer->Start(std::chrono::milliseconds(iLastWatchedDelay));
}
else
@@ -216,7 +217,7 @@ void CPVRPlaybackState::OnPlaybackStarted(const CFileItem& item)
if (m_playingClientId != -1)
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_playingClientId);
if (client)
m_strPlayingClientName = client->GetFriendlyName();
@@ -330,11 +331,11 @@ bool CPVRPlaybackState::IsPlayingChannel(int iClientID, int iUniqueChannelID) co
return m_playingClientId == iClientID && m_playingChannelUniqueId == iUniqueChannelID;
}
-bool CPVRPlaybackState::IsPlayingChannel(const std::shared_ptr<CPVRChannel>& channel) const
+bool CPVRPlaybackState::IsPlayingChannel(const std::shared_ptr<const CPVRChannel>& channel) const
{
if (channel)
{
- const std::shared_ptr<CPVRChannel> current = GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> current = GetPlayingChannel();
if (current && current->ClientID() == channel->ClientID() &&
current->UniqueID() == channel->UniqueID())
return true;
@@ -343,11 +344,12 @@ bool CPVRPlaybackState::IsPlayingChannel(const std::shared_ptr<CPVRChannel>& cha
return false;
}
-bool CPVRPlaybackState::IsPlayingRecording(const std::shared_ptr<CPVRRecording>& recording) const
+bool CPVRPlaybackState::IsPlayingRecording(
+ const std::shared_ptr<const CPVRRecording>& recording) const
{
if (recording)
{
- const std::shared_ptr<CPVRRecording> current = GetPlayingRecording();
+ const std::shared_ptr<const CPVRRecording> current = GetPlayingRecording();
if (current && current->ClientID() == recording->ClientID() &&
current->ClientRecordingID() == recording->ClientRecordingID())
return true;
@@ -356,11 +358,11 @@ bool CPVRPlaybackState::IsPlayingRecording(const std::shared_ptr<CPVRRecording>&
return false;
}
-bool CPVRPlaybackState::IsPlayingEpgTag(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRPlaybackState::IsPlayingEpgTag(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (epgTag)
{
- const std::shared_ptr<CPVREpgInfoTag> current = GetPlayingEpgTag();
+ const std::shared_ptr<const CPVREpgInfoTag> current = GetPlayingEpgTag();
if (current && current->ClientID() == epgTag->ClientID() &&
current->UniqueChannelID() == epgTag->UniqueChannelID() &&
current->UniqueBroadcastID() == epgTag->UniqueBroadcastID())
@@ -419,27 +421,64 @@ bool CPVRPlaybackState::IsRecording() const
bool CPVRPlaybackState::IsRecordingOnPlayingChannel() const
{
- const std::shared_ptr<CPVRChannel> currentChannel = GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> currentChannel = GetPlayingChannel();
return currentChannel &&
CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*currentChannel);
}
bool CPVRPlaybackState::IsPlayingActiveRecording() const
{
- const std::shared_ptr<CPVRRecording> currentRecording = GetPlayingRecording();
+ const std::shared_ptr<const CPVRRecording> currentRecording = GetPlayingRecording();
return currentRecording && currentRecording->IsInProgress();
}
bool CPVRPlaybackState::CanRecordOnPlayingChannel() const
{
- const std::shared_ptr<CPVRChannel> currentChannel = GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> currentChannel = GetPlayingChannel();
return currentChannel && currentChannel->CanRecord();
}
+namespace
+{
+std::shared_ptr<CPVRChannelGroup> GetFirstNonDeletedAndNonHiddenChannelGroup(
+ const std::shared_ptr<CPVRChannelGroupMember>& groupMember)
+{
+ CPVRChannelGroups* groups{
+ CServiceBroker::GetPVRManager().ChannelGroups()->Get(groupMember->IsRadio())};
+ if (groups)
+ {
+ const std::vector<std::shared_ptr<CPVRChannelGroup>> members{
+ groups->GetMembers(true /* exclude hidden */)};
+
+ for (const auto& member : members)
+ {
+ if (member->IsDeleted())
+ continue;
+
+ if (member->GetByUniqueID(groupMember->Channel()->StorageId()))
+ return member;
+ }
+ }
+
+ CLog::LogFC(LOGERROR, LOGPVR,
+ "Failed to obtain non-deleted and non-hidden group for channel '{}‘",
+ groupMember->Channel()->ChannelName());
+ return {};
+}
+} // unnamed namespace
+
void CPVRPlaybackState::SetActiveChannelGroup(const std::shared_ptr<CPVRChannelGroup>& group)
{
if (group)
{
+ if (group->IsHidden() || group->IsDeleted())
+ {
+ CLog::LogFC(LOGERROR, LOGPVR,
+ "Rejecting to make hidden or deleted group '{}‘ the active group.",
+ group->GroupName());
+ return;
+ }
+
if (group->IsRadio())
m_activeGroupRadio = group;
else
@@ -455,62 +494,26 @@ void CPVRPlaybackState::SetActiveChannelGroup(
const std::shared_ptr<CPVRChannelGroupMember>& channel)
{
const bool bRadio = channel->Channel()->IsRadio();
- const std::shared_ptr<CPVRChannelGroup> group =
- CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio)->GetById(channel->GroupID());
+ std::shared_ptr<CPVRChannelGroup> group{
+ CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio)->GetById(channel->GroupID())};
- SetActiveChannelGroup(group);
-}
+ if (group && (group->IsHidden() || group->IsDeleted()))
+ group = GetFirstNonDeletedAndNonHiddenChannelGroup(channel);
-namespace
-{
-std::shared_ptr<CPVRChannelGroup> GetFirstNonDeletedAndNonHiddenChannelGroup(bool bRadio)
-{
- CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio);
- if (groups)
- {
- const std::vector<std::shared_ptr<CPVRChannelGroup>> members =
- groups->GetMembers(true); // exclude hidden
-
- const auto it = std::find_if(members.cbegin(), members.cend(),
- [](const auto& group) { return !group->IsDeleted(); });
- if (it != members.cend())
- return (*it);
- }
-
- CLog::LogFC(LOGERROR, LOGPVR, "Failed to obtain any non-deleted and non-hidden group");
- return {};
+ SetActiveChannelGroup(group);
}
-} // unnamed namespace
std::shared_ptr<CPVRChannelGroup> CPVRPlaybackState::GetActiveChannelGroup(bool bRadio) const
{
if (bRadio)
- {
- if (m_activeGroupRadio && (m_activeGroupRadio->IsDeleted() || m_activeGroupRadio->IsHidden()))
- {
- // switch to first non-deleted and non-hidden group
- const auto group = GetFirstNonDeletedAndNonHiddenChannelGroup(bRadio);
- if (group)
- const_cast<CPVRPlaybackState*>(this)->SetActiveChannelGroup(group);
- }
return m_activeGroupRadio;
- }
else
- {
- if (m_activeGroupTV && (m_activeGroupTV->IsDeleted() || m_activeGroupTV->IsHidden()))
- {
- // switch to first non-deleted and non-hidden group
- const auto group = GetFirstNonDeletedAndNonHiddenChannelGroup(bRadio);
- if (group)
- const_cast<CPVRPlaybackState*>(this)->SetActiveChannelGroup(group);
- }
return m_activeGroupTV;
- }
}
CDateTime CPVRPlaybackState::GetPlaybackTime(int iClientID, int iUniqueChannelID) const
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = GetPlayingEpgTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = GetPlayingEpgTag();
if (epgTag && iClientID == epgTag->ClientID() && iUniqueChannelID == epgTag->UniqueChannelID())
{
// playing an epg tag on requested channel
@@ -542,7 +545,7 @@ void CPVRPlaybackState::UpdateLastWatched(const std::shared_ptr<CPVRChannelGroup
time_t iTime;
time.GetAsTime(iTime);
- channel->Channel()->SetLastWatched(iTime);
+ channel->Channel()->SetLastWatched(iTime, channel->GroupID());
// update last watched timestamp for group
const bool bRadio = channel->Channel()->IsRadio();
diff --git a/xbmc/pvr/PVRPlaybackState.h b/xbmc/pvr/PVRPlaybackState.h
index 2ef750087c..c11eeac4cd 100644
--- a/xbmc/pvr/PVRPlaybackState.h
+++ b/xbmc/pvr/PVRPlaybackState.h
@@ -115,21 +115,21 @@ public:
* @param channel The channel to check.
* @return True if it's playing, false otherwise.
*/
- bool IsPlayingChannel(const std::shared_ptr<CPVRChannel>& channel) const;
+ bool IsPlayingChannel(const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @brief Check if the given recording is playing.
* @param recording The recording to check.
* @return True if it's playing, false otherwise.
*/
- bool IsPlayingRecording(const std::shared_ptr<CPVRRecording>& recording) const;
+ bool IsPlayingRecording(const std::shared_ptr<const CPVRRecording>& recording) const;
/*!
* @brief Check if the given epg tag is playing.
* @param epgTag The tag to check.
* @return True if it's playing, false otherwise.
*/
- bool IsPlayingEpgTag(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ bool IsPlayingEpgTag(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
/*!
* @brief Return the channel that is currently playing.
diff --git a/xbmc/pvr/PVRThumbLoader.cpp b/xbmc/pvr/PVRThumbLoader.cpp
index 4b3544c7c0..3f5fa63822 100644
--- a/xbmc/pvr/PVRThumbLoader.cpp
+++ b/xbmc/pvr/PVRThumbLoader.cpp
@@ -11,9 +11,6 @@
#include "FileItem.h"
#include "ServiceBroker.h"
#include "TextureCache.h"
-#include "TextureCacheJob.h"
-#include "pictures/Picture.h"
-#include "pvr/PVRCachedImages.h"
#include "pvr/PVRManager.h"
#include "pvr/filesystem/PVRGUIDirectory.h"
#include "settings/AdvancedSettings.h"
@@ -102,35 +99,7 @@ bool CPVRThumbLoader::FillThumb(CFileItem& item)
std::string CPVRThumbLoader::CreateChannelGroupThumb(const CFileItem& channelGroupItem)
{
- const CPVRGUIDirectory channelGroupDir(channelGroupItem.GetPath());
- CFileItemList channels;
- if (channelGroupDir.GetChannelsDirectory(channels))
- {
- std::vector<std::string> channelIcons;
- for (const auto& channel : channels)
- {
- const std::string& icon = channel->GetArt("icon");
- if (!icon.empty())
- channelIcons.emplace_back(CPVRCachedImages::UnwrapImageURL(icon));
-
- if (channelIcons.size() == 9) // limit number of tiles
- break;
- }
-
- std::string thumb = StringUtils::Format(
- "{}?ts={}", // append timestamp to Thumb URL to enforce texture refresh
- CTextureUtils::GetWrappedImageURL(channelGroupItem.GetPath(), "pvr"), std::time(nullptr));
- const std::string relativeCacheFile = CTextureCache::GetCacheFile(thumb) + ".png";
- if (CPicture::CreateTiledThumb(channelIcons, CTextureCache::GetCachedPath(relativeCacheFile)))
- {
- CTextureDetails details;
- details.file = relativeCacheFile;
- details.width = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes;
- details.height = details.width;
- CServiceBroker::GetTextureCache()->AddCachedTexture(thumb, details);
- return thumb;
- }
- }
-
- return {};
+ return StringUtils::Format("{}?ts={}", // append timestamp to Thumb URL to enforce texture refresh
+ CTextureUtils::GetWrappedImageURL(channelGroupItem.GetPath(), "pvr"),
+ std::time(nullptr));
}
diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp
index f149301cce..d3a8408dc0 100644
--- a/xbmc/pvr/addons/PVRClient.cpp
+++ b/xbmc/pvr/addons/PVRClient.cpp
@@ -421,7 +421,7 @@ const std::string CPVRClient::GetFriendlyName() const
return Name();
}
-PVR_ERROR CPVRClient::GetDriveSpace(uint64_t& iTotal, uint64_t& iUsed)
+PVR_ERROR CPVRClient::GetDriveSpace(uint64_t& iTotal, uint64_t& iUsed) const
{
/* default to 0 in case of error */
iTotal = 0;
@@ -448,7 +448,7 @@ PVR_ERROR CPVRClient::StartChannelScan()
m_clientCapabilities.SupportsChannelScan());
}
-PVR_ERROR CPVRClient::OpenDialogChannelAdd(const std::shared_ptr<CPVRChannel>& channel)
+PVR_ERROR CPVRClient::OpenDialogChannelAdd(const std::shared_ptr<const CPVRChannel>& channel)
{
return DoAddonCall(
__func__,
@@ -460,7 +460,7 @@ PVR_ERROR CPVRClient::OpenDialogChannelAdd(const std::shared_ptr<CPVRChannel>& c
m_clientCapabilities.SupportsChannelSettings());
}
-PVR_ERROR CPVRClient::OpenDialogChannelSettings(const std::shared_ptr<CPVRChannel>& channel)
+PVR_ERROR CPVRClient::OpenDialogChannelSettings(const std::shared_ptr<const CPVRChannel>& channel)
{
return DoAddonCall(
__func__,
@@ -472,7 +472,7 @@ PVR_ERROR CPVRClient::OpenDialogChannelSettings(const std::shared_ptr<CPVRChanne
m_clientCapabilities.SupportsChannelSettings());
}
-PVR_ERROR CPVRClient::DeleteChannel(const std::shared_ptr<CPVRChannel>& channel)
+PVR_ERROR CPVRClient::DeleteChannel(const std::shared_ptr<const CPVRChannel>& channel)
{
return DoAddonCall(
__func__,
@@ -484,7 +484,7 @@ PVR_ERROR CPVRClient::DeleteChannel(const std::shared_ptr<CPVRChannel>& channel)
m_clientCapabilities.SupportsChannelSettings());
}
-PVR_ERROR CPVRClient::RenameChannel(const std::shared_ptr<CPVRChannel>& channel)
+PVR_ERROR CPVRClient::RenameChannel(const std::shared_ptr<const CPVRChannel>& channel)
{
return DoAddonCall(
__func__,
@@ -498,7 +498,10 @@ PVR_ERROR CPVRClient::RenameChannel(const std::shared_ptr<CPVRChannel>& channel)
m_clientCapabilities.SupportsChannelSettings());
}
-PVR_ERROR CPVRClient::GetEPGForChannel(int iChannelUid, CPVREpg* epg, time_t start, time_t end)
+PVR_ERROR CPVRClient::GetEPGForChannel(int iChannelUid,
+ CPVREpg* epg,
+ time_t start,
+ time_t end) const
{
return DoAddonCall(
__func__,
@@ -652,8 +655,8 @@ void CPVRClient::WriteStreamProperties(const PVR_NAMED_VALUE* properties,
}
}
-PVR_ERROR CPVRClient::GetEpgTagStreamProperties(const std::shared_ptr<CPVREpgInfoTag>& tag,
- CPVRStreamProperties& props)
+PVR_ERROR CPVRClient::GetEpgTagStreamProperties(const std::shared_ptr<const CPVREpgInfoTag>& tag,
+ CPVRStreamProperties& props) const
{
return DoAddonCall(__func__, [&tag, &props](const AddonInstance* addon) {
CAddonEpgTag addonTag(tag);
@@ -672,7 +675,7 @@ PVR_ERROR CPVRClient::GetEpgTagStreamProperties(const std::shared_ptr<CPVREpgInf
}
PVR_ERROR CPVRClient::GetEpgTagEdl(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
- std::vector<PVR_EDL_ENTRY>& edls)
+ std::vector<PVR_EDL_ENTRY>& edls) const
{
edls.clear();
return DoAddonCall(
@@ -694,7 +697,7 @@ PVR_ERROR CPVRClient::GetEpgTagEdl(const std::shared_ptr<const CPVREpgInfoTag>&
m_clientCapabilities.SupportsEpgTagEdl());
}
-PVR_ERROR CPVRClient::GetChannelGroupsAmount(int& iGroups)
+PVR_ERROR CPVRClient::GetChannelGroupsAmount(int& iGroups) const
{
iGroups = -1;
return DoAddonCall(
@@ -705,7 +708,7 @@ PVR_ERROR CPVRClient::GetChannelGroupsAmount(int& iGroups)
m_clientCapabilities.SupportsChannelGroups());
}
-PVR_ERROR CPVRClient::GetChannelGroups(CPVRChannelGroups* groups)
+PVR_ERROR CPVRClient::GetChannelGroups(CPVRChannelGroups* groups) const
{
return DoAddonCall(__func__,
[this, groups](const AddonInstance* addon) {
@@ -718,7 +721,8 @@ PVR_ERROR CPVRClient::GetChannelGroups(CPVRChannelGroups* groups)
}
PVR_ERROR CPVRClient::GetChannelGroupMembers(
- CPVRChannelGroup* group, std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers)
+ CPVRChannelGroup* group,
+ std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers) const
{
return DoAddonCall(__func__,
[this, group, &groupMembers](const AddonInstance* addon) {
@@ -733,7 +737,7 @@ PVR_ERROR CPVRClient::GetChannelGroupMembers(
m_clientCapabilities.SupportsChannelGroups());
}
-PVR_ERROR CPVRClient::GetProvidersAmount(int& iProviders)
+PVR_ERROR CPVRClient::GetProvidersAmount(int& iProviders) const
{
iProviders = -1;
return DoAddonCall(__func__, [&iProviders](const AddonInstance* addon) {
@@ -741,7 +745,7 @@ PVR_ERROR CPVRClient::GetProvidersAmount(int& iProviders)
});
}
-PVR_ERROR CPVRClient::GetProviders(CPVRProvidersContainer& providers)
+PVR_ERROR CPVRClient::GetProviders(CPVRProvidersContainer& providers) const
{
return DoAddonCall(__func__,
[this, &providers](const AddonInstance* addon) {
@@ -753,7 +757,7 @@ PVR_ERROR CPVRClient::GetProviders(CPVRProvidersContainer& providers)
m_clientCapabilities.SupportsProviders());
}
-PVR_ERROR CPVRClient::GetChannelsAmount(int& iChannels)
+PVR_ERROR CPVRClient::GetChannelsAmount(int& iChannels) const
{
iChannels = -1;
return DoAddonCall(__func__, [&iChannels](const AddonInstance* addon) {
@@ -761,7 +765,8 @@ PVR_ERROR CPVRClient::GetChannelsAmount(int& iChannels)
});
}
-PVR_ERROR CPVRClient::GetChannels(bool radio, std::vector<std::shared_ptr<CPVRChannel>>& channels)
+PVR_ERROR CPVRClient::GetChannels(bool radio,
+ std::vector<std::shared_ptr<CPVRChannel>>& channels) const
{
return DoAddonCall(__func__,
[this, radio, &channels](const AddonInstance* addon) {
@@ -774,7 +779,7 @@ PVR_ERROR CPVRClient::GetChannels(bool radio, std::vector<std::shared_ptr<CPVRCh
(!radio && m_clientCapabilities.SupportsTV()));
}
-PVR_ERROR CPVRClient::GetRecordingsAmount(bool deleted, int& iRecordings)
+PVR_ERROR CPVRClient::GetRecordingsAmount(bool deleted, int& iRecordings) const
{
iRecordings = -1;
return DoAddonCall(
@@ -786,7 +791,7 @@ PVR_ERROR CPVRClient::GetRecordingsAmount(bool deleted, int& iRecordings)
(!deleted || m_clientCapabilities.SupportsRecordingsUndelete()));
}
-PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings* results, bool deleted)
+PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings* results, bool deleted) const
{
return DoAddonCall(__func__,
[this, results, deleted](const AddonInstance* addon) {
@@ -882,7 +887,8 @@ PVR_ERROR CPVRClient::SetRecordingLastPlayedPosition(const CPVRRecording& record
m_clientCapabilities.SupportsRecordingsLastPlayedPosition());
}
-PVR_ERROR CPVRClient::GetRecordingLastPlayedPosition(const CPVRRecording& recording, int& iPosition)
+PVR_ERROR CPVRClient::GetRecordingLastPlayedPosition(const CPVRRecording& recording,
+ int& iPosition) const
{
iPosition = -1;
return DoAddonCall(
@@ -896,7 +902,7 @@ PVR_ERROR CPVRClient::GetRecordingLastPlayedPosition(const CPVRRecording& record
}
PVR_ERROR CPVRClient::GetRecordingEdl(const CPVRRecording& recording,
- std::vector<PVR_EDL_ENTRY>& edls)
+ std::vector<PVR_EDL_ENTRY>& edls) const
{
edls.clear();
return DoAddonCall(
@@ -919,7 +925,7 @@ PVR_ERROR CPVRClient::GetRecordingEdl(const CPVRRecording& recording,
m_clientCapabilities.SupportsRecordingsEdl());
}
-PVR_ERROR CPVRClient::GetRecordingSize(const CPVRRecording& recording, int64_t& sizeInBytes)
+PVR_ERROR CPVRClient::GetRecordingSize(const CPVRRecording& recording, int64_t& sizeInBytes) const
{
return DoAddonCall(
__func__,
@@ -931,7 +937,7 @@ PVR_ERROR CPVRClient::GetRecordingSize(const CPVRRecording& recording, int64_t&
m_clientCapabilities.SupportsRecordingsSize());
}
-PVR_ERROR CPVRClient::GetTimersAmount(int& iTimers)
+PVR_ERROR CPVRClient::GetTimersAmount(int& iTimers) const
{
iTimers = -1;
return DoAddonCall(
@@ -942,7 +948,7 @@ PVR_ERROR CPVRClient::GetTimersAmount(int& iTimers)
m_clientCapabilities.SupportsTimers());
}
-PVR_ERROR CPVRClient::GetTimers(CPVRTimersContainer* results)
+PVR_ERROR CPVRClient::GetTimers(CPVRTimersContainer* results) const
{
return DoAddonCall(__func__,
[this, results](const AddonInstance* addon) {
@@ -1091,7 +1097,7 @@ PVR_ERROR CPVRClient::UpdateTimerTypes()
for (const auto& type : timerTypes)
{
const auto it = std::find_if(m_timertypes.cbegin(), m_timertypes.cend(),
- [&type](const std::shared_ptr<CPVRTimerType>& entry) {
+ [&type](const std::shared_ptr<const CPVRTimerType>& entry) {
return entry->GetTypeId() == type->GetTypeId();
});
if (it == m_timertypes.cend())
@@ -1110,7 +1116,7 @@ PVR_ERROR CPVRClient::UpdateTimerTypes()
return retVal;
}
-PVR_ERROR CPVRClient::GetStreamReadChunkSize(int& iChunkSize)
+PVR_ERROR CPVRClient::GetStreamReadChunkSize(int& iChunkSize) const
{
return DoAddonCall(
__func__,
@@ -1166,7 +1172,7 @@ PVR_ERROR CPVRClient::SeekTime(double time, bool backwards, double* startpts)
});
}
-PVR_ERROR CPVRClient::GetLiveStreamLength(int64_t& iLength)
+PVR_ERROR CPVRClient::GetLiveStreamLength(int64_t& iLength) const
{
iLength = -1;
return DoAddonCall(__func__, [&iLength](const AddonInstance* addon) {
@@ -1175,7 +1181,7 @@ PVR_ERROR CPVRClient::GetLiveStreamLength(int64_t& iLength)
});
}
-PVR_ERROR CPVRClient::GetRecordedStreamLength(int64_t& iLength)
+PVR_ERROR CPVRClient::GetRecordedStreamLength(int64_t& iLength) const
{
iLength = -1;
return DoAddonCall(__func__, [&iLength](const AddonInstance* addon) {
@@ -1184,7 +1190,7 @@ PVR_ERROR CPVRClient::GetRecordedStreamLength(int64_t& iLength)
});
}
-PVR_ERROR CPVRClient::SignalQuality(int channelUid, PVR_SIGNAL_STATUS& qualityinfo)
+PVR_ERROR CPVRClient::SignalQuality(int channelUid, PVR_SIGNAL_STATUS& qualityinfo) const
{
return DoAddonCall(__func__, [channelUid, &qualityinfo](const AddonInstance* addon) {
return addon->toAddon->GetSignalStatus(addon, channelUid, &qualityinfo);
@@ -1201,8 +1207,8 @@ PVR_ERROR CPVRClient::GetDescrambleInfo(int channelUid, PVR_DESCRAMBLE_INFO& des
m_clientCapabilities.SupportsDescrambleInfo());
}
-PVR_ERROR CPVRClient::GetChannelStreamProperties(const std::shared_ptr<CPVRChannel>& channel,
- CPVRStreamProperties& props)
+PVR_ERROR CPVRClient::GetChannelStreamProperties(const std::shared_ptr<const CPVRChannel>& channel,
+ CPVRStreamProperties& props) const
{
return DoAddonCall(__func__, [this, &channel, &props](const AddonInstance* addon) {
if (!CanPlayChannel(channel))
@@ -1224,8 +1230,8 @@ PVR_ERROR CPVRClient::GetChannelStreamProperties(const std::shared_ptr<CPVRChann
});
}
-PVR_ERROR CPVRClient::GetRecordingStreamProperties(const std::shared_ptr<CPVRRecording>& recording,
- CPVRStreamProperties& props)
+PVR_ERROR CPVRClient::GetRecordingStreamProperties(
+ const std::shared_ptr<const CPVRRecording>& recording, CPVRStreamProperties& props) const
{
return DoAddonCall(__func__, [this, &recording, &props](const AddonInstance* addon) {
if (!m_clientCapabilities.SupportsRecordings())
@@ -1247,7 +1253,7 @@ PVR_ERROR CPVRClient::GetRecordingStreamProperties(const std::shared_ptr<CPVRRec
});
}
-PVR_ERROR CPVRClient::GetStreamProperties(PVR_STREAM_PROPERTIES* props)
+PVR_ERROR CPVRClient::GetStreamProperties(PVR_STREAM_PROPERTIES* props) const
{
return DoAddonCall(__func__, [&props](const AddonInstance* addon) {
return addon->toAddon->GetStreamProperties(addon, props);
@@ -1379,13 +1385,13 @@ PVR_ERROR CPVRClient::DoAddonCall(const char* strFunctionName,
return error;
}
-bool CPVRClient::CanPlayChannel(const std::shared_ptr<CPVRChannel>& channel) const
+bool CPVRClient::CanPlayChannel(const std::shared_ptr<const CPVRChannel>& channel) const
{
return (m_bReadyToUse && ((m_clientCapabilities.SupportsTV() && !channel->IsRadio()) ||
(m_clientCapabilities.SupportsRadio() && channel->IsRadio())));
}
-PVR_ERROR CPVRClient::OpenLiveStream(const std::shared_ptr<CPVRChannel>& channel)
+PVR_ERROR CPVRClient::OpenLiveStream(const std::shared_ptr<const CPVRChannel>& channel)
{
if (!channel)
return PVR_ERROR_INVALID_PARAMETERS;
@@ -1410,7 +1416,7 @@ PVR_ERROR CPVRClient::OpenLiveStream(const std::shared_ptr<CPVRChannel>& channel
});
}
-PVR_ERROR CPVRClient::OpenRecordedStream(const std::shared_ptr<CPVRRecording>& recording)
+PVR_ERROR CPVRClient::OpenRecordedStream(const std::shared_ptr<const CPVRRecording>& recording)
{
if (!recording)
return PVR_ERROR_INVALID_PARAMETERS;
@@ -1487,7 +1493,7 @@ PVR_ERROR CPVRClient::CanSeekStream(bool& bCanSeek) const
});
}
-PVR_ERROR CPVRClient::GetStreamTimes(PVR_STREAM_TIMES* times)
+PVR_ERROR CPVRClient::GetStreamTimes(PVR_STREAM_TIMES* times) const
{
return DoAddonCall(__func__, [&times](const AddonInstance* addon) {
return addon->toAddon->GetStreamTimes(addon, times);
@@ -1529,16 +1535,16 @@ PVR_ERROR CPVRClient::OnPowerSavingDeactivated()
});
}
-std::shared_ptr<CPVRClientMenuHooks> CPVRClient::GetMenuHooks()
+std::shared_ptr<CPVRClientMenuHooks> CPVRClient::GetMenuHooks() const
{
if (!m_menuhooks)
- m_menuhooks.reset(new CPVRClientMenuHooks(ID()));
+ m_menuhooks = std::make_shared<CPVRClientMenuHooks>(ID());
return m_menuhooks;
}
PVR_ERROR CPVRClient::CallEpgTagMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVREpgInfoTag>& tag)
+ const std::shared_ptr<const CPVREpgInfoTag>& tag)
{
return DoAddonCall(__func__, [&hook, &tag](const AddonInstance* addon) {
CAddonEpgTag addonTag(tag);
@@ -1553,7 +1559,7 @@ PVR_ERROR CPVRClient::CallEpgTagMenuHook(const CPVRClientMenuHook& hook,
}
PVR_ERROR CPVRClient::CallChannelMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVRChannel>& channel)
+ const std::shared_ptr<const CPVRChannel>& channel)
{
return DoAddonCall(__func__, [&hook, &channel](const AddonInstance* addon) {
PVR_CHANNEL tag;
@@ -1569,7 +1575,7 @@ PVR_ERROR CPVRClient::CallChannelMenuHook(const CPVRClientMenuHook& hook,
}
PVR_ERROR CPVRClient::CallRecordingMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVRRecording>& recording,
+ const std::shared_ptr<const CPVRRecording>& recording,
bool bDeleted)
{
return DoAddonCall(__func__, [&hook, &recording, &bDeleted](const AddonInstance* addon) {
@@ -1586,7 +1592,7 @@ PVR_ERROR CPVRClient::CallRecordingMenuHook(const CPVRClientMenuHook& hook,
}
PVR_ERROR CPVRClient::CallTimerMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVRTimerInfoTag>& timer)
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer)
{
return DoAddonCall(__func__, [&hook, &timer](const AddonInstance* addon) {
PVR_TIMER tag;
diff --git a/xbmc/pvr/addons/PVRClient.h b/xbmc/pvr/addons/PVRClient.h
index c045431d25..12bcc4e57d 100644
--- a/xbmc/pvr/addons/PVRClient.h
+++ b/xbmc/pvr/addons/PVRClient.h
@@ -141,7 +141,7 @@ public:
* @param pProperties The properties.
* @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully.
*/
- PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties);
+ PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) const;
/*!
* @return The name reported by the backend.
@@ -175,7 +175,7 @@ public:
* @param iUsed The used disk space.
* @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully.
*/
- PVR_ERROR GetDriveSpace(uint64_t& iTotal, uint64_t& iUsed);
+ PVR_ERROR GetDriveSpace(uint64_t& iTotal, uint64_t& iUsed) const;
/*!
* @brief Start a channel scan on the server.
@@ -188,28 +188,28 @@ public:
* @param channel The channel to add
* @return PVR_ERROR_NO_ERROR if the add has been fetched successfully.
*/
- PVR_ERROR OpenDialogChannelAdd(const std::shared_ptr<CPVRChannel>& channel);
+ PVR_ERROR OpenDialogChannelAdd(const std::shared_ptr<const CPVRChannel>& channel);
/*!
* @brief Request the client to open dialog about given channel settings
* @param channel The channel to edit
* @return PVR_ERROR_NO_ERROR if the edit has been fetched successfully.
*/
- PVR_ERROR OpenDialogChannelSettings(const std::shared_ptr<CPVRChannel>& channel);
+ PVR_ERROR OpenDialogChannelSettings(const std::shared_ptr<const CPVRChannel>& channel);
/*!
* @brief Request the client to delete given channel
* @param channel The channel to delete
* @return PVR_ERROR_NO_ERROR if the delete has been fetched successfully.
*/
- PVR_ERROR DeleteChannel(const std::shared_ptr<CPVRChannel>& channel);
+ PVR_ERROR DeleteChannel(const std::shared_ptr<const CPVRChannel>& channel);
/*!
* @brief Request the client to rename given channel
* @param channel The channel to rename
* @return PVR_ERROR_NO_ERROR if the rename has been fetched successfully.
*/
- PVR_ERROR RenameChannel(const std::shared_ptr<CPVRChannel>& channel);
+ PVR_ERROR RenameChannel(const std::shared_ptr<const CPVRChannel>& channel);
/*
* @brief Check if an epg tag can be recorded
@@ -236,8 +236,8 @@ public:
* @param props The container to be filled with the stream properties.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetEpgTagStreamProperties(const std::shared_ptr<CPVREpgInfoTag>& tag,
- CPVRStreamProperties& props);
+ PVR_ERROR GetEpgTagStreamProperties(const std::shared_ptr<const CPVREpgInfoTag>& tag,
+ CPVRStreamProperties& props) const;
//@}
/** @name PVR EPG methods */
@@ -251,7 +251,7 @@ public:
* @param end The end time to use.
* @return PVR_ERROR_NO_ERROR if the table has been fetched successfully.
*/
- PVR_ERROR GetEPGForChannel(int iChannelUid, CPVREpg* epg, time_t start, time_t end);
+ PVR_ERROR GetEPGForChannel(int iChannelUid, CPVREpg* epg, time_t start, time_t end) const;
/*!
* @brief Tell the client the past time frame to use when notifying epg events back
@@ -294,14 +294,14 @@ public:
* @param iGroups The total amount of channel groups on the server or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetChannelGroupsAmount(int& iGroups);
+ PVR_ERROR GetChannelGroupsAmount(int& iGroups) const;
/*!
* @brief Request the list of all channel groups from the backend.
* @param groups The groups container to get the groups for.
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
- PVR_ERROR GetChannelGroups(CPVRChannelGroups* groups);
+ PVR_ERROR GetChannelGroups(CPVRChannelGroups* groups) const;
/*!
* @brief Request the list of all group members from the backend.
@@ -310,7 +310,8 @@ public:
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
PVR_ERROR GetChannelGroupMembers(
- CPVRChannelGroup* group, std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers);
+ CPVRChannelGroup* group,
+ std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers) const;
//@}
/** @name PVR channel methods */
@@ -321,7 +322,7 @@ public:
* @param iChannels The total amount of channels on the server or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetChannelsAmount(int& iChannels);
+ PVR_ERROR GetChannelsAmount(int& iChannels) const;
/*!
* @brief Request the list of all channels from the backend.
@@ -329,21 +330,21 @@ public:
* @param channels The container for the channels.
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
- PVR_ERROR GetChannels(bool bRadio, std::vector<std::shared_ptr<CPVRChannel>>& channels);
+ PVR_ERROR GetChannels(bool bRadio, std::vector<std::shared_ptr<CPVRChannel>>& channels) const;
/*!
* @brief Get the total amount of providers from the backend.
* @param iChannels The total amount of channels on the server or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetProvidersAmount(int& iProviders);
+ PVR_ERROR GetProvidersAmount(int& iProviders) const;
/*!
* @brief Request the list of all providers from the backend.
* @param providers The providers list to add the providers to.
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
- PVR_ERROR GetProviders(CPVRProvidersContainer& providers);
+ PVR_ERROR GetProviders(CPVRProvidersContainer& providers) const;
//@}
/** @name PVR recording methods */
@@ -355,7 +356,7 @@ public:
* @param iRecordings The total amount of recordings on the server or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetRecordingsAmount(bool deleted, int& iRecordings);
+ PVR_ERROR GetRecordingsAmount(bool deleted, int& iRecordings) const;
/*!
* @brief Request the list of all recordings from the backend.
@@ -363,7 +364,7 @@ public:
* @param deleted True to return deleted recordings.
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
- PVR_ERROR GetRecordings(CPVRRecordings* results, bool deleted);
+ PVR_ERROR GetRecordings(CPVRRecordings* results, bool deleted) const;
/*!
* @brief Delete a recording on the backend.
@@ -421,7 +422,7 @@ public:
* @param iPosition The last watched position in seconds or -1 on error
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetRecordingLastPlayedPosition(const CPVRRecording& recording, int& iPosition);
+ PVR_ERROR GetRecordingLastPlayedPosition(const CPVRRecording& recording, int& iPosition) const;
/*!
* @brief Retrieve the edit decision list (EDL) from the backend.
@@ -429,7 +430,7 @@ public:
* @param edls The edit decision list (empty on error).
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetRecordingEdl(const CPVRRecording& recording, std::vector<PVR_EDL_ENTRY>& edls);
+ PVR_ERROR GetRecordingEdl(const CPVRRecording& recording, std::vector<PVR_EDL_ENTRY>& edls) const;
/*!
* @brief Retrieve the size of a recording on the backend.
@@ -437,7 +438,7 @@ public:
* @param sizeInBytes The size in bytes
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetRecordingSize(const CPVRRecording& recording, int64_t& sizeInBytes);
+ PVR_ERROR GetRecordingSize(const CPVRRecording& recording, int64_t& sizeInBytes) const;
/*!
* @brief Retrieve the edit decision list (EDL) from the backend.
@@ -446,7 +447,7 @@ public:
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
PVR_ERROR GetEpgTagEdl(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
- std::vector<PVR_EDL_ENTRY>& edls);
+ std::vector<PVR_EDL_ENTRY>& edls) const;
//@}
/** @name PVR timer methods */
@@ -457,14 +458,14 @@ public:
* @param iTimers The total amount of timers on the backend or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetTimersAmount(int& iTimers);
+ PVR_ERROR GetTimersAmount(int& iTimers) const;
/*!
* @brief Request the list of all timers from the backend.
* @param results The container to store the result in.
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
- PVR_ERROR GetTimers(CPVRTimersContainer* results);
+ PVR_ERROR GetTimers(CPVRTimersContainer* results) const;
/*!
* @brief Add a timer on the backend.
@@ -509,7 +510,7 @@ public:
* @param channel The channel to stream.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR OpenLiveStream(const std::shared_ptr<CPVRChannel>& channel);
+ PVR_ERROR OpenLiveStream(const std::shared_ptr<const CPVRChannel>& channel);
/*!
* @brief Close an open live stream.
@@ -540,7 +541,7 @@ public:
* @param iLength The total length of the stream that's currently being read or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetLiveStreamLength(int64_t& iLength);
+ PVR_ERROR GetLiveStreamLength(int64_t& iLength) const;
/*!
* @brief (Un)Pause a stream.
@@ -555,7 +556,7 @@ public:
* @param qualityinfo The signal quality.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR SignalQuality(int channelUid, PVR_SIGNAL_STATUS& qualityinfo);
+ PVR_ERROR SignalQuality(int channelUid, PVR_SIGNAL_STATUS& qualityinfo) const;
/*!
* @brief Get the descramble information of the stream that's currently open.
@@ -571,8 +572,8 @@ public:
* @param props The container to be filled with the stream properties.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetChannelStreamProperties(const std::shared_ptr<CPVRChannel>& channel,
- CPVRStreamProperties& props);
+ PVR_ERROR GetChannelStreamProperties(const std::shared_ptr<const CPVRChannel>& channel,
+ CPVRStreamProperties& props) const;
/*!
* @brief Check whether PVR backend supports pausing the currently playing stream
@@ -623,7 +624,7 @@ public:
* @param recording The recording to open.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR OpenRecordedStream(const std::shared_ptr<CPVRRecording>& recording);
+ PVR_ERROR OpenRecordedStream(const std::shared_ptr<const CPVRRecording>& recording);
/*!
* @brief Close an open recording stream.
@@ -654,7 +655,7 @@ public:
* @param iLength The total length of the stream that's currently being read or -1 on error.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetRecordedStreamLength(int64_t& iLength);
+ PVR_ERROR GetRecordedStreamLength(int64_t& iLength) const;
/*!
* @brief Fill the given container with the properties required for playback of the given recording. Values are obtained from the PVR backend.
@@ -662,8 +663,8 @@ public:
* @param props The container to be filled with the stream properties.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetRecordingStreamProperties(const std::shared_ptr<CPVRRecording>& recording,
- CPVRStreamProperties& props);
+ PVR_ERROR GetRecordingStreamProperties(const std::shared_ptr<const CPVRRecording>& recording,
+ CPVRStreamProperties& props) const;
//@}
/** @name PVR demultiplexer methods */
@@ -708,13 +709,13 @@ public:
* @param times The stream times.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* times);
+ PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* times) const;
/*!
* @brief Get the client's menu hooks.
* @return The hooks. Guaranteed never to be nullptr.
*/
- std::shared_ptr<CPVRClientMenuHooks> GetMenuHooks();
+ std::shared_ptr<CPVRClientMenuHooks> GetMenuHooks() const;
/*!
* @brief Call one of the EPG tag menu hooks of the client.
@@ -723,7 +724,7 @@ public:
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
PVR_ERROR CallEpgTagMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVREpgInfoTag>& tag);
+ const std::shared_ptr<const CPVREpgInfoTag>& tag);
/*!
* @brief Call one of the channel menu hooks of the client.
@@ -732,7 +733,7 @@ public:
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
PVR_ERROR CallChannelMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVRChannel>& channel);
+ const std::shared_ptr<const CPVRChannel>& channel);
/*!
* @brief Call one of the recording menu hooks of the client.
@@ -742,7 +743,7 @@ public:
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
PVR_ERROR CallRecordingMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVRRecording>& recording,
+ const std::shared_ptr<const CPVRRecording>& recording,
bool bDeleted);
/*!
@@ -752,7 +753,7 @@ public:
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
PVR_ERROR CallTimerMenuHook(const CPVRClientMenuHook& hook,
- const std::shared_ptr<CPVRTimerInfoTag>& timer);
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer);
/*!
* @brief Call one of the settings menu hooks of the client.
@@ -787,7 +788,7 @@ public:
* @param iChunkSize the chunk size in bytes.
* @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
*/
- PVR_ERROR GetStreamReadChunkSize(int& iChunkSize);
+ PVR_ERROR GetStreamReadChunkSize(int& iChunkSize) const;
/*!
* @brief Get the interface table used between addon and Kodi.
@@ -828,7 +829,7 @@ private:
* @param channel The channel to check.
* @return True when it can be played, false otherwise.
*/
- bool CanPlayChannel(const std::shared_ptr<CPVRChannel>& channel) const;
+ bool CanPlayChannel(const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @brief Stop this instance, if it is currently running.
@@ -1056,7 +1057,7 @@ private:
std::string m_strConnectionString; /*!< the cached connection string */
std::string m_strBackendHostname; /*!< the cached backend hostname */
CPVRClientCapabilities m_clientCapabilities; /*!< the cached add-on's capabilities */
- std::shared_ptr<CPVRClientMenuHooks> m_menuhooks; /*!< the menu hooks for this add-on */
+ mutable std::shared_ptr<CPVRClientMenuHooks> m_menuhooks; /*!< the menu hooks for this add-on */
/* stored strings to make sure const char* members in AddonProperties_PVR stay valid */
std::string m_strUserPath; /*!< @brief translated path to the user profile */
diff --git a/xbmc/pvr/addons/PVRClientCapabilities.cpp b/xbmc/pvr/addons/PVRClientCapabilities.cpp
index 8650465ce8..7c09fd55ff 100644
--- a/xbmc/pvr/addons/PVRClientCapabilities.cpp
+++ b/xbmc/pvr/addons/PVRClientCapabilities.cpp
@@ -13,20 +13,21 @@
#include <algorithm>
#include <iterator>
+#include <memory>
using namespace PVR;
CPVRClientCapabilities::CPVRClientCapabilities(const CPVRClientCapabilities& other)
{
if (other.m_addonCapabilities)
- m_addonCapabilities.reset(new PVR_ADDON_CAPABILITIES(*other.m_addonCapabilities));
+ m_addonCapabilities = std::make_unique<PVR_ADDON_CAPABILITIES>(*other.m_addonCapabilities);
InitRecordingsLifetimeValues();
}
const CPVRClientCapabilities& CPVRClientCapabilities::operator=(const CPVRClientCapabilities& other)
{
if (other.m_addonCapabilities)
- m_addonCapabilities.reset(new PVR_ADDON_CAPABILITIES(*other.m_addonCapabilities));
+ m_addonCapabilities = std::make_unique<PVR_ADDON_CAPABILITIES>(*other.m_addonCapabilities);
InitRecordingsLifetimeValues();
return *this;
}
@@ -34,7 +35,7 @@ const CPVRClientCapabilities& CPVRClientCapabilities::operator=(const CPVRClient
const CPVRClientCapabilities& CPVRClientCapabilities::operator=(
const PVR_ADDON_CAPABILITIES& addonCapabilities)
{
- m_addonCapabilities.reset(new PVR_ADDON_CAPABILITIES(addonCapabilities));
+ m_addonCapabilities = std::make_unique<PVR_ADDON_CAPABILITIES>(addonCapabilities);
InitRecordingsLifetimeValues();
return *this;
}
diff --git a/xbmc/pvr/addons/PVRClientMenuHooks.cpp b/xbmc/pvr/addons/PVRClientMenuHooks.cpp
index 12f150a1c5..9d58f1a915 100644
--- a/xbmc/pvr/addons/PVRClientMenuHooks.cpp
+++ b/xbmc/pvr/addons/PVRClientMenuHooks.cpp
@@ -13,6 +13,8 @@
#include "pvr/PVRContextMenus.h"
#include "utils/log.h"
+#include <memory>
+
namespace PVR
{
@@ -100,7 +102,7 @@ std::string CPVRClientMenuHook::GetLabel() const
void CPVRClientMenuHooks::AddHook(const PVR_MENUHOOK& addonHook)
{
if (!m_hooks)
- m_hooks.reset(new std::vector<CPVRClientMenuHook>());
+ m_hooks = std::make_unique<std::vector<CPVRClientMenuHook>>();
const CPVRClientMenuHook hook(m_addonId, addonHook);
m_hooks->emplace_back(hook);
diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp
index b0c74f013e..b2261d376d 100644
--- a/xbmc/pvr/addons/PVRClients.cpp
+++ b/xbmc/pvr/addons/PVRClients.cpp
@@ -135,7 +135,7 @@ void CPVRClients::UpdateClients(
// determine actual enabled state of instance
if (instanceEnabled && instanceId != ADDON_SINGLETON_INSTANCE_ID)
{
- const std::shared_ptr<CPVRClient> client = GetClient(clientId);
+ const std::shared_ptr<const CPVRClient> client = GetClient(clientId);
instanceEnabled = client ? client->IsEnabled() : false;
}
@@ -377,7 +377,7 @@ std::vector<CVariant> CPVRClients::GetClientProviderInfos() const
return clientProviderInfos;
}
-int CPVRClients::GetFirstCreatedClientID()
+int CPVRClients::GetFirstCreatedClientID() const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const auto it = std::find_if(m_clientMap.cbegin(), m_clientMap.cend(),
@@ -431,7 +431,7 @@ int CPVRClients::EnabledClientAmount() const
bool CPVRClients::IsEnabledClient(int clientId) const
{
- const std::shared_ptr<CPVRClient> client = GetClient(clientId);
+ const std::shared_ptr<const CPVRClient> client = GetClient(clientId);
return client && !CServiceBroker::GetAddonMgr().IsAddonDisabled(client->ID());
}
@@ -512,7 +512,7 @@ bool CPVRClients::GetAddonsWithStatus(
for (const auto& addon : addons)
{
bool enabled = !CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID());
- addonsWithStatus.emplace_back(std::make_pair(addon, enabled));
+ addonsWithStatus.emplace_back(addon, enabled);
if (!foundChangedAddon && addon->ID() == changedAddonId)
foundChangedAddon = true;
@@ -539,8 +539,7 @@ std::vector<std::pair<ADDON::AddonInstanceId, bool>> CPVRClients::GetInstanceIds
if (std::find(instanceIds.begin(), instanceIds.end(), knownInstanceId) == instanceIds.end())
{
// instance was removed
- instanceIdsWithStatus.emplace_back(
- std::pair<ADDON::AddonInstanceId, bool>(knownInstanceId, false));
+ instanceIdsWithStatus.emplace_back(knownInstanceId, false);
}
}
@@ -555,48 +554,50 @@ std::vector<SBackend> CPVRClients::GetBackendProperties() const
{
std::vector<SBackend> backendProperties;
- ForCreatedClients(__FUNCTION__, [&backendProperties](const std::shared_ptr<CPVRClient>& client) {
- SBackend properties;
+ ForCreatedClients(
+ __FUNCTION__, [&backendProperties](const std::shared_ptr<const CPVRClient>& client) {
+ SBackend properties;
- if (client->GetDriveSpace(properties.diskTotal, properties.diskUsed) == PVR_ERROR_NO_ERROR)
- {
- properties.diskTotal *= 1024;
- properties.diskUsed *= 1024;
- }
+ if (client->GetDriveSpace(properties.diskTotal, properties.diskUsed) == PVR_ERROR_NO_ERROR)
+ {
+ properties.diskTotal *= 1024;
+ properties.diskUsed *= 1024;
+ }
- int iAmount = 0;
- if (client->GetProvidersAmount(iAmount) == PVR_ERROR_NO_ERROR)
- properties.numProviders = iAmount;
- if (client->GetChannelGroupsAmount(iAmount) == PVR_ERROR_NO_ERROR)
- properties.numChannelGroups = iAmount;
- if (client->GetChannelsAmount(iAmount) == PVR_ERROR_NO_ERROR)
- properties.numChannels = iAmount;
- if (client->GetTimersAmount(iAmount) == PVR_ERROR_NO_ERROR)
- properties.numTimers = iAmount;
- if (client->GetRecordingsAmount(false, iAmount) == PVR_ERROR_NO_ERROR)
- properties.numRecordings = iAmount;
- if (client->GetRecordingsAmount(true, iAmount) == PVR_ERROR_NO_ERROR)
- properties.numDeletedRecordings = iAmount;
- properties.name = client->GetBackendName();
- properties.version = client->GetBackendVersion();
- properties.host = client->GetConnectionString();
-
- backendProperties.emplace_back(properties);
- return PVR_ERROR_NO_ERROR;
- });
+ int iAmount = 0;
+ if (client->GetProvidersAmount(iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numProviders = iAmount;
+ if (client->GetChannelGroupsAmount(iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numChannelGroups = iAmount;
+ if (client->GetChannelsAmount(iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numChannels = iAmount;
+ if (client->GetTimersAmount(iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numTimers = iAmount;
+ if (client->GetRecordingsAmount(false, iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numRecordings = iAmount;
+ if (client->GetRecordingsAmount(true, iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numDeletedRecordings = iAmount;
+ properties.name = client->GetBackendName();
+ properties.version = client->GetBackendVersion();
+ properties.host = client->GetConnectionString();
+
+ backendProperties.emplace_back(properties);
+ return PVR_ERROR_NO_ERROR;
+ });
return backendProperties;
}
bool CPVRClients::GetTimers(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRTimersContainer* timers,
- std::vector<int>& failedClients)
+ std::vector<int>& failedClients) const
{
- return ForClients(__FUNCTION__, clients,
- [timers](const std::shared_ptr<CPVRClient>& client) {
- return client->GetTimers(timers);
- },
- failedClients) == PVR_ERROR_NO_ERROR;
+ return ForClients(
+ __FUNCTION__, clients,
+ [timers](const std::shared_ptr<const CPVRClient>& client) {
+ return client->GetTimers(timers);
+ },
+ failedClients) == PVR_ERROR_NO_ERROR;
}
PVR_ERROR CPVRClients::UpdateTimerTypes(const std::vector<std::shared_ptr<CPVRClient>>& clients,
@@ -629,13 +630,14 @@ const std::vector<std::shared_ptr<CPVRTimerType>> CPVRClients::GetTimerTypes() c
PVR_ERROR CPVRClients::GetRecordings(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRRecordings* recordings,
bool deleted,
- std::vector<int>& failedClients)
+ std::vector<int>& failedClients) const
{
- return ForClients(__FUNCTION__, clients,
- [recordings, deleted](const std::shared_ptr<CPVRClient>& client) {
- return client->GetRecordings(recordings, deleted);
- },
- failedClients);
+ return ForClients(
+ __FUNCTION__, clients,
+ [recordings, deleted](const std::shared_ptr<const CPVRClient>& client) {
+ return client->GetRecordings(recordings, deleted);
+ },
+ failedClients);
}
PVR_ERROR CPVRClients::DeleteAllRecordingsFromTrash()
@@ -662,48 +664,52 @@ PVR_ERROR CPVRClients::SetEPGMaxFutureDays(int iFutureDays)
PVR_ERROR CPVRClients::GetChannels(const std::vector<std::shared_ptr<CPVRClient>>& clients,
bool bRadio,
std::vector<std::shared_ptr<CPVRChannel>>& channels,
- std::vector<int>& failedClients)
+ std::vector<int>& failedClients) const
{
- return ForClients(__FUNCTION__, clients,
- [bRadio, &channels](const std::shared_ptr<CPVRClient>& client) {
- return client->GetChannels(bRadio, channels);
- },
- failedClients);
+ return ForClients(
+ __FUNCTION__, clients,
+ [bRadio, &channels](const std::shared_ptr<const CPVRClient>& client) {
+ return client->GetChannels(bRadio, channels);
+ },
+ failedClients);
}
PVR_ERROR CPVRClients::GetProviders(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRProvidersContainer* providers,
- std::vector<int>& failedClients)
+ std::vector<int>& failedClients) const
{
- return ForClients(__FUNCTION__, clients,
- [providers](const std::shared_ptr<CPVRClient>& client) {
- return client->GetProviders(*providers);
- },
- failedClients);
+ return ForClients(
+ __FUNCTION__, clients,
+ [providers](const std::shared_ptr<const CPVRClient>& client) {
+ return client->GetProviders(*providers);
+ },
+ failedClients);
}
PVR_ERROR CPVRClients::GetChannelGroups(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRChannelGroups* groups,
- std::vector<int>& failedClients)
+ std::vector<int>& failedClients) const
{
- return ForClients(__FUNCTION__, clients,
- [groups](const std::shared_ptr<CPVRClient>& client) {
- return client->GetChannelGroups(groups);
- },
- failedClients);
+ return ForClients(
+ __FUNCTION__, clients,
+ [groups](const std::shared_ptr<const CPVRClient>& client) {
+ return client->GetChannelGroups(groups);
+ },
+ failedClients);
}
PVR_ERROR CPVRClients::GetChannelGroupMembers(
const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRChannelGroup* group,
std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers,
- std::vector<int>& failedClients)
+ std::vector<int>& failedClients) const
{
- return ForClients(__FUNCTION__, clients,
- [group, &groupMembers](const std::shared_ptr<CPVRClient>& client) {
- return client->GetChannelGroupMembers(group, groupMembers);
- },
- failedClients);
+ return ForClients(
+ __FUNCTION__, clients,
+ [group, &groupMembers](const std::shared_ptr<const CPVRClient>& client) {
+ return client->GetChannelGroupMembers(group, groupMembers);
+ },
+ failedClients);
}
std::vector<std::shared_ptr<CPVRClient>> CPVRClients::GetClientsSupportingChannelScan() const
@@ -886,7 +892,7 @@ void CPVRClients::ConnectionStateChange(CPVRClient* client,
namespace
{
-void LogClientWarning(const char* strFunctionName, const std::shared_ptr<CPVRClient>& client)
+void LogClientWarning(const char* strFunctionName, const std::shared_ptr<const CPVRClient>& client)
{
if (client->IgnoreClient())
CLog::Log(LOGWARNING, "{}: Not calling add-on '{}'. Add-on not (yet) connected.",
diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h
index f5071ebb48..edc720ffc5 100644
--- a/xbmc/pvr/addons/PVRClients.h
+++ b/xbmc/pvr/addons/PVRClients.h
@@ -150,7 +150,7 @@ struct SBackend
* @brief Get the ID of the first created client.
* @return the ID or -1 if no clients are created;
*/
- int GetFirstCreatedClientID();
+ int GetFirstCreatedClientID() const;
/*!
* @brief Check whether there are any created, but not (yet) connected clients.
@@ -208,7 +208,7 @@ struct SBackend
*/
bool GetTimers(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRTimersContainer* timers,
- std::vector<int>& failedClients);
+ std::vector<int>& failedClients) const;
/*!
* @brief Update all timer types from the given clients
@@ -241,7 +241,7 @@ struct SBackend
PVR_ERROR GetRecordings(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRRecordings* recordings,
bool deleted,
- std::vector<int>& failedClients);
+ std::vector<int>& failedClients) const;
/*!
* @brief Delete all "soft" deleted recordings permanently on the backend.
@@ -300,7 +300,7 @@ struct SBackend
PVR_ERROR GetChannels(const std::vector<std::shared_ptr<CPVRClient>>& clients,
bool bRadio,
std::vector<std::shared_ptr<CPVRChannel>>& channels,
- std::vector<int>& failedClients);
+ std::vector<int>& failedClients) const;
/*!
* @brief Get all providers from backends.
@@ -311,7 +311,7 @@ struct SBackend
*/
PVR_ERROR GetProviders(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRProvidersContainer* providers,
- std::vector<int>& failedClients);
+ std::vector<int>& failedClients) const;
/*!
* @brief Get all channel groups from the given clients.
@@ -322,7 +322,7 @@ struct SBackend
*/
PVR_ERROR GetChannelGroups(const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRChannelGroups* groups,
- std::vector<int>& failedClients);
+ std::vector<int>& failedClients) const;
/*!
* @brief Get all group members of a channel group from the given clients.
@@ -336,7 +336,7 @@ struct SBackend
const std::vector<std::shared_ptr<CPVRClient>>& clients,
CPVRChannelGroup* group,
std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers,
- std::vector<int>& failedClients);
+ std::vector<int>& failedClients) const;
/*!
* @brief Get a list of clients providing a channel scan dialog.
diff --git a/xbmc/pvr/channels/PVRChannel.cpp b/xbmc/pvr/channels/PVRChannel.cpp
index a65f323474..783d89869c 100644
--- a/xbmc/pvr/channels/PVRChannel.cpp
+++ b/xbmc/pvr/channels/PVRChannel.cpp
@@ -136,7 +136,7 @@ bool CPVRChannel::QueueDelete()
if (!database)
return bReturn;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
ResetEPG();
@@ -205,7 +205,7 @@ void CPVRChannel::ResetEPG()
epgToUnsubscribe->Events().Unsubscribe(this);
}
-bool CPVRChannel::UpdateFromClient(const std::shared_ptr<CPVRChannel>& channel)
+bool CPVRChannel::UpdateFromClient(const std::shared_ptr<const CPVRChannel>& channel)
{
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -262,7 +262,7 @@ bool CPVRChannel::SetChannelID(int iChannelId)
{
m_iChannelId = iChannelId;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
epg->GetChannelData()->SetChannelId(m_iChannelId);
@@ -300,7 +300,7 @@ bool CPVRChannel::SetLocked(bool bIsLocked)
{
m_bIsLocked = bIsLocked;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
epg->GetChannelData()->SetLocked(m_bIsLocked);
@@ -357,7 +357,7 @@ bool CPVRChannel::SetIconPath(const std::string& strIconPath, bool bIsUserSetIco
m_iconPath.SetClientImage(strIconPath);
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
epg->GetChannelData()->SetChannelIconPath(strIconPath);
@@ -380,7 +380,7 @@ bool CPVRChannel::SetChannelName(const std::string& strChannelName, bool bIsUser
m_strChannelName = strName;
m_bIsUserSetName = bIsUserSetName;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
epg->GetChannelData()->SetChannelName(m_strChannelName);
@@ -391,16 +391,17 @@ bool CPVRChannel::SetChannelName(const std::string& strChannelName, bool bIsUser
return false;
}
-bool CPVRChannel::SetLastWatched(time_t iLastWatched)
+bool CPVRChannel::SetLastWatched(time_t lastWatched, int groupId)
{
{
std::unique_lock<CCriticalSection> lock(m_critSection);
- m_iLastWatched = iLastWatched;
+ m_iLastWatched = lastWatched;
+ m_lastWatchedGroupId = groupId;
}
const std::shared_ptr<CPVRDatabase> database = CServiceBroker::GetPVRManager().GetTVDatabase();
if (database)
- return database->UpdateLastWatched(*this);
+ return database->UpdateLastWatched(*this, groupId);
return false;
}
@@ -534,7 +535,7 @@ bool CPVRChannel::SetClientProviderUid(int iClientProviderUid)
std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannel::GetEpgTags() const
{
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (!epg)
{
CLog::LogFC(LOGDEBUG, LOGPVR, "Cannot get EPG for channel '{}'", m_strChannelName);
@@ -550,7 +551,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannel::GetEPGTimeline(
const CDateTime& minEventEnd,
const CDateTime& maxEventStart) const
{
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
{
return epg->GetTimeline(timelineStart, timelineEnd, minEventEnd, maxEventStart);
@@ -566,7 +567,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannel::GetEPGTimeline(
std::shared_ptr<CPVREpgInfoTag> CPVRChannel::CreateEPGGapTag(const CDateTime& start,
const CDateTime& end) const
{
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
return std::make_shared<CPVREpgInfoTag>(epg->GetChannelData(), epg->EpgID(), start, end, true);
else
@@ -577,7 +578,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVRChannel::CreateEPGGapTag(const CDateTime& st
std::shared_ptr<CPVREpgInfoTag> CPVRChannel::GetEPGNow() const
{
std::shared_ptr<CPVREpgInfoTag> tag;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
tag = epg->GetTagNow();
@@ -587,7 +588,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVRChannel::GetEPGNow() const
std::shared_ptr<CPVREpgInfoTag> CPVRChannel::GetEPGNext() const
{
std::shared_ptr<CPVREpgInfoTag> tag;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
tag = epg->GetTagNext();
@@ -597,7 +598,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVRChannel::GetEPGNext() const
std::shared_ptr<CPVREpgInfoTag> CPVRChannel::GetEPGPrevious() const
{
std::shared_ptr<CPVREpgInfoTag> tag;
- const std::shared_ptr<CPVREpg> epg = GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = GetEPG();
if (epg)
tag = epg->GetTagPrevious();
@@ -718,6 +719,12 @@ bool CPVRChannel::IsUserSetHidden() const
return m_bIsUserSetHidden;
}
+int CPVRChannel::LastWatchedGroupId() const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return m_lastWatchedGroupId;
+}
+
std::string CPVRChannel::ChannelName() const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -809,7 +816,8 @@ std::string CPVRChannel::EPGScraper() const
bool CPVRChannel::CanRecord() const
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_iClientId);
return client && client->GetClientCapabilities().SupportsRecordings() &&
client->GetClientCapabilities().SupportsTimers();
}
diff --git a/xbmc/pvr/channels/PVRChannel.h b/xbmc/pvr/channels/PVRChannel.h
index 8ff50547bb..26cbc2d193 100644
--- a/xbmc/pvr/channels/PVRChannel.h
+++ b/xbmc/pvr/channels/PVRChannel.h
@@ -71,7 +71,7 @@ public:
* @param channel The new channel data.
* @return True if something changed, false otherwise.
*/
- bool UpdateFromClient(const std::shared_ptr<CPVRChannel>& channel);
+ bool UpdateFromClient(const std::shared_ptr<const CPVRChannel>& channel);
/*!
* @brief Persists the changes in the database.
@@ -182,6 +182,11 @@ public:
bool IsUserSetHidden() const;
/*!
+ * @return the id of the channel group the channel was watched from the last time; -1 if unknown.
+ */
+ int LastWatchedGroupId() const;
+
+ /*!
* @brief Set the path to the icon for this channel.
* @param strIconPath The new path.
* @param bIsUserSetIcon true if user changed the icon via GUI, false otherwise.
@@ -208,11 +213,12 @@ public:
time_t LastWatched() const;
/*!
- * @brief Last time channel has been watched
- * @param iLastWatched The new value.
+ * @brief Set the last time the channel has been watched and the channel group used to watch.
+ * @param lastWatched The new last watched time value.
+ * @param groupId the id of the group used to watch the channel.
* @return True if the something changed, false otherwise.
*/
- bool SetLastWatched(time_t iLastWatched);
+ bool SetLastWatched(time_t lastWatched, int groupId);
/*!
* @brief Check whether this channel has unpersisted data changes.
@@ -543,6 +549,8 @@ private:
int m_iClientOrder = 0; /*!< the order from this channels group member */
int m_iClientProviderUid =
PVR_PROVIDER_INVALID_UID; /*!< the unique id for this provider from the client */
+ int m_lastWatchedGroupId{
+ -1}; /*!< the id of the channel group the channel was watched from the last time */
//@}
mutable CCriticalSection m_critSection;
diff --git a/xbmc/pvr/channels/PVRChannelGroup.cpp b/xbmc/pvr/channels/PVRChannelGroup.cpp
index 060b01f298..2bb1608a39 100644
--- a/xbmc/pvr/channels/PVRChannelGroup.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroup.cpp
@@ -38,7 +38,7 @@
using namespace PVR;
CPVRChannelGroup::CPVRChannelGroup(const CPVRChannelsPath& path,
- const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup)
+ const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup)
: m_allChannelsGroup(allChannelsGroup), m_path(path)
{
GetSettings()->RegisterCallback(this);
@@ -182,8 +182,8 @@ void CPVRChannelGroup::SetPath(const CPVRChannelsPath& path)
struct sortByClientChannelNumber
{
- bool operator()(const std::shared_ptr<CPVRChannelGroupMember>& channel1,
- const std::shared_ptr<CPVRChannelGroupMember>& channel2) const
+ bool operator()(const std::shared_ptr<const CPVRChannelGroupMember>& channel1,
+ const std::shared_ptr<const CPVRChannelGroupMember>& channel2) const
{
if (channel1->ClientPriority() == channel2->ClientPriority())
{
@@ -198,8 +198,8 @@ struct sortByClientChannelNumber
struct sortByChannelNumber
{
- bool operator()(const std::shared_ptr<CPVRChannelGroupMember>& channel1,
- const std::shared_ptr<CPVRChannelGroupMember>& channel2) const
+ bool operator()(const std::shared_ptr<const CPVRChannelGroupMember>& channel1,
+ const std::shared_ptr<const CPVRChannelGroupMember>& channel2) const
{
return channel1->ChannelNumber() < channel2->ChannelNumber();
}
@@ -247,7 +247,7 @@ void CPVRChannelGroup::UpdateClientPriorities()
bool CPVRChannelGroup::UpdateMembersClientPriority()
{
- const std::shared_ptr<CPVRClients> clients = CServiceBroker::GetPVRManager().Clients();
+ const std::shared_ptr<const CPVRClients> clients = CServiceBroker::GetPVRManager().Clients();
bool bChanged = false;
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -259,7 +259,7 @@ bool CPVRChannelGroup::UpdateMembersClientPriority()
if (bUseBackendChannelOrder)
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
clients->GetCreatedClient(member->Channel()->ClientID());
if (!client)
continue;
@@ -291,7 +291,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroup::GetByUniqueID(
std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetByUniqueID(int iUniqueChannelId,
int iClientID) const
{
- const std::shared_ptr<CPVRChannelGroupMember> groupMember =
+ const std::shared_ptr<const CPVRChannelGroupMember> groupMember =
GetByUniqueID(std::make_pair(iClientID, iUniqueChannelId));
return groupMember ? groupMember->Channel() : std::shared_ptr<CPVRChannel>();
}
@@ -314,7 +314,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroup::GetLastPlayedChannelGr
std::shared_ptr<CPVRChannelGroupMember> groupMember;
for (const auto& memberPair : m_members)
{
- const std::shared_ptr<CPVRChannel> channel = memberPair.second->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = memberPair.second->Channel();
if (channel->ChannelID() != iCurrentChannel &&
CServiceBroker::GetPVRManager().Clients()->IsCreatedClient(channel->ClientID()) &&
channel->LastWatched() > 0 &&
@@ -353,18 +353,18 @@ GroupMemberPair CPVRChannelGroup::GetLastAndPreviousToLastPlayedChannelGroupMemb
}
CPVRChannelNumber CPVRChannelGroup::GetChannelNumber(
- const std::shared_ptr<CPVRChannel>& channel) const
+ const std::shared_ptr<const CPVRChannel>& channel) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::shared_ptr<CPVRChannelGroupMember> member = GetByUniqueID(channel->StorageId());
+ const std::shared_ptr<const CPVRChannelGroupMember> member = GetByUniqueID(channel->StorageId());
return member ? member->ChannelNumber() : CPVRChannelNumber();
}
CPVRChannelNumber CPVRChannelGroup::GetClientChannelNumber(
- const std::shared_ptr<CPVRChannel>& channel) const
+ const std::shared_ptr<const CPVRChannel>& channel) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::shared_ptr<CPVRChannelGroupMember> member = GetByUniqueID(channel->StorageId());
+ const std::shared_ptr<const CPVRChannelGroupMember> member = GetByUniqueID(channel->StorageId());
return member ? member->ClientChannelNumber() : CPVRChannelNumber();
}
@@ -385,7 +385,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroup::GetByChannelNumber(
}
std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroup::GetNextChannelGroupMember(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const
{
std::shared_ptr<CPVRChannelGroupMember> nextMember;
@@ -413,7 +413,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroup::GetNextChannelGroupMem
}
std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroup::GetPreviousChannelGroupMember(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const
{
std::shared_ptr<CPVRChannelGroupMember> previousMember;
@@ -484,7 +484,8 @@ void CPVRChannelGroup::GetChannelNumbers(std::vector<std::string>& channelNumber
int CPVRChannelGroup::LoadFromDatabase(const std::vector<std::shared_ptr<CPVRClient>>& clients)
{
- const std::shared_ptr<CPVRDatabase> database(CServiceBroker::GetPVRManager().GetTVDatabase());
+ const std::shared_ptr<const CPVRDatabase> database(
+ CServiceBroker::GetPVRManager().GetTVDatabase());
if (!database)
return -1;
@@ -494,7 +495,7 @@ int CPVRChannelGroup::LoadFromDatabase(const std::vector<std::shared_ptr<CPVRCli
std::vector<std::shared_ptr<CPVRChannelGroupMember>> membersToDelete;
if (!results.empty())
{
- const std::shared_ptr<CPVRClients> allClients = CServiceBroker::GetPVRManager().Clients();
+ const std::shared_ptr<const CPVRClients> allClients = CServiceBroker::GetPVRManager().Clients();
std::unique_lock<CCriticalSection> lock(m_critSection);
for (const auto& member : results)
@@ -641,10 +642,11 @@ bool CPVRChannelGroup::HasValidDataForClient(int iClientId) const
bool CPVRChannelGroup::HasValidDataForClients(
const std::vector<std::shared_ptr<CPVRClient>>& clients) const
{
- return m_failedClients.empty() || std::none_of(clients.cbegin(), clients.cend(),
- [this](const std::shared_ptr<CPVRClient>& client) {
- return !HasValidDataForClient(client->GetID());
- });
+ return m_failedClients.empty() ||
+ std::none_of(clients.cbegin(), clients.cend(),
+ [this](const std::shared_ptr<const CPVRClient>& client) {
+ return !HasValidDataForClient(client->GetID());
+ });
}
bool CPVRChannelGroup::UpdateChannelNumbersFromAllChannelsGroup()
@@ -685,7 +687,7 @@ std::vector<std::shared_ptr<CPVRChannelGroupMember>> CPVRChannelGroup::RemoveDel
// check for deleted/invalid channels
for (auto it = m_sortedMembers.begin(); it != m_sortedMembers.end();)
{
- const std::shared_ptr<CPVRChannel> channel = (*it)->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = (*it)->Channel();
if (GetClientID() >= 0 && GetClientID() != channel->ClientID())
{
@@ -756,7 +758,8 @@ bool CPVRChannelGroup::UpdateGroupEntries(
return bReturn;
}
-bool CPVRChannelGroup::RemoveFromGroup(const std::shared_ptr<CPVRChannelGroupMember>& groupMember)
+bool CPVRChannelGroup::RemoveFromGroup(
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember)
{
bool bReturn = false;
const auto channel = groupMember->Channel();
@@ -782,7 +785,8 @@ bool CPVRChannelGroup::RemoveFromGroup(const std::shared_ptr<CPVRChannelGroupMem
return bReturn;
}
-bool CPVRChannelGroup::AppendToGroup(const std::shared_ptr<CPVRChannelGroupMember>& groupMember)
+bool CPVRChannelGroup::AppendToGroup(
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember)
{
bool bReturn = false;
@@ -814,7 +818,7 @@ bool CPVRChannelGroup::AppendToGroup(const std::shared_ptr<CPVRChannelGroupMembe
}
bool CPVRChannelGroup::IsGroupMember(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
return m_members.find(groupMember->Channel()->StorageId()) != m_members.end();
diff --git a/xbmc/pvr/channels/PVRChannelGroup.h b/xbmc/pvr/channels/PVRChannelGroup.h
index 605908663a..8fe5b63b60 100644
--- a/xbmc/pvr/channels/PVRChannelGroup.h
+++ b/xbmc/pvr/channels/PVRChannelGroup.h
@@ -31,6 +31,8 @@ static constexpr int PVR_GROUP_TYPE_LOCAL = 2;
static constexpr int PVR_GROUP_CLIENT_ID_UNKNOWN = -2;
static constexpr int PVR_GROUP_CLIENT_ID_LOCAL = -1;
+static constexpr int PVR_GROUP_ID_UNNKOWN{-1};
+
enum class PVREvent;
class CPVRChannel;
@@ -61,7 +63,7 @@ public:
* @param allChannelsGroup The channel group containing all TV or radio channels.
*/
CPVRChannelGroup(const CPVRChannelsPath& path,
- const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup);
+ const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup);
~CPVRChannelGroup() override;
@@ -134,21 +136,22 @@ public:
* @param groupMember The channel group member to remove.
* @return True if the channel group member was removed, false otherwise.
*/
- virtual bool RemoveFromGroup(const std::shared_ptr<CPVRChannelGroupMember>& groupMember);
+ virtual bool RemoveFromGroup(const std::shared_ptr<const CPVRChannelGroupMember>& groupMember);
/*!
* @brief Append a channel group member to this container.
* @param groupMember The channel group member to append.
* @return True if the channel group member was appended, false otherwise.
*/
- virtual bool AppendToGroup(const std::shared_ptr<CPVRChannelGroupMember>& groupMember);
+ virtual bool AppendToGroup(const std::shared_ptr<const CPVRChannelGroupMember>& groupMember);
/*!
* @brief Check whether a channel group member is in this container.
* @param groupMember The channel group member to check.
* @return True if the channel group member was found, false otherwise.
*/
- virtual bool IsGroupMember(const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const;
+ virtual bool IsGroupMember(
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const;
/*!
* @brief Change the name of this group.
@@ -283,14 +286,14 @@ public:
* @param channel The channel to get the channel number for.
* @return The channel number in this group.
*/
- CPVRChannelNumber GetChannelNumber(const std::shared_ptr<CPVRChannel>& channel) const;
+ CPVRChannelNumber GetChannelNumber(const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @brief Get the client channel number in this group of the given channel.
* @param channel The channel to get the channel number for.
* @return The client channel number in this group.
*/
- CPVRChannelNumber GetClientChannelNumber(const std::shared_ptr<CPVRChannel>& channel) const;
+ CPVRChannelNumber GetClientChannelNumber(const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @brief Get the next channel group member in this group.
@@ -298,7 +301,7 @@ public:
* @return The channel group member or nullptr if it wasn't found.
*/
std::shared_ptr<CPVRChannelGroupMember> GetNextChannelGroupMember(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const;
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const;
/*!
* @brief Get the previous channel group member in this group.
@@ -306,7 +309,7 @@ public:
* @return The channel group member or nullptr if it wasn't found.
*/
std::shared_ptr<CPVRChannelGroupMember> GetPreviousChannelGroupMember(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const;
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const;
/*!
* @brief Get a channel given it's channel ID.
@@ -571,7 +574,7 @@ private:
void OnSettingChanged();
- std::shared_ptr<CPVRChannelGroup> m_allChannelsGroup;
+ std::shared_ptr<const CPVRChannelGroup> m_allChannelsGroup;
CPVRChannelsPath m_path;
bool m_bDeleted = false;
mutable std::optional<int> m_clientPriority;
diff --git a/xbmc/pvr/channels/PVRChannelGroupAllChannels.cpp b/xbmc/pvr/channels/PVRChannelGroupAllChannels.cpp
index b72908e3a5..700500c822 100644
--- a/xbmc/pvr/channels/PVRChannelGroupAllChannels.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroupAllChannels.cpp
@@ -130,7 +130,7 @@ std::vector<std::shared_ptr<CPVRChannelGroupMember>> CPVRChannelGroupAllChannels
}
bool CPVRChannelGroupAllChannels::AppendToGroup(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember)
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember)
{
if (IsGroupMember(groupMember))
return false;
@@ -142,7 +142,7 @@ bool CPVRChannelGroupAllChannels::AppendToGroup(
}
bool CPVRChannelGroupAllChannels::RemoveFromGroup(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember)
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember)
{
if (!IsGroupMember(groupMember))
return false;
@@ -154,7 +154,7 @@ bool CPVRChannelGroupAllChannels::RemoveFromGroup(
}
bool CPVRChannelGroupAllChannels::IsGroupMember(
- const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const
{
return !groupMember->Channel()->IsHidden();
}
diff --git a/xbmc/pvr/channels/PVRChannelGroupAllChannels.h b/xbmc/pvr/channels/PVRChannelGroupAllChannels.h
index 0b6a5dd6cc..98d692185a 100644
--- a/xbmc/pvr/channels/PVRChannelGroupAllChannels.h
+++ b/xbmc/pvr/channels/PVRChannelGroupAllChannels.h
@@ -40,17 +40,18 @@ public:
/*!
* @see CPVRChannelGroup::IsGroupMember
*/
- bool IsGroupMember(const std::shared_ptr<CPVRChannelGroupMember>& groupMember) const override;
+ bool IsGroupMember(
+ const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) const override;
/*!
* @see CPVRChannelGroup::AppendToGroup
*/
- bool AppendToGroup(const std::shared_ptr<CPVRChannelGroupMember>& groupMember) override;
+ bool AppendToGroup(const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) override;
/*!
* @see CPVRChannelGroup::RemoveFromGroup
*/
- bool RemoveFromGroup(const std::shared_ptr<CPVRChannelGroupMember>& groupMember) override;
+ bool RemoveFromGroup(const std::shared_ptr<const CPVRChannelGroupMember>& groupMember) override;
/*!
* @brief Check whether the group name is still correct after the language setting changed.
diff --git a/xbmc/pvr/channels/PVRChannelGroupFromClient.cpp b/xbmc/pvr/channels/PVRChannelGroupFromClient.cpp
index e8878ad509..8e582b77f7 100644
--- a/xbmc/pvr/channels/PVRChannelGroupFromClient.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroupFromClient.cpp
@@ -16,7 +16,7 @@
using namespace PVR;
CPVRChannelGroupFromClient::CPVRChannelGroupFromClient(
- const CPVRChannelsPath& path, const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup)
+ const CPVRChannelsPath& path, const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup)
: CPVRChannelGroup(path, allChannelsGroup)
{
}
@@ -24,7 +24,7 @@ CPVRChannelGroupFromClient::CPVRChannelGroupFromClient(
CPVRChannelGroupFromClient::CPVRChannelGroupFromClient(
const PVR_CHANNEL_GROUP& group,
int clientID,
- const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup)
+ const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup)
: CPVRChannelGroup(CPVRChannelsPath(group.bIsRadio, group.strGroupName, clientID),
allChannelsGroup)
{
diff --git a/xbmc/pvr/channels/PVRChannelGroupFromClient.h b/xbmc/pvr/channels/PVRChannelGroupFromClient.h
index d697b3aa4d..b0d9df8bd0 100644
--- a/xbmc/pvr/channels/PVRChannelGroupFromClient.h
+++ b/xbmc/pvr/channels/PVRChannelGroupFromClient.h
@@ -22,7 +22,7 @@ public:
* @param allChannelsGroup The channel group containing all TV or radio channels.
*/
CPVRChannelGroupFromClient(const CPVRChannelsPath& path,
- const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup);
+ const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup);
/*!
* @brief Create a new channel group instance from a channel group provided by a PVR client.
@@ -32,7 +32,7 @@ public:
*/
CPVRChannelGroupFromClient(const PVR_CHANNEL_GROUP& group,
int clientID,
- const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup);
+ const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup);
/*!
* @brief Check whether this group could be deleted by the user.
diff --git a/xbmc/pvr/channels/PVRChannelGroupFromUser.cpp b/xbmc/pvr/channels/PVRChannelGroupFromUser.cpp
index 7d2934e35e..253f24dec7 100644
--- a/xbmc/pvr/channels/PVRChannelGroupFromUser.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroupFromUser.cpp
@@ -11,7 +11,7 @@
using namespace PVR;
CPVRChannelGroupFromUser::CPVRChannelGroupFromUser(
- const CPVRChannelsPath& path, const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup)
+ const CPVRChannelsPath& path, const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup)
: CPVRChannelGroup(path, allChannelsGroup)
{
}
diff --git a/xbmc/pvr/channels/PVRChannelGroupFromUser.h b/xbmc/pvr/channels/PVRChannelGroupFromUser.h
index 3a3d26b7d1..b89946b3e7 100644
--- a/xbmc/pvr/channels/PVRChannelGroupFromUser.h
+++ b/xbmc/pvr/channels/PVRChannelGroupFromUser.h
@@ -22,7 +22,7 @@ public:
* @param allChannelsGroup The channel group containing all TV or radio channels.
*/
CPVRChannelGroupFromUser(const CPVRChannelsPath& path,
- const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup);
+ const std::shared_ptr<const CPVRChannelGroup>& allChannelsGroup);
/*!
* @brief Check whether this group could be deleted by the user.
diff --git a/xbmc/pvr/channels/PVRChannelGroupMember.cpp b/xbmc/pvr/channels/PVRChannelGroupMember.cpp
index 0223b14795..de04a968cd 100644
--- a/xbmc/pvr/channels/PVRChannelGroupMember.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroupMember.cpp
@@ -88,7 +88,7 @@ void CPVRChannelGroupMember::SetGroupID(int iGroupID)
void CPVRChannelGroupMember::SetGroupName(const std::string& groupName)
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_iChannelClientID);
if (client)
m_path = CPVRChannelsPath(m_bIsRadio, groupName, m_iGroupClientID, client->ID(),
diff --git a/xbmc/pvr/channels/PVRChannelGroups.cpp b/xbmc/pvr/channels/PVRChannelGroups.cpp
index 04356a0e7c..f692e19110 100644
--- a/xbmc/pvr/channels/PVRChannelGroups.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroups.cpp
@@ -165,7 +165,8 @@ bool CPVRChannelGroups::Update(const std::shared_ptr<CPVRChannelGroup>& group,
return true;
}
-int CPVRChannelGroups::GetGroupTypePriority(const std::shared_ptr<CPVRChannelGroup>& group) const
+int CPVRChannelGroups::GetGroupTypePriority(
+ const std::shared_ptr<const CPVRChannelGroup>& group) const
{
switch (group->GroupType())
{
@@ -180,7 +181,8 @@ int CPVRChannelGroups::GetGroupTypePriority(const std::shared_ptr<CPVRChannelGro
}
}
-int CPVRChannelGroups::GetGroupClientPriority(const std::shared_ptr<CPVRChannelGroup>& group) const
+int CPVRChannelGroups::GetGroupClientPriority(
+ const std::shared_ptr<const CPVRChannelGroup>& group) const
{
// if no priority was set by the user, use the client id to distinguish between the clients
const int priority = group->GetClientPriority();
@@ -237,7 +239,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroups::GetChannelGroupMember
{
if (path.IsChannel())
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
GetByName(path.GetGroupName(), path.GetGroupClientID());
if (group)
return group->GetByUniqueID(
@@ -248,7 +250,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroups::GetChannelGroupMember
}
std::vector<std::shared_ptr<CPVRChannelGroupMember>> CPVRChannelGroups::GetMembersAvailableForGroup(
- const std::shared_ptr<CPVRChannelGroup>& group)
+ const std::shared_ptr<const CPVRChannelGroup>& group)
{
std::vector<std::shared_ptr<CPVRChannelGroupMember>> result;
@@ -307,7 +309,7 @@ bool CPVRChannelGroups::HasValidDataForClients(
{
return m_failedClientsForChannelGroups.empty() ||
std::none_of(clients.cbegin(), clients.cend(),
- [this](const std::shared_ptr<CPVRClient>& client) {
+ [this](const std::shared_ptr<const CPVRClient>& client) {
return std::find(m_failedClientsForChannelGroups.cbegin(),
m_failedClientsForChannelGroups.cend(),
client->GetID()) != m_failedClientsForChannelGroups.cend();
@@ -387,7 +389,8 @@ std::shared_ptr<CPVRChannelGroup> CPVRChannelGroups::CreateChannelGroup(
bool CPVRChannelGroups::LoadFromDatabase(const std::vector<std::shared_ptr<CPVRClient>>& clients)
{
- const std::shared_ptr<CPVRDatabase> database(CServiceBroker::GetPVRManager().GetTVDatabase());
+ const std::shared_ptr<const CPVRDatabase> database(
+ CServiceBroker::GetPVRManager().GetTVDatabase());
if (!database)
return false;
diff --git a/xbmc/pvr/channels/PVRChannelGroups.h b/xbmc/pvr/channels/PVRChannelGroups.h
index 0fcbe74ef9..bb86c28d96 100644
--- a/xbmc/pvr/channels/PVRChannelGroups.h
+++ b/xbmc/pvr/channels/PVRChannelGroups.h
@@ -98,7 +98,7 @@ public:
* @return The channel group members that could be added to the group
*/
std::vector<std::shared_ptr<CPVRChannelGroupMember>> GetMembersAvailableForGroup(
- const std::shared_ptr<CPVRChannelGroup>& group);
+ const std::shared_ptr<const CPVRChannelGroup>& group);
/*!
* @brief Get a pointer to a channel group given its ID.
@@ -243,8 +243,8 @@ private:
void OnPVRManagerEvent(const PVR::PVREvent& event);
- int GetGroupTypePriority(const std::shared_ptr<CPVRChannelGroup>& group) const;
- int GetGroupClientPriority(const std::shared_ptr<CPVRChannelGroup>& group) const;
+ int GetGroupTypePriority(const std::shared_ptr<const CPVRChannelGroup>& group) const;
+ int GetGroupClientPriority(const std::shared_ptr<const CPVRChannelGroup>& group) const;
bool m_bRadio{false};
std::vector<std::shared_ptr<CPVRChannelGroup>> m_groups;
diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp
index 4349d0b7c9..7e57310c2c 100644
--- a/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp
+++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp
@@ -97,7 +97,8 @@ std::shared_ptr<CPVRChannel> CPVRChannelGroupsContainer::GetChannelById(int iCha
return channel;
}
-std::shared_ptr<CPVRChannel> CPVRChannelGroupsContainer::GetChannelForEpgTag(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+std::shared_ptr<CPVRChannel> CPVRChannelGroupsContainer::GetChannelForEpgTag(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (!epgTag)
return {};
@@ -117,7 +118,8 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRChannelGroupsContainer::GetChannelGr
std::shared_ptr<CPVRChannel> CPVRChannelGroupsContainer::GetByPath(const std::string& strPath) const
{
- const std::shared_ptr<CPVRChannelGroupMember> groupMember = GetChannelGroupMemberByPath(strPath);
+ const std::shared_ptr<const CPVRChannelGroupMember> groupMember =
+ GetChannelGroupMemberByPath(strPath);
if (groupMember)
return groupMember->Channel();
diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.h b/xbmc/pvr/channels/PVRChannelGroupsContainer.h
index 7e6cf9e378..5a680bc166 100644
--- a/xbmc/pvr/channels/PVRChannelGroupsContainer.h
+++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.h
@@ -113,7 +113,8 @@ namespace PVR
* @param epgTag The epg tag.
* @return The channel.
*/
- std::shared_ptr<CPVRChannel> GetChannelForEpgTag(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ std::shared_ptr<CPVRChannel> GetChannelForEpgTag(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
/*!
* @brief Get a channel given it's path.
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp
index 10b2623ad6..b057b338bc 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp
@@ -25,7 +25,7 @@ CGUIDialogPVRChannelGuide::CGUIDialogPVRChannelGuide()
{
}
-void CGUIDialogPVRChannelGuide::Open(const std::shared_ptr<CPVRChannel>& channel)
+void CGUIDialogPVRChannelGuide::Open(const std::shared_ptr<const CPVRChannel>& channel)
{
m_channel = channel;
CGUIDialogPVRItemsViewBase::Open();
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h
index b348a9e699..2835c90f59 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h
+++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h
@@ -22,13 +22,13 @@ namespace PVR
CGUIDialogPVRChannelGuide();
~CGUIDialogPVRChannelGuide() override = default;
- void Open(const std::shared_ptr<CPVRChannel>& channel);
+ void Open(const std::shared_ptr<const CPVRChannel>& channel);
protected:
void OnInitWindow() override;
void OnDeinitWindow(int nextWindowID) override;
private:
- std::shared_ptr<CPVRChannel> m_channel;
+ std::shared_ptr<const CPVRChannel> m_channel;
};
}
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp
index 3d6985112f..d61e3b0355 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp
@@ -208,7 +208,7 @@ void CGUIDialogPVRChannelManager::OnInitWindow()
if (m_initialSelection)
{
// set initial selection
- const std::shared_ptr<CPVRChannel> channel = m_initialSelection->GetPVRChannelInfoTag();
+ const std::shared_ptr<const CPVRChannel> channel = m_initialSelection->GetPVRChannelInfoTag();
for (int i = 0; i < m_channelItems->Size(); ++i)
{
if (m_channelItems->Get(i)->GetPVRChannelInfoTag() == channel)
@@ -750,7 +750,7 @@ bool CGUIDialogPVRChannelManager::OnContextButton(int itemNumber, CONTEXT_BUTTON
const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*pItem);
if (client)
{
- const std::shared_ptr<CPVRChannel> channel = pItem->GetPVRChannelInfoTag();
+ const std::shared_ptr<const CPVRChannel> channel = pItem->GetPVRChannelInfoTag();
PVR_ERROR ret = client->DeleteChannel(channel);
if (ret == PVR_ERROR_NO_ERROR)
{
@@ -821,7 +821,7 @@ void CGUIDialogPVRChannelManager::Update()
channelFile = std::make_shared<CFileItem>(member);
if (!channelFile)
continue;
- const std::shared_ptr<CPVRChannel> channel(channelFile->GetPVRChannelInfoTag());
+ const std::shared_ptr<const CPVRChannel> channel(channelFile->GetPVRChannelInfoTag());
channelFile->SetProperty(PROPERTY_CHANNEL_ENABLED, !channel->IsHidden());
channelFile->SetProperty(PROPERTY_CHANNEL_USER_SET_HIDDEN, channel->IsUserSetHidden());
@@ -835,7 +835,8 @@ void CGUIDialogPVRChannelManager::Update()
channelFile->SetProperty(PROPERTY_CHANNEL_NUMBER,
member->ChannelNumber().FormattedChannelNumber());
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*channelFile);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(*channelFile);
if (client)
{
channelFile->SetProperty(PROPERTY_CLIENT_NAME, client->GetFriendlyName());
@@ -923,7 +924,7 @@ void CGUIDialogPVRChannelManager::RenameChannel(const CFileItemPtr& pItem)
std::string strChannelName = pItem->GetProperty(PROPERTY_CHANNEL_NAME).asString();
if (strChannelName != pItem->GetPVRChannelInfoTag()->ChannelName())
{
- std::shared_ptr<CPVRChannel> channel = pItem->GetPVRChannelInfoTag();
+ const std::shared_ptr<CPVRChannel> channel = pItem->GetPVRChannelInfoTag();
channel->SetChannelName(strChannelName);
const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*pItem);
@@ -1068,8 +1069,9 @@ namespace
bool IsItemChanged(const std::shared_ptr<CFileItem>& item)
{
- const std::shared_ptr<CPVRChannelGroupMember> member = item->GetPVRChannelGroupMemberInfoTag();
- const std::shared_ptr<CPVRChannel> channel = member->Channel();
+ const std::shared_ptr<const CPVRChannelGroupMember> member =
+ item->GetPVRChannelGroupMemberInfoTag();
+ const std::shared_ptr<const CPVRChannel> channel = member->Channel();
return item->GetProperty(PROPERTY_CHANNEL_ENABLED).asBoolean() == channel->IsHidden() ||
item->GetProperty(PROPERTY_CHANNEL_USER_SET_HIDDEN).asBoolean() !=
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp
index b5830d49cb..9c079c8e7e 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp
@@ -177,7 +177,7 @@ void CGUIDialogPVRChannelsOSD::Update()
CPVRManager& pvrMgr = CServiceBroker::GetPVRManager();
pvrMgr.Events().Subscribe(this, &CGUIDialogPVRChannelsOSD::Notify);
- const std::shared_ptr<CPVRChannel> channel = pvrMgr.PlaybackState()->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> channel = pvrMgr.PlaybackState()->GetPlayingChannel();
if (channel)
{
const std::shared_ptr<CPVRChannelGroup> group =
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp
index 89d6ddfcc5..c0d6d801d7 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp
@@ -581,7 +581,8 @@ void CGUIDialogPVRGroupManager::Update()
for (auto& group : *m_channelGroups)
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*group);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(*group);
if (client)
group->SetProperty(PROPERTY_CLIENT_NAME, client->GetFriendlyName());
}
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp
index 9bce21edf8..1605234bfe 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp
@@ -11,12 +11,14 @@
#include "FileItem.h"
#include "ServiceBroker.h"
#include "guilib/GUIMessage.h"
+#include "pvr/PVRItem.h"
#include "pvr/PVRManager.h"
#include "pvr/addons/PVRClient.h"
#include "pvr/epg/EpgInfoTag.h"
#include "pvr/guilib/PVRGUIActionsEPG.h"
#include "pvr/guilib/PVRGUIActionsPlayback.h"
#include "pvr/guilib/PVRGUIActionsTimers.h"
+#include "pvr/guilib/PVRGUIRecordingsPlayActionProcessor.h"
#include "pvr/recordings/PVRRecordings.h"
#include "pvr/timers/PVRTimerInfoTag.h"
#include "pvr/timers/PVRTimers.h"
@@ -134,15 +136,26 @@ bool CGUIDialogPVRGuideInfo::OnClickButtonPlay(const CGUIMessage& message)
if (m_progItem)
{
if (message.GetSenderId() == CONTROL_BTN_PLAY_RECORDING)
- CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(
- *m_progItem, true /* bCheckResume */);
+ {
+ const auto recording{CPVRItem(m_progItem).GetRecording()};
+ if (recording)
+ {
+ CGUIPVRRecordingsPlayActionProcessor proc{std::make_shared<CFileItem>(recording)};
+ proc.ProcessDefaultAction();
+ if (proc.UserCancelled())
+ Open();
+ }
+ }
else if (message.GetSenderId() == CONTROL_BTN_PLAY_EPGTAG &&
m_progItem->GetEPGInfoTag()->IsPlayable())
+ {
CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag(*m_progItem);
+ }
else
+ {
CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(
*m_progItem, true /* bCheckResume */);
-
+ }
bReturn = true;
}
}
@@ -214,7 +227,7 @@ void CGUIDialogPVRGuideInfo::OnInitWindow()
bool bHideRecord = true;
bool bHideAddTimer = true;
- const std::shared_ptr<CPVRTimerInfoTag> timer = mgr.Timers()->GetTimerForEpgTag(epgTag);
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = mgr.Timers()->GetTimerForEpgTag(epgTag);
bool bHideSetReminder = timer || (epgTag->StartAsLocalTime() <= CDateTime::GetCurrentDateTime());
if (timer)
@@ -232,7 +245,7 @@ void CGUIDialogPVRGuideInfo::OnInitWindow()
}
else if (epgTag->IsRecordable())
{
- const std::shared_ptr<CPVRClient> client = mgr.GetClient(epgTag->ClientID());
+ const std::shared_ptr<const CPVRClient> client = mgr.GetClient(epgTag->ClientID());
if (client && client->GetClientCapabilities().SupportsTimers())
{
SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 264); /* Record */
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
index 8392668504..5af1b8d789 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
@@ -87,7 +87,7 @@ void CGUIDialogPVRGuideSearch::UpdateChannelSpin()
int iSelectedChannel = EPG_SEARCH_UNSET;
for (const auto& groupMember : groupMembers)
{
- labels.emplace_back(std::make_pair(groupMember->Channel()->ChannelName(), iIndex));
+ labels.emplace_back(groupMember->Channel()->ChannelName(), iIndex);
m_channelsMap.insert(std::make_pair(iIndex, groupMember));
if (iSelectedChannel == EPG_SEARCH_UNSET &&
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp
index ead92d2518..6d73450e4e 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp
@@ -64,11 +64,12 @@ bool CGUIDialogPVRRadioRDSInfo::OnMessage(CGUIMessage& message)
}
else if (iControl == SPIN_CONTROL_INFO)
{
- const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> channel =
+ CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
if (!channel)
return false;
- const std::shared_ptr<CPVRRadioRDSInfoTag> currentRDS = channel->GetRadioRDSInfoTag();
+ const std::shared_ptr<const CPVRRadioRDSInfoTag> currentRDS = channel->GetRadioRDSInfoTag();
if (!currentRDS)
return false;
@@ -158,11 +159,12 @@ void CGUIDialogPVRRadioRDSInfo::InitInfoControls()
void CGUIDialogPVRRadioRDSInfo::UpdateInfoControls()
{
- const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> channel =
+ CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
if (!channel)
return;
- const std::shared_ptr<CPVRRadioRDSInfoTag> currentRDS = channel->GetRadioRDSInfoTag();
+ const std::shared_ptr<const CPVRRadioRDSInfoTag> currentRDS = channel->GetRadioRDSInfoTag();
if (!currentRDS)
return;
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp
index 93960832ef..6019c3feb1 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp
@@ -13,7 +13,7 @@
#include "guilib/GUIMessage.h"
#include "pvr/PVRManager.h"
#include "pvr/guilib/PVRGUIActionsEPG.h"
-#include "pvr/guilib/PVRGUIActionsPlayback.h"
+#include "pvr/guilib/PVRGUIRecordingsPlayActionProcessor.h"
using namespace PVR;
@@ -59,8 +59,12 @@ bool CGUIDialogPVRRecordingInfo::OnClickButtonPlay(const CGUIMessage& message)
Close();
if (m_recordItem)
- CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(
- *m_recordItem, true /* check resume */);
+ {
+ CGUIPVRRecordingsPlayActionProcessor proc{m_recordItem};
+ proc.ProcessDefaultAction();
+ if (proc.UserCancelled())
+ Open();
+ }
bReturn = true;
}
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp
index ed8cd7bd1e..6454e7dff5 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp
@@ -86,7 +86,7 @@ void CGUIDialogPVRRecordingSettings::InitializeSettings()
}
std::shared_ptr<CSetting> setting = nullptr;
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_recording->ClientID());
// Name
@@ -109,7 +109,7 @@ bool CGUIDialogPVRRecordingSettings::CanEditRecording(const CFileItem& item)
if (!item.HasPVRRecordingInfoTag())
return false;
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(item.GetPVRRecordingInfoTag()->ClientID());
if (!client)
@@ -200,7 +200,7 @@ void CGUIDialogPVRRecordingSettings::LifetimesFiller(const SettingConstPtr& sett
{
list.clear();
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(pThis->m_recording->ClientID());
if (client)
{
diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
index f7866278e7..f6a3a2c651 100644
--- a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
+++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
@@ -778,8 +778,8 @@ void CGUIDialogPVRTimerSettings::AddCondition(const std::shared_ptr<CSetting>& s
{
GetSettingsManager()->AddDynamicCondition(identifier, condition, this);
CSettingDependency dep(depType, GetSettingsManager());
- dep.And()->Add(CSettingDependencyConditionPtr(
- new CSettingDependencyCondition(identifier, "true", settingId, false, GetSettingsManager())));
+ dep.And()->Add(std::make_shared<CSettingDependencyCondition>(identifier, "true", settingId, false,
+ GetSettingsManager()));
SettingDependencies deps(setting->GetDependencies());
deps.push_back(dep);
setting->SetDependencies(deps);
@@ -841,7 +841,7 @@ void CGUIDialogPVRTimerSettings::InitializeTypesList()
// Drop TimerTypes without 'Series' EPG attributes if none are set
if (type->RequiresEpgSeriesOnCreate())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
if (epgTag && !epgTag->IsSeries())
continue;
}
@@ -849,7 +849,7 @@ void CGUIDialogPVRTimerSettings::InitializeTypesList()
// Drop TimerTypes which need series link if none is set
if (type->RequiresEpgSeriesLinkOnCreate())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
if (!epgTag || epgTag->SeriesLink().empty())
continue;
}
@@ -861,7 +861,7 @@ void CGUIDialogPVRTimerSettings::InitializeTypesList()
// Drop TimerTypes that aren't rules and cannot be recorded
if (!type->IsTimerRule())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
bool bCanRecord = epgTag ? epgTag->IsRecordable()
: m_timerInfoTag->EndAsLocalTime() > CDateTime::GetCurrentDateTime();
if (!bCanRecord)
@@ -895,13 +895,13 @@ void CGUIDialogPVRTimerSettings::InitializeChannelsList()
}
// Add regular channels
- const std::shared_ptr<CPVRChannelGroup> allGroup =
+ const std::shared_ptr<const CPVRChannelGroup> allGroup =
CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio);
const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers =
allGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE);
for (const auto& groupMember : groupMembers)
{
- const std::shared_ptr<CPVRChannel> channel = groupMember->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = groupMember->Channel();
const std::string channelDescription = StringUtils::Format(
"{} {}", groupMember->ChannelNumber().FormattedChannelNumber(), channel->ChannelName());
m_channelEntries.insert(
@@ -974,8 +974,7 @@ void CGUIDialogPVRTimerSettings::ChannelsFiller(const SettingConstPtr& setting,
!pThis->m_timerType->SupportsAnyChannel())
continue;
- list.emplace_back(
- IntegerSettingOption(channelEntry.second.description, channelEntry.first));
+ list.emplace_back(channelEntry.second.description, channelEntry.first);
}
if (!foundCurrent && (pThis->m_channel == channelEntry.second))
diff --git a/xbmc/pvr/epg/Epg.cpp b/xbmc/pvr/epg/Epg.cpp
index 861b88ea71..5b895ad042 100644
--- a/xbmc/pvr/epg/Epg.cpp
+++ b/xbmc/pvr/epg/Epg.cpp
@@ -180,7 +180,7 @@ bool CPVREpg::UpdateEntries(const CPVREpg& epg)
namespace
{
-bool IsTagExpired(const std::shared_ptr<CPVREpgInfoTag>& tag)
+bool IsTagExpired(const std::shared_ptr<const CPVREpgInfoTag>& tag)
{
// Respect epg linger time.
const int iPastDays = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
@@ -398,7 +398,8 @@ bool CPVREpg::UpdateFromScraper(time_t start, time_t end, bool bForceUpdate)
return true;
}
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
if (client)
{
if (!client->GetClientCapabilities().SupportsEPG())
@@ -542,7 +543,7 @@ void CPVREpg::RemovedFromContainer()
m_events.Publish(PVREvent::EpgDeleted);
}
-int CPVREpg::CleanupCachedImages(const std::shared_ptr<CPVREpgDatabase>& database)
+int CPVREpg::CleanupCachedImages(const std::shared_ptr<const CPVREpgDatabase>& database)
{
const std::vector<std::string> urlsToCheck = database->GetAllIconPaths(EpgID());
const std::string owner = StringUtils::Format(CPVREpgInfoTag::IMAGE_OWNER_PATTERN, EpgID());
diff --git a/xbmc/pvr/epg/Epg.h b/xbmc/pvr/epg/Epg.h
index 31bc8d0888..c3742faf81 100644
--- a/xbmc/pvr/epg/Epg.h
+++ b/xbmc/pvr/epg/Epg.h
@@ -281,7 +281,7 @@ namespace PVR
* @param database The EPG database
* @return number of cleaned up images.
*/
- int CleanupCachedImages(const std::shared_ptr<CPVREpgDatabase>& database);
+ int CleanupCachedImages(const std::shared_ptr<const CPVREpgDatabase>& database);
private:
CPVREpg() = delete;
diff --git a/xbmc/pvr/epg/EpgContainer.cpp b/xbmc/pvr/epg/EpgContainer.cpp
index 027cea91ed..4e20f790b1 100644
--- a/xbmc/pvr/epg/EpgContainer.cpp
+++ b/xbmc/pvr/epg/EpgContainer.cpp
@@ -486,7 +486,8 @@ std::shared_ptr<CPVREpg> CPVREpgContainer::GetByChannelUid(int iClientId, int iC
return epg;
}
-std::shared_ptr<CPVREpgInfoTag> CPVREpgContainer::GetTagById(const std::shared_ptr<CPVREpg>& epg, unsigned int iBroadcastId) const
+std::shared_ptr<CPVREpgInfoTag> CPVREpgContainer::GetTagById(
+ const std::shared_ptr<const CPVREpg>& epg, unsigned int iBroadcastId) const
{
std::shared_ptr<CPVREpgInfoTag> retval;
@@ -528,7 +529,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgContainer::GetTags(
// make sure we have up-to-date data in the database.
PersistAll(std::numeric_limits<unsigned int>::max());
- const std::shared_ptr<CPVREpgDatabase> database = GetEpgDatabase();
+ const std::shared_ptr<const CPVREpgDatabase> database = GetEpgDatabase();
std::vector<std::shared_ptr<CPVREpgInfoTag>> results = database->GetEpgTags(searchData);
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -572,8 +573,8 @@ std::shared_ptr<CPVREpg> CPVREpgContainer::CreateChannelEpg(int iEpgId, const st
if (iEpgId <= 0)
iEpgId = NextEpgId();
- epg.reset(new CPVREpg(iEpgId, channelData->ChannelName(), strScraperName, channelData,
- GetEpgDatabase()));
+ epg = std::make_shared<CPVREpg>(iEpgId, channelData->ChannelName(), strScraperName, channelData,
+ GetEpgDatabase());
std::unique_lock<CCriticalSection> lock(m_critSection);
m_epgIdToEpgMap.insert({iEpgId, epg});
@@ -650,7 +651,7 @@ bool CPVREpgContainer::QueueDeleteEpgs(const std::vector<std::shared_ptr<CPVREpg
return true;
}
-bool CPVREpgContainer::QueueDeleteEpg(const std::shared_ptr<CPVREpg>& epg,
+bool CPVREpgContainer::QueueDeleteEpg(const std::shared_ptr<const CPVREpg>& epg,
const std::shared_ptr<CPVREpgDatabase>& database)
{
if (!epg || epg->EpgID() < 0)
@@ -741,8 +742,8 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
std::unique_ptr<CPVRGUIProgressHandler> progressHandler;
if (bShowProgress && !bOnlyPending && !epgsToUpdate.empty())
- progressHandler.reset(
- new CPVRGUIProgressHandler(g_localizeStrings.Get(19004))); // Loading programme guide
+ progressHandler = std::make_unique<CPVRGUIProgressHandler>(
+ g_localizeStrings.Get(19004)); // Loading programme guide
for (const auto& epgEntry : epgsToUpdate)
{
@@ -812,7 +813,7 @@ std::pair<CDateTime, CDateTime> CPVREpgContainer::GetFirstAndLastEPGDate() const
{
// Get values from db
std::pair<CDateTime, CDateTime> dbDates;
- const std::shared_ptr<CPVREpgDatabase> database = GetEpgDatabase();
+ const std::shared_ptr<const CPVREpgDatabase> database = GetEpgDatabase();
if (database)
dbDates = database->GetFirstAndLastEPGDate();
@@ -891,13 +892,13 @@ void CPVREpgContainer::SetHasPendingUpdates(bool bHasPendingUpdates /* = true */
void CPVREpgContainer::UpdateRequest(int iClientID, int iUniqueChannelID)
{
std::unique_lock<CCriticalSection> lock(m_updateRequestsLock);
- m_updateRequests.emplace_back(CEpgUpdateRequest(iClientID, iUniqueChannelID));
+ m_updateRequests.emplace_back(iClientID, iUniqueChannelID);
}
void CPVREpgContainer::UpdateFromClient(const std::shared_ptr<CPVREpgInfoTag>& tag, EPG_EVENT_STATE eNewState)
{
std::unique_lock<CCriticalSection> lock(m_epgTagChangesLock);
- m_epgTagChanges.emplace_back(CEpgTagStateChange(tag, eNewState));
+ m_epgTagChanges.emplace_back(tag, eNewState);
}
int CPVREpgContainer::GetPastDaysToDisplay() const
@@ -934,7 +935,7 @@ void CPVREpgContainer::OnSystemWake()
int CPVREpgContainer::CleanupCachedImages()
{
- const std::shared_ptr<CPVREpgDatabase> database = GetEpgDatabase();
+ const std::shared_ptr<const CPVREpgDatabase> database = GetEpgDatabase();
if (!database)
{
CLog::LogF(LOGERROR, "No EPG database");
@@ -954,7 +955,7 @@ int CPVREpgContainer::CleanupCachedImages()
std::vector<std::shared_ptr<CPVREpgSearchFilter>> CPVREpgContainer::GetSavedSearches(bool bRadio)
{
- const std::shared_ptr<CPVREpgDatabase> database = GetEpgDatabase();
+ const std::shared_ptr<const CPVREpgDatabase> database = GetEpgDatabase();
if (!database)
{
CLog::LogF(LOGERROR, "No EPG database");
@@ -966,7 +967,7 @@ std::vector<std::shared_ptr<CPVREpgSearchFilter>> CPVREpgContainer::GetSavedSear
std::shared_ptr<CPVREpgSearchFilter> CPVREpgContainer::GetSavedSearchById(bool bRadio, int iId)
{
- const std::shared_ptr<CPVREpgDatabase> database = GetEpgDatabase();
+ const std::shared_ptr<const CPVREpgDatabase> database = GetEpgDatabase();
if (!database)
{
CLog::LogF(LOGERROR, "No EPG database");
diff --git a/xbmc/pvr/epg/EpgContainer.h b/xbmc/pvr/epg/EpgContainer.h
index 68cf50d654..0b6c426b61 100644
--- a/xbmc/pvr/epg/EpgContainer.h
+++ b/xbmc/pvr/epg/EpgContainer.h
@@ -144,7 +144,8 @@ namespace PVR
* @param iBroadcastId The event id to lookup.
* @return The requested event, or an empty tag when not found
*/
- std::shared_ptr<CPVREpgInfoTag> GetTagById(const std::shared_ptr<CPVREpg>& epg, unsigned int iBroadcastId) const;
+ std::shared_ptr<CPVREpgInfoTag> GetTagById(const std::shared_ptr<const CPVREpg>& epg,
+ unsigned int iBroadcastId) const;
/*!
* @brief Get the EPG event with the given database id
@@ -321,7 +322,7 @@ namespace PVR
* @param database The database containing the epg data.
* @return True on success, false otherwise.
*/
- bool QueueDeleteEpg(const std::shared_ptr<CPVREpg>& epg,
+ bool QueueDeleteEpg(const std::shared_ptr<const CPVREpg>& epg,
const std::shared_ptr<CPVREpgDatabase>& database);
std::shared_ptr<CPVREpgDatabase> m_database; /*!< the EPG database */
diff --git a/xbmc/pvr/epg/EpgDatabase.cpp b/xbmc/pvr/epg/EpgDatabase.cpp
index 191f5958b5..dbe3a49c3f 100644
--- a/xbmc/pvr/epg/EpgDatabase.cpp
+++ b/xbmc/pvr/epg/EpgDatabase.cpp
@@ -408,7 +408,7 @@ std::vector<std::shared_ptr<CPVREpg>> CPVREpgDatabase::GetAll()
}
std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::CreateEpgTag(
- const std::unique_ptr<dbiplus::Dataset>& pDS)
+ const std::unique_ptr<dbiplus::Dataset>& pDS) const
{
if (!pDS->eof())
{
@@ -460,7 +460,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::CreateEpgTag(
return {};
}
-bool CPVREpgDatabase::HasTags(int iEpgID)
+bool CPVREpgDatabase::HasTags(int iEpgID) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery =
@@ -469,7 +469,7 @@ bool CPVREpgDatabase::HasTags(int iEpgID)
return !strValue.empty();
}
-CDateTime CPVREpgDatabase::GetLastEndTime(int iEpgID)
+CDateTime CPVREpgDatabase::GetLastEndTime(int iEpgID) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery =
@@ -481,7 +481,7 @@ CDateTime CPVREpgDatabase::GetLastEndTime(int iEpgID)
return {};
}
-std::pair<CDateTime, CDateTime> CPVREpgDatabase::GetFirstAndLastEPGDate()
+std::pair<CDateTime, CDateTime> CPVREpgDatabase::GetFirstAndLastEPGDate() const
{
CDateTime first;
CDateTime last;
@@ -505,7 +505,7 @@ std::pair<CDateTime, CDateTime> CPVREpgDatabase::GetFirstAndLastEPGDate()
return {first, last};
}
-CDateTime CPVREpgDatabase::GetMinStartTime(int iEpgID, const CDateTime& minStart)
+CDateTime CPVREpgDatabase::GetMinStartTime(int iEpgID, const CDateTime& minStart) const
{
time_t t;
minStart.GetAsTime(t);
@@ -522,7 +522,7 @@ CDateTime CPVREpgDatabase::GetMinStartTime(int iEpgID, const CDateTime& minStart
return {};
}
-CDateTime CPVREpgDatabase::GetMaxEndTime(int iEpgID, const CDateTime& maxEnd)
+CDateTime CPVREpgDatabase::GetMaxEndTime(int iEpgID, const CDateTime& maxEnd) const
{
time_t t;
maxEnd.GetAsTime(t);
@@ -665,7 +665,7 @@ private:
} // unnamed namespace
std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTags(
- const PVREpgSearchData& searchData)
+ const PVREpgSearchData& searchData) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -786,7 +786,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTags(
}
std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByUniqueBroadcastID(
- int iEpgID, unsigned int iUniqueBroadcastId)
+ int iEpgID, unsigned int iUniqueBroadcastId) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery = PrepareSQL("SELECT * "
@@ -812,7 +812,8 @@ std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByUniqueBroadcastID(
return {};
}
-std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByDatabaseID(int iEpgID, int iDatabaseId)
+std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByDatabaseID(int iEpgID,
+ int iDatabaseId) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery = PrepareSQL("SELECT * "
@@ -838,8 +839,8 @@ std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByDatabaseID(int iEpgI
return {};
}
-std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByStartTime(int iEpgID,
- const CDateTime& startTime)
+std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByStartTime(
+ int iEpgID, const CDateTime& startTime) const
{
time_t start;
startTime.GetAsTime(start);
@@ -869,7 +870,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByStartTime(int iEpgID
}
std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMinStartTime(
- int iEpgID, const CDateTime& minStartTime)
+ int iEpgID, const CDateTime& minStartTime) const
{
time_t minStart;
minStartTime.GetAsTime(minStart);
@@ -899,8 +900,8 @@ std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMinStartTime(
return {};
}
-std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMaxEndTime(int iEpgID,
- const CDateTime& maxEndTime)
+std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMaxEndTime(
+ int iEpgID, const CDateTime& maxEndTime) const
{
time_t maxEnd;
maxEndTime.GetAsTime(maxEnd);
@@ -931,7 +932,7 @@ std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMaxEndTime(int iEpgI
}
std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTagsByMinStartMaxEndTime(
- int iEpgID, const CDateTime& minStartTime, const CDateTime& maxEndTime)
+ int iEpgID, const CDateTime& minStartTime, const CDateTime& maxEndTime) const
{
time_t minStart;
minStartTime.GetAsTime(minStart);
@@ -971,7 +972,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTagsByMinSta
}
std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTagsByMinEndMaxStartTime(
- int iEpgID, const CDateTime& minEndTime, const CDateTime& maxStartTime)
+ int iEpgID, const CDateTime& minEndTime, const CDateTime& maxStartTime) const
{
time_t minEnd;
minEndTime.GetAsTime(minEnd);
@@ -1034,7 +1035,7 @@ bool CPVREpgDatabase::QueueDeleteEpgTagsByMinEndMaxStartTimeQuery(int iEpgID,
return false;
}
-std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetAllEpgTags(int iEpgID)
+std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetAllEpgTags(int iEpgID) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery =
@@ -1060,7 +1061,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetAllEpgTags(int
return {};
}
-std::vector<std::string> CPVREpgDatabase::GetAllIconPaths(int iEpgID)
+std::vector<std::string> CPVREpgDatabase::GetAllIconPaths(int iEpgID) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery =
@@ -1086,7 +1087,7 @@ std::vector<std::string> CPVREpgDatabase::GetAllIconPaths(int iEpgID)
return {};
}
-bool CPVREpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime* lastScan)
+bool CPVREpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime* lastScan) const
{
bool bReturn = false;
@@ -1269,7 +1270,7 @@ bool CPVREpgDatabase::QueuePersistQuery(const CPVREpgInfoTag& tag)
return true;
}
-int CPVREpgDatabase::GetLastEPGId()
+int CPVREpgDatabase::GetLastEPGId() const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
std::string strQuery = PrepareSQL("SELECT MAX(idEpg) FROM epg");
@@ -1282,7 +1283,7 @@ int CPVREpgDatabase::GetLastEPGId()
/********** Saved searches methods **********/
std::shared_ptr<CPVREpgSearchFilter> CPVREpgDatabase::CreateEpgSearchFilter(
- bool bRadio, const std::unique_ptr<dbiplus::Dataset>& pDS)
+ bool bRadio, const std::unique_ptr<dbiplus::Dataset>& pDS) const
{
if (!pDS->eof())
{
@@ -1328,7 +1329,8 @@ std::shared_ptr<CPVREpgSearchFilter> CPVREpgDatabase::CreateEpgSearchFilter(
return {};
}
-std::vector<std::shared_ptr<CPVREpgSearchFilter>> CPVREpgDatabase::GetSavedSearches(bool bRadio)
+std::vector<std::shared_ptr<CPVREpgSearchFilter>> CPVREpgDatabase::GetSavedSearches(
+ bool bRadio) const
{
std::vector<std::shared_ptr<CPVREpgSearchFilter>> result;
@@ -1354,7 +1356,7 @@ std::vector<std::shared_ptr<CPVREpgSearchFilter>> CPVREpgDatabase::GetSavedSearc
return result;
}
-std::shared_ptr<CPVREpgSearchFilter> CPVREpgDatabase::GetSavedSearchById(bool bRadio, int iId)
+std::shared_ptr<CPVREpgSearchFilter> CPVREpgDatabase::GetSavedSearchById(bool bRadio, int iId) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
const std::string strQuery =
diff --git a/xbmc/pvr/epg/EpgDatabase.h b/xbmc/pvr/epg/EpgDatabase.h
index 580568d23a..b3cb2bb265 100644
--- a/xbmc/pvr/epg/EpgDatabase.h
+++ b/xbmc/pvr/epg/EpgDatabase.h
@@ -108,34 +108,34 @@ namespace PVR
* @param iEpgID The ID of the EPG.
* @return The entries.
*/
- std::vector<std::shared_ptr<CPVREpgInfoTag>> GetAllEpgTags(int iEpgID);
+ std::vector<std::shared_ptr<CPVREpgInfoTag>> GetAllEpgTags(int iEpgID) const;
/*!
* @brief Get all icon paths for a given EPG id.
* @param iEpgID The ID of the EPG.
* @return The entries.
*/
- std::vector<std::string> GetAllIconPaths(int iEpgID);
+ std::vector<std::string> GetAllIconPaths(int iEpgID) const;
/*!
* @brief Check whether this EPG has any tags.
* @param iEpgID The ID of the EPG.
* @return True in case there are tags, false otherwise.
*/
- bool HasTags(int iEpgID);
+ bool HasTags(int iEpgID) const;
/*!
* @brief Get the end time of the last tag in this EPG.
* @param iEpgID The ID of the EPG.
* @return The time.
*/
- CDateTime GetLastEndTime(int iEpgID);
+ CDateTime GetLastEndTime(int iEpgID) const;
/*!
* @brief Get the start and end time across all EPGs.
* @return The times; first: start time, second: end time.
*/
- std::pair<CDateTime, CDateTime> GetFirstAndLastEPGDate();
+ std::pair<CDateTime, CDateTime> GetFirstAndLastEPGDate() const;
/*!
* @brief Get the start time of the first tag with a start time greater than the given min time.
@@ -143,7 +143,7 @@ namespace PVR
* @param minStart The min start time.
* @return The time.
*/
- CDateTime GetMinStartTime(int iEpgID, const CDateTime& minStart);
+ CDateTime GetMinStartTime(int iEpgID, const CDateTime& minStart) const;
/*!
* @brief Get the end time of the first tag with an end time less than the given max time.
@@ -151,14 +151,15 @@ namespace PVR
* @param maxEnd The mx end time.
* @return The time.
*/
- CDateTime GetMaxEndTime(int iEpgID, const CDateTime& maxEnd);
+ CDateTime GetMaxEndTime(int iEpgID, const CDateTime& maxEnd) const;
/*!
* @brief Get all EPG tags matching the given search criteria.
* @param searchData The search criteria.
* @return The matching tags.
*/
- std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEpgTags(const PVREpgSearchData& searchData);
+ std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEpgTags(
+ const PVREpgSearchData& searchData) const;
/*!
* @brief Get an EPG tag given its EPG id and unique broadcast ID.
@@ -166,8 +167,8 @@ namespace PVR
* @param iUniqueBroadcastId The unique broadcast ID for the tag to get.
* @return The tag or nullptr, if not found.
*/
- std::shared_ptr<CPVREpgInfoTag> GetEpgTagByUniqueBroadcastID(int iEpgID,
- unsigned int iUniqueBroadcastId);
+ std::shared_ptr<CPVREpgInfoTag> GetEpgTagByUniqueBroadcastID(
+ int iEpgID, unsigned int iUniqueBroadcastId) const;
/*!
* @brief Get an EPG tag given its EPG id and database ID.
@@ -175,7 +176,7 @@ namespace PVR
* @param iDatabaseId The database ID for the tag to get.
* @return The tag or nullptr, if not found.
*/
- std::shared_ptr<CPVREpgInfoTag> GetEpgTagByDatabaseID(int iEpgID, int iDatabaseId);
+ std::shared_ptr<CPVREpgInfoTag> GetEpgTagByDatabaseID(int iEpgID, int iDatabaseId) const;
/*!
* @brief Get an EPG tag given its EPG ID and start time.
@@ -183,7 +184,8 @@ namespace PVR
* @param startTime The start time for the tag to get.
* @return The tag or nullptr, if not found.
*/
- std::shared_ptr<CPVREpgInfoTag> GetEpgTagByStartTime(int iEpgID, const CDateTime& startTime);
+ std::shared_ptr<CPVREpgInfoTag> GetEpgTagByStartTime(int iEpgID,
+ const CDateTime& startTime) const;
/*!
* @brief Get the next EPG tag matching the given EPG id and min start time.
@@ -192,7 +194,7 @@ namespace PVR
* @return The tag or nullptr, if not found.
*/
std::shared_ptr<CPVREpgInfoTag> GetEpgTagByMinStartTime(int iEpgID,
- const CDateTime& minStartTime);
+ const CDateTime& minStartTime) const;
/*!
* @brief Get the next EPG tag matching the given EPG id and max end time.
@@ -200,7 +202,8 @@ namespace PVR
* @param maxEndTime The max end time for the tag to get.
* @return The tag or nullptr, if not found.
*/
- std::shared_ptr<CPVREpgInfoTag> GetEpgTagByMaxEndTime(int iEpgID, const CDateTime& maxEndTime);
+ std::shared_ptr<CPVREpgInfoTag> GetEpgTagByMaxEndTime(int iEpgID,
+ const CDateTime& maxEndTime) const;
/*!
* @brief Get all EPG tags matching the given EPG id, min start time and max end time.
@@ -210,7 +213,7 @@ namespace PVR
* @return The tags or empty vector, if no tags were found.
*/
std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEpgTagsByMinStartMaxEndTime(
- int iEpgID, const CDateTime& minStartTime, const CDateTime& maxEndTime);
+ int iEpgID, const CDateTime& minStartTime, const CDateTime& maxEndTime) const;
/*!
* @brief Get all EPG tags matching the given EPG id, min end time and max start time.
@@ -220,7 +223,7 @@ namespace PVR
* @return The tags or empty vector, if no tags were found.
*/
std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEpgTagsByMinEndMaxStartTime(
- int iEpgID, const CDateTime& minEndTime, const CDateTime& maxStartTime);
+ int iEpgID, const CDateTime& minEndTime, const CDateTime& maxStartTime) const;
/*!
* @brief Write the query to delete all EPG tags in range of given EPG id, min end time and max
@@ -240,7 +243,7 @@ namespace PVR
* @param lastScan The last scan time or -1 if it wasn't found.
* @return True if the time was fetched successfully, false otherwise.
*/
- bool GetLastEpgScanTime(int iEpgId, CDateTime* lastScan);
+ bool GetLastEpgScanTime(int iEpgId, CDateTime* lastScan) const;
/*!
* @brief Write the query to update the last scan time for the given EPG to db query queue.
@@ -297,7 +300,7 @@ namespace PVR
/*!
* @return Last EPG id in the database
*/
- int GetLastEPGId();
+ int GetLastEPGId() const;
//@}
@@ -309,7 +312,7 @@ namespace PVR
* @param bRadio Whether to fetch saved searches for radio or TV.
* @return The searches.
*/
- std::vector<std::shared_ptr<CPVREpgSearchFilter>> GetSavedSearches(bool bRadio);
+ std::vector<std::shared_ptr<CPVREpgSearchFilter>> GetSavedSearches(bool bRadio) const;
/*!
* @brief Get the saved search matching the given id.
@@ -317,7 +320,7 @@ namespace PVR
* @param iId The id.
* @return The saved search or nullptr if not found.
*/
- std::shared_ptr<CPVREpgSearchFilter> GetSavedSearchById(bool bRadio, int iId);
+ std::shared_ptr<CPVREpgSearchFilter> GetSavedSearchById(bool bRadio, int iId) const;
/*!
* @brief Persist a search.
@@ -367,11 +370,12 @@ namespace PVR
int GetMinSchemaVersion() const override { return 4; }
- std::shared_ptr<CPVREpgInfoTag> CreateEpgTag(const std::unique_ptr<dbiplus::Dataset>& pDS);
+ std::shared_ptr<CPVREpgInfoTag> CreateEpgTag(
+ const std::unique_ptr<dbiplus::Dataset>& pDS) const;
std::shared_ptr<CPVREpgSearchFilter> CreateEpgSearchFilter(
- bool bRadio, const std::unique_ptr<dbiplus::Dataset>& pDS);
+ bool bRadio, const std::unique_ptr<dbiplus::Dataset>& pDS) const;
- CCriticalSection m_critSection;
+ mutable CCriticalSection m_critSection;
};
}
diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp
index 8b0930e5d2..a21894b64c 100644
--- a/xbmc/pvr/epg/EpgInfoTag.cpp
+++ b/xbmc/pvr/epg/EpgInfoTag.cpp
@@ -138,7 +138,7 @@ void CPVREpgInfoTag::SetChannelData(const std::shared_ptr<CPVREpgChannelData>& d
if (data)
m_channelData = data;
else
- m_channelData.reset(new CPVREpgChannelData);
+ m_channelData = std::make_shared<CPVREpgChannelData>();
}
bool CPVREpgInfoTag::operator==(const CPVREpgInfoTag& right) const
@@ -573,7 +573,7 @@ std::vector<PVR_EDL_ENTRY> CPVREpgInfoTag::GetEdl() const
std::vector<PVR_EDL_ENTRY> edls;
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
if (client && client->GetClientCapabilities().SupportsEpgTagEdl())
@@ -598,7 +598,7 @@ bool CPVREpgInfoTag::IsRecordable() const
bool bIsRecordable = false;
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
if (!client || (client->IsRecordable(shared_from_this(), bIsRecordable) != PVR_ERROR_NO_ERROR))
{
@@ -613,7 +613,7 @@ bool CPVREpgInfoTag::IsPlayable() const
bool bIsPlayable = false;
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
if (!client || (client->IsPlayable(shared_from_this(), bIsPlayable) != PVR_ERROR_NO_ERROR))
{
diff --git a/xbmc/pvr/epg/EpgSearchFilter.cpp b/xbmc/pvr/epg/EpgSearchFilter.cpp
index e1cc35fe71..4d96993a17 100644
--- a/xbmc/pvr/epg/EpgSearchFilter.cpp
+++ b/xbmc/pvr/epg/EpgSearchFilter.cpp
@@ -257,7 +257,7 @@ void CPVREpgSearchFilter::SetLastExecutedDateTime(const CDateTime& lastExecutedD
m_lastExecutedDateTime = lastExecutedDateTime;
}
-bool CPVREpgSearchFilter::MatchGenre(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchGenre(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
if (m_bEpgSearchDataFiltered)
return true;
@@ -282,7 +282,7 @@ bool CPVREpgSearchFilter::MatchGenre(const std::shared_ptr<CPVREpgInfoTag>& tag)
return true;
}
-bool CPVREpgSearchFilter::MatchDuration(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchDuration(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
bool bReturn(true);
@@ -295,7 +295,8 @@ bool CPVREpgSearchFilter::MatchDuration(const std::shared_ptr<CPVREpgInfoTag>& t
return bReturn;
}
-bool CPVREpgSearchFilter::MatchStartAndEndTimes(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchStartAndEndTimes(
+ const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
if (m_bEpgSearchDataFiltered)
return true;
@@ -310,7 +311,7 @@ bool CPVREpgSearchFilter::MatchStartAndEndTimes(const std::shared_ptr<CPVREpgInf
tag->EndAsUTC() <= m_searchData.m_endDateTime));
}
-bool CPVREpgSearchFilter::MatchSearchTerm(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchSearchTerm(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
bool bReturn(true);
@@ -329,7 +330,7 @@ bool CPVREpgSearchFilter::MatchSearchTerm(const std::shared_ptr<CPVREpgInfoTag>&
return bReturn;
}
-bool CPVREpgSearchFilter::FilterEntry(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::FilterEntry(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
return MatchGenre(tag) && MatchDuration(tag) && MatchStartAndEndTimes(tag) &&
MatchSearchTerm(tag) && MatchChannel(tag) && MatchChannelGroup(tag) && MatchTimers(tag) &&
@@ -340,20 +341,17 @@ void CPVREpgSearchFilter::RemoveDuplicates(std::vector<std::shared_ptr<CPVREpgIn
{
for (auto it = results.begin(); it != results.end();)
{
- 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();
+ it = results.erase(std::remove_if(results.begin(), results.end(),
+ [&it](const std::shared_ptr<const CPVREpgInfoTag>& entry) {
+ return *it != entry && (*it)->Title() == entry->Title() &&
+ (*it)->Plot() == entry->Plot() &&
+ (*it)->PlotOutline() == entry->PlotOutline();
}),
results.end());
}
}
-bool CPVREpgSearchFilter::MatchChannel(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchChannel(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
return tag && (tag->IsRadio() == m_bIsRadio) &&
(m_iClientID == -1 || tag->ClientID() == m_iClientID) &&
@@ -361,16 +359,16 @@ bool CPVREpgSearchFilter::MatchChannel(const std::shared_ptr<CPVREpgInfoTag>& ta
CServiceBroker::GetPVRManager().Clients()->IsCreatedClient(tag->ClientID());
}
-bool CPVREpgSearchFilter::MatchChannelGroup(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchChannelGroup(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
if (m_iChannelGroupID != -1)
{
if (!m_groupIdMatches.has_value())
{
- const std::shared_ptr<CPVRChannelGroup> group = CServiceBroker::GetPVRManager()
- .ChannelGroups()
- ->Get(m_bIsRadio)
- ->GetById(m_iChannelGroupID);
+ const std::shared_ptr<const CPVRChannelGroup> group = CServiceBroker::GetPVRManager()
+ .ChannelGroups()
+ ->Get(m_bIsRadio)
+ ->GetById(m_iChannelGroupID);
m_groupIdMatches =
group && (group->GetByUniqueID({tag->ClientID(), tag->UniqueChannelID()}) != nullptr);
}
@@ -381,23 +379,24 @@ bool CPVREpgSearchFilter::MatchChannelGroup(const std::shared_ptr<CPVREpgInfoTag
return true;
}
-bool CPVREpgSearchFilter::MatchFreeToAir(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchFreeToAir(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
if (m_bFreeToAirOnly)
{
- const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag);
+ const std::shared_ptr<const CPVRChannel> channel =
+ CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag);
return channel && !channel->IsEncrypted();
}
return true;
}
-bool CPVREpgSearchFilter::MatchTimers(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchTimers(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
return (!m_bIgnorePresentTimers || !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(tag));
}
-bool CPVREpgSearchFilter::MatchRecordings(const std::shared_ptr<CPVREpgInfoTag>& tag) const
+bool CPVREpgSearchFilter::MatchRecordings(const std::shared_ptr<const CPVREpgInfoTag>& tag) const
{
return (!m_bIgnorePresentRecordings || !CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(tag));
}
diff --git a/xbmc/pvr/epg/EpgSearchFilter.h b/xbmc/pvr/epg/EpgSearchFilter.h
index aa6a2e58c4..5966f6b057 100644
--- a/xbmc/pvr/epg/EpgSearchFilter.h
+++ b/xbmc/pvr/epg/EpgSearchFilter.h
@@ -47,7 +47,7 @@ namespace PVR
* @param tag The tag to check.
* @return True if this tag matches the filter, false if not.
*/
- bool FilterEntry(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
+ bool FilterEntry(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
/*!
* @brief remove duplicates from a list of epg tags.
@@ -133,15 +133,15 @@ namespace PVR
void SetChanged(bool bChanged) { m_bChanged = bChanged; }
private:
- bool MatchGenre(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchDuration(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchStartAndEndTimes(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchSearchTerm(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchChannel(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchChannelGroup(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchFreeToAir(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchTimers(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
- bool MatchRecordings(const std::shared_ptr<CPVREpgInfoTag>& tag) const;
+ bool MatchGenre(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchDuration(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchStartAndEndTimes(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchSearchTerm(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchChannel(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchChannelGroup(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchFreeToAir(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchTimers(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
+ bool MatchRecordings(const std::shared_ptr<const CPVREpgInfoTag>& tag) const;
bool m_bChanged = false;
diff --git a/xbmc/pvr/epg/EpgTagsCache.cpp b/xbmc/pvr/epg/EpgTagsCache.cpp
index 2c4d1112e2..fd11c4a5c5 100644
--- a/xbmc/pvr/epg/EpgTagsCache.cpp
+++ b/xbmc/pvr/epg/EpgTagsCache.cpp
@@ -76,7 +76,7 @@ bool CPVREpgTagsCache::Refresh()
m_nowActiveEnd > activeTime)
return false;
- const std::shared_ptr<CPVREpgInfoTag> prevNowActiveTag = m_nowActiveTag;
+ const std::shared_ptr<const CPVREpgInfoTag> prevNowActiveTag = m_nowActiveTag;
m_lastEndedTag.reset();
m_nowActiveTag.reset();
diff --git a/xbmc/pvr/filesystem/PVRGUIDirectory.cpp b/xbmc/pvr/filesystem/PVRGUIDirectory.cpp
index cf7baf0576..610ae39d33 100644
--- a/xbmc/pvr/filesystem/PVRGUIDirectory.cpp
+++ b/xbmc/pvr/filesystem/PVRGUIDirectory.cpp
@@ -66,14 +66,14 @@ bool GetRootDirectory(bool bRadio, CFileItemList& results)
{
std::shared_ptr<CFileItem> item;
- const std::shared_ptr<CPVRClients> clients = CServiceBroker::GetPVRManager().Clients();
+ const std::shared_ptr<const CPVRClients> clients = CServiceBroker::GetPVRManager().Clients();
// EPG
const bool bAnyClientSupportingEPG = clients->AnyClientSupportingEPG();
if (bAnyClientSupportingEPG)
{
- item.reset(
- new CFileItem(StringUtils::Format("pvr://guide/{}/", bRadio ? "radio" : "tv"), true));
+ item = std::make_shared<CFileItem>(
+ StringUtils::Format("pvr://guide/{}/", bRadio ? "radio" : "tv"), true);
item->SetLabel(g_localizeStrings.Get(19069)); // Guide
item->SetProperty("node.target", CWindowTranslator::TranslateWindow(bRadio ? WINDOW_RADIO_GUIDE
: WINDOW_TV_GUIDE));
@@ -82,8 +82,8 @@ bool GetRootDirectory(bool bRadio, CFileItemList& results)
}
// Channels
- item.reset(new CFileItem(
- bRadio ? CPVRChannelsPath::PATH_RADIO_CHANNELS : CPVRChannelsPath::PATH_TV_CHANNELS, true));
+ item = std::make_shared<CFileItem>(
+ bRadio ? CPVRChannelsPath::PATH_RADIO_CHANNELS : CPVRChannelsPath::PATH_TV_CHANNELS, true);
item->SetLabel(g_localizeStrings.Get(19019)); // Channels
item->SetProperty("node.target", CWindowTranslator::TranslateWindow(bRadio ? WINDOW_RADIO_CHANNELS
: WINDOW_TV_CHANNELS));
@@ -93,9 +93,9 @@ bool GetRootDirectory(bool bRadio, CFileItemList& results)
// Recordings
if (clients->AnyClientSupportingRecordings())
{
- item.reset(new CFileItem(bRadio ? CPVRRecordingsPath::PATH_ACTIVE_RADIO_RECORDINGS
- : CPVRRecordingsPath::PATH_ACTIVE_TV_RECORDINGS,
- true));
+ item = std::make_shared<CFileItem>(bRadio ? CPVRRecordingsPath::PATH_ACTIVE_RADIO_RECORDINGS
+ : CPVRRecordingsPath::PATH_ACTIVE_TV_RECORDINGS,
+ true);
item->SetLabel(g_localizeStrings.Get(19017)); // Recordings
item->SetProperty("node.target", CWindowTranslator::TranslateWindow(
bRadio ? WINDOW_RADIO_RECORDINGS : WINDOW_TV_RECORDINGS));
@@ -105,16 +105,16 @@ bool GetRootDirectory(bool bRadio, CFileItemList& results)
// Timers/Timer rules
// - always present, because Reminders are always available, no client support needed for this
- item.reset(new CFileItem(
- bRadio ? CPVRTimersPath::PATH_RADIO_TIMERS : CPVRTimersPath::PATH_TV_TIMERS, true));
+ item = std::make_shared<CFileItem>(
+ bRadio ? CPVRTimersPath::PATH_RADIO_TIMERS : CPVRTimersPath::PATH_TV_TIMERS, true);
item->SetLabel(g_localizeStrings.Get(19040)); // Timers
item->SetProperty("node.target", CWindowTranslator::TranslateWindow(bRadio ? WINDOW_RADIO_TIMERS
: WINDOW_TV_TIMERS));
item->SetArt("icon", "DefaultPVRTimers.png");
results.Add(item);
- item.reset(new CFileItem(
- bRadio ? CPVRTimersPath::PATH_RADIO_TIMER_RULES : CPVRTimersPath::PATH_TV_TIMER_RULES, true));
+ item = std::make_shared<CFileItem>(
+ bRadio ? CPVRTimersPath::PATH_RADIO_TIMER_RULES : CPVRTimersPath::PATH_TV_TIMER_RULES, true);
item->SetLabel(g_localizeStrings.Get(19138)); // Timer rules
item->SetProperty("node.target", CWindowTranslator::TranslateWindow(
bRadio ? WINDOW_RADIO_TIMER_RULES : WINDOW_TV_TIMER_RULES));
@@ -124,8 +124,8 @@ bool GetRootDirectory(bool bRadio, CFileItemList& results)
// Search
if (bAnyClientSupportingEPG)
{
- item.reset(new CFileItem(
- bRadio ? CPVREpgSearchPath::PATH_RADIO_SEARCH : CPVREpgSearchPath::PATH_TV_SEARCH, true));
+ item = std::make_shared<CFileItem>(
+ bRadio ? CPVREpgSearchPath::PATH_RADIO_SEARCH : CPVREpgSearchPath::PATH_TV_SEARCH, true);
item->SetLabel(g_localizeStrings.Get(137)); // Search
item->SetProperty("node.target", CWindowTranslator::TranslateWindow(bRadio ? WINDOW_RADIO_SEARCH
: WINDOW_TV_SEARCH));
@@ -154,17 +154,17 @@ bool CPVRGUIDirectory::GetDirectory(CFileItemList& results) const
{
std::shared_ptr<CFileItem> item;
- item.reset(new CFileItem(base + "channels/", true));
+ item = std::make_shared<CFileItem>(base + "channels/", true);
item->SetLabel(g_localizeStrings.Get(19019)); // Channels
item->SetLabelPreformatted(true);
results.Add(item);
- item.reset(new CFileItem(base + "recordings/active/", true));
+ item = std::make_shared<CFileItem>(base + "recordings/active/", true);
item->SetLabel(g_localizeStrings.Get(19017)); // Recordings
item->SetLabelPreformatted(true);
results.Add(item);
- item.reset(new CFileItem(base + "recordings/deleted/", true));
+ item = std::make_shared<CFileItem>(base + "recordings/deleted/", true);
item->SetLabel(g_localizeStrings.Get(19184)); // Deleted recordings
item->SetLabelPreformatted(true);
results.Add(item);
@@ -309,7 +309,7 @@ void GetSubDirectories(const CPVRRecordingsPath& recParentPath,
std::shared_ptr<CFileItem> item;
if (!results.Contains(strFilePath))
{
- item.reset(new CFileItem(strCurrent, true));
+ item = std::make_shared<CFileItem>(strCurrent, true);
item->SetPath(strFilePath);
item->SetLabel(strCurrent);
item->SetLabelPreformatted(true);
@@ -345,9 +345,6 @@ void GetSubDirectories(const CPVRRecordingsPath& recParentPath,
{
item->IncrementProperty("inprogressepisodes", 1);
}
- item->SetLabel2(StringUtils::Format("{} / {}", item->GetProperty("watchedepisodes").asString(),
- item->GetProperty("totalepisodes").asString()));
-
item->IncrementProperty("sizeinbytes", recording->GetSizeInBytes());
}
@@ -458,6 +455,109 @@ bool CPVRGUIDirectory::GetChannelGroupsDirectory(bool bRadio,
return false;
}
+namespace
+{
+std::shared_ptr<CPVRChannelGroupMember> GetLastWatchedChannelGroupMember(
+ const std::shared_ptr<CPVRChannel>& channel)
+{
+ const int lastGroupId{channel->LastWatchedGroupId()};
+ if (lastGroupId != PVR_GROUP_ID_UNNKOWN)
+ {
+ const std::shared_ptr<const CPVRChannelGroup> lastGroup{
+ CServiceBroker::GetPVRManager().ChannelGroups()->GetByIdFromAll(lastGroupId)};
+ if (lastGroup && !lastGroup->IsHidden() && !lastGroup->IsDeleted())
+ return lastGroup->GetByUniqueID(channel->StorageId());
+ }
+ return {};
+}
+
+std::shared_ptr<CPVRChannelGroupMember> GetFirstMatchingGroupMember(
+ const std::shared_ptr<CPVRChannel>& channel)
+{
+ CPVRChannelGroups* groups{
+ CServiceBroker::GetPVRManager().ChannelGroups()->Get(channel->IsRadio())};
+ if (groups)
+ {
+ const std::vector<std::shared_ptr<CPVRChannelGroup>> channelGroups{
+ groups->GetMembers(true /* exclude hidden */)};
+
+ for (const auto& channelGroup : channelGroups)
+ {
+ if (channelGroup->IsDeleted())
+ continue;
+
+ std::shared_ptr<CPVRChannelGroupMember> groupMember{
+ channelGroup->GetByUniqueID(channel->StorageId())};
+ if (groupMember)
+ return groupMember;
+ }
+ }
+ return {};
+}
+
+std::vector<std::shared_ptr<CPVRChannelGroupMember>> GetChannelGroupMembers(
+ const CPVRChannelsPath& path)
+{
+ const std::string& groupName{path.GetGroupName()};
+
+ std::shared_ptr<CPVRChannelGroup> group;
+ if (path.IsHiddenChannelGroup()) // hidden channels from the 'all channels' group
+ {
+ group = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(path.IsRadio());
+ }
+ else if (groupName == "*") // all channels across all groups
+ {
+ group = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(path.IsRadio());
+ if (group)
+ {
+ std::vector<std::shared_ptr<CPVRChannelGroupMember>> result;
+
+ const std::vector<std::shared_ptr<CPVRChannelGroupMember>> allGroupMembers{
+ group->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE)};
+ for (const auto& allGroupMember : allGroupMembers)
+ {
+ std::shared_ptr<CPVRChannelGroupMember> member{
+ GetLastWatchedChannelGroupMember(allGroupMember->Channel())};
+ if (member)
+ {
+ result.emplace_back(member);
+ continue; // Process next 'All channels' group member.
+ }
+
+ if (group->IsHidden())
+ {
+ // Very special case. 'All channels' group is hidden. Let's see what we get iterating all
+ // non-hidden / non-deleted groups. We must not return any 'All channels' group members,
+ // because their path is invalid (it contains the group).
+ member = GetFirstMatchingGroupMember(allGroupMember->Channel());
+ if (member)
+ result.emplace_back(member);
+ }
+ else
+ {
+ // Use the 'All channels' group member.
+ result.emplace_back(allGroupMember);
+ }
+ }
+ return result;
+ }
+ }
+ else
+ {
+ group = CServiceBroker::GetPVRManager()
+ .ChannelGroups()
+ ->Get(path.IsRadio())
+ ->GetByName(groupName, path.GetGroupClientID());
+ }
+
+ if (group)
+ return group->GetMembers(CPVRChannelGroup::Include::ALL);
+
+ CLog::LogF(LOGERROR, "Unable to obtain members for channel group '{}'", groupName);
+ return {};
+}
+} // unnamed namespace
+
bool CPVRGUIDirectory::GetChannelsDirectory(CFileItemList& results) const
{
const CPVRChannelsPath path(m_url.GetWithoutOptions());
@@ -468,13 +568,13 @@ bool CPVRGUIDirectory::GetChannelsDirectory(CFileItemList& results) const
std::shared_ptr<CFileItem> item;
// all tv channels
- item.reset(new CFileItem(CPVRChannelsPath::PATH_TV_CHANNELS, true));
+ item = std::make_shared<CFileItem>(CPVRChannelsPath::PATH_TV_CHANNELS, true);
item->SetLabel(g_localizeStrings.Get(19020)); // TV
item->SetLabelPreformatted(true);
results.Add(item);
// all radio channels
- item.reset(new CFileItem(CPVRChannelsPath::PATH_RADIO_CHANNELS, true));
+ item = std::make_shared<CFileItem>(CPVRChannelsPath::PATH_RADIO_CHANNELS, true);
item->SetLabel(g_localizeStrings.Get(19021)); // Radio
item->SetLabelPreformatted(true);
results.Add(item);
@@ -487,46 +587,20 @@ bool CPVRGUIDirectory::GetChannelsDirectory(CFileItemList& results) const
}
else if (path.IsChannelGroup())
{
- const std::string& strGroupName = path.GetGroupName();
- bool bShowHiddenChannels = path.IsHiddenChannelGroup();
-
- std::shared_ptr<CPVRChannelGroup> group;
- if (bShowHiddenChannels || strGroupName == "*") // all channels
+ const bool playedOnly{(m_url.HasOption("view") && (m_url.GetOption("view") == "lastplayed"))};
+ const bool showHiddenChannels{path.IsHiddenChannelGroup()};
+ const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers{
+ GetChannelGroupMembers(path)};
+ for (const auto& groupMember : groupMembers)
{
- group = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(path.IsRadio());
- }
- else
- {
- group = CServiceBroker::GetPVRManager()
- .ChannelGroups()
- ->Get(path.IsRadio())
- ->GetByName(strGroupName, path.GetGroupClientID());
- }
-
- if (group)
- {
- const bool playedOnly =
- (m_url.HasOption("view") && (m_url.GetOption("view") == "lastplayed"));
-
- const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers =
- group->GetMembers();
- for (const auto& groupMember : groupMembers)
- {
- if (bShowHiddenChannels != groupMember->Channel()->IsHidden())
- continue;
+ if (showHiddenChannels != groupMember->Channel()->IsHidden())
+ continue;
- if (playedOnly && !groupMember->Channel()->LastWatched())
- continue;
+ if (playedOnly && !groupMember->Channel()->LastWatched())
+ continue;
- results.Add(std::make_shared<CFileItem>(groupMember));
- }
+ results.Add(std::make_shared<CFileItem>(groupMember));
}
- else
- {
- CLog::LogF(LOGERROR, "Unable to obtain members of channel group '{}'", strGroupName);
- return false;
- }
-
return true;
}
}
@@ -575,7 +649,7 @@ bool GetTimersSubDirectory(const CPVRTimersPath& path,
if ((timer->IsRadio() == bRadio) && timer->HasParent() && (timer->ClientID() == iClientId) &&
(timer->ParentClientIndex() == iParentId) && (!bHideDisabled || !timer->IsDisabled()))
{
- item.reset(new CFileItem(timer));
+ item = std::make_shared<CFileItem>(timer);
const CPVRTimersPath timersPath(path.GetPath(), timer->ClientID(), timer->ClientIndex());
item->SetPath(timersPath.GetPath());
results.Add(item);
diff --git a/xbmc/pvr/guilib/CMakeLists.txt b/xbmc/pvr/guilib/CMakeLists.txt
index 3a73a68be5..67b6f902e5 100644
--- a/xbmc/pvr/guilib/CMakeLists.txt
+++ b/xbmc/pvr/guilib/CMakeLists.txt
@@ -30,6 +30,7 @@ set(HEADERS GUIEPGGridContainer.h
PVRGUIActionsTimers.h
PVRGUIChannelIconUpdater.h
PVRGUIChannelNavigator.h
- PVRGUIProgressHandler.h)
+ PVRGUIProgressHandler.h
+ PVRGUIRecordingsPlayActionProcessor.h)
core_add_library(pvr_guilib)
diff --git a/xbmc/pvr/guilib/GUIEPGGridContainer.cpp b/xbmc/pvr/guilib/GUIEPGGridContainer.cpp
index 8d79ab5edf..157c39fb91 100644
--- a/xbmc/pvr/guilib/GUIEPGGridContainer.cpp
+++ b/xbmc/pvr/guilib/GUIEPGGridContainer.cpp
@@ -690,7 +690,7 @@ void CGUIEPGGridContainer::UpdateItems()
const std::shared_ptr<CFileItem> prevItem = GetPrevItem().first;
if (prevItem)
{
- const std::shared_ptr<CPVREpgInfoTag> tag = prevItem->GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> tag = prevItem->GetEPGInfoTag();
if (tag && !tag->IsGapTag())
{
if (oldGridStart >= tag->StartAsUTC())
diff --git a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
index f4b4fee56f..f99515160c 100644
--- a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
+++ b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
@@ -41,7 +41,8 @@ void CGUIEPGGridContainerModel::SetInvalid()
std::shared_ptr<CFileItem> CGUIEPGGridContainerModel::CreateGapItem(int iChannel) const
{
- const std::shared_ptr<CPVRChannel> channel = m_channelItems[iChannel]->GetPVRChannelInfoTag();
+ const std::shared_ptr<const CPVRChannel> channel =
+ m_channelItems[iChannel]->GetPVRChannelInfoTag();
const std::shared_ptr<CPVREpgInfoTag> gapTag = channel->CreateEPGGapTag(m_gridStart, m_gridEnd);
return std::make_shared<CFileItem>(gapTag);
}
@@ -132,7 +133,7 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList>&
const CDateTimeSpan unit(0, 0, iRulerUnit * MINSPERBLOCK, 0);
for (; ruler < rulerEnd; ruler += unit)
{
- rulerItem.reset(new CFileItem(ruler.GetAsLocalizedTime("", false)));
+ rulerItem = std::make_shared<CFileItem>(ruler.GetAsLocalizedTime("", false));
rulerItem->SetLabel2(ruler.GetAsLocalizedDate(true));
m_rulerItems.emplace_back(rulerItem);
}
@@ -244,7 +245,7 @@ std::shared_ptr<CFileItem> CGUIEPGGridContainerModel::GetEpgTagsBefore(EpgTags&
// ptr comp does not work for gap tags!
// if ((*it) == epgTags.tags.front()->GetEPGInfoTag())
- const std::shared_ptr<CPVREpgInfoTag> t = epgTags.tags.front()->GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> t = epgTags.tags.front()->GetEPGInfoTag();
if ((*it)->StartAsUTC() == t->StartAsUTC() && (*it)->EndAsUTC() == t->EndAsUTC())
{
if (!result && IsEventMemberOfBlock(*it, iBlock))
@@ -306,7 +307,7 @@ std::shared_ptr<CFileItem> CGUIEPGGridContainerModel::GetEpgTagsAfter(EpgTags& e
// ptr comp does not work for gap tags!
// if ((*it) == epgTags.tags.back()->GetEPGInfoTag())
- const std::shared_ptr<CPVREpgInfoTag> t = epgTags.tags.back()->GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> t = epgTags.tags.back()->GetEPGInfoTag();
if ((*it)->StartAsUTC() == t->StartAsUTC() && (*it)->EndAsUTC() == t->EndAsUTC())
{
if (!result && IsEventMemberOfBlock(*it, iBlock))
@@ -373,10 +374,10 @@ void CGUIEPGGridContainerModel::FindChannelAndBlockIndex(int channelUid,
newChannelIndex = iCurrentChannel;
// find the new block index
- const std::shared_ptr<CPVREpg> epg = channel->GetPVRChannelInfoTag()->GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = channel->GetPVRChannelInfoTag()->GetEPG();
if (epg)
{
- const std::shared_ptr<CPVREpgInfoTag> tag = epg->GetTagByBroadcastId(broadcastUid);
+ const std::shared_ptr<const CPVREpgInfoTag> tag = epg->GetTagByBroadcastId(broadcastUid);
if (tag)
newBlockIndex = GetFirstEventBlock(tag) + eventOffset;
}
@@ -405,7 +406,7 @@ GridItem* CGUIEPGGridContainerModel::GetGridItemPtr(int iChannel, int iBlock) co
return nullptr;
}
- const std::shared_ptr<CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
const int startBlock = GetFirstEventBlock(epgTag);
const int endBlock = GetLastEventBlock(epgTag);
@@ -641,7 +642,7 @@ int CGUIEPGGridContainerModel::GetNowBlock() const
}
int CGUIEPGGridContainerModel::GetFirstEventBlock(
- const std::shared_ptr<CPVREpgInfoTag>& event) const
+ const std::shared_ptr<const CPVREpgInfoTag>& event) const
{
const CDateTime eventStart = event->StartAsUTC();
int diff;
@@ -658,14 +659,15 @@ int CGUIEPGGridContainerModel::GetFirstEventBlock(
return static_cast<int>(std::ceil(fBlockIndex));
}
-int CGUIEPGGridContainerModel::GetLastEventBlock(const std::shared_ptr<CPVREpgInfoTag>& event) const
+int CGUIEPGGridContainerModel::GetLastEventBlock(
+ const std::shared_ptr<const CPVREpgInfoTag>& event) const
{
// Last block of a tag is always the block calculated using event's end time, not rounded up.
return GetBlock(event->EndAsUTC());
}
-bool CGUIEPGGridContainerModel::IsEventMemberOfBlock(const std::shared_ptr<CPVREpgInfoTag>& event,
- int iBlock) const
+bool CGUIEPGGridContainerModel::IsEventMemberOfBlock(
+ const std::shared_ptr<const CPVREpgInfoTag>& event, int iBlock) const
{
const int iFirstBlock = GetFirstEventBlock(event);
const int iLastBlock = GetLastEventBlock(event);
diff --git a/xbmc/pvr/guilib/GUIEPGGridContainerModel.h b/xbmc/pvr/guilib/GUIEPGGridContainerModel.h
index 2e0a6d6ffa..2d99c568fe 100644
--- a/xbmc/pvr/guilib/GUIEPGGridContainerModel.h
+++ b/xbmc/pvr/guilib/GUIEPGGridContainerModel.h
@@ -105,9 +105,9 @@ public:
CDateTime GetStartTimeForBlock(int block) const;
int GetBlock(const CDateTime& datetime) const;
- int GetFirstEventBlock(const std::shared_ptr<CPVREpgInfoTag>& event) const;
- int GetLastEventBlock(const std::shared_ptr<CPVREpgInfoTag>& event) const;
- bool IsEventMemberOfBlock(const std::shared_ptr<CPVREpgInfoTag>& event, int iBlock) const;
+ int GetFirstEventBlock(const std::shared_ptr<const CPVREpgInfoTag>& event) const;
+ int GetLastEventBlock(const std::shared_ptr<const CPVREpgInfoTag>& event) const;
+ bool IsEventMemberOfBlock(const std::shared_ptr<const CPVREpgInfoTag>& event, int iBlock) const;
std::unique_ptr<CFileItemList> GetCurrentTimeLineItems(int firstChannel, int numChannels) const;
diff --git a/xbmc/pvr/guilib/PVRGUIActionListener.cpp b/xbmc/pvr/guilib/PVRGUIActionListener.cpp
index 3e683861e1..6a2c568f03 100644
--- a/xbmc/pvr/guilib/PVRGUIActionListener.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionListener.cpp
@@ -283,9 +283,9 @@ bool CPVRGUIActionListener::OnAction(const CAction& action)
int iChannelNumber = static_cast<int>(action.GetAmount(0));
int iSubChannelNumber = static_cast<int>(action.GetAmount(1));
- const std::shared_ptr<CPVRPlaybackState> playbackState =
+ const std::shared_ptr<const CPVRPlaybackState> playbackState =
CServiceBroker::GetPVRManager().PlaybackState();
- const std::shared_ptr<CPVRChannelGroup> activeGroup =
+ const std::shared_ptr<const CPVRChannelGroup> activeGroup =
playbackState->GetActiveChannelGroup(playbackState->IsPlayingRadio());
const std::shared_ptr<CPVRChannelGroupMember> groupMember =
activeGroup->GetByChannelNumber(CPVRChannelNumber(iChannelNumber, iSubChannelNumber));
diff --git a/xbmc/pvr/guilib/PVRGUIActionsChannels.cpp b/xbmc/pvr/guilib/PVRGUIActionsChannels.cpp
index 0527c1ba65..fb61fc2e6f 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsChannels.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsChannels.cpp
@@ -60,10 +60,11 @@ void CPVRChannelSwitchingInputHandler::AppendChannelNumberCharacter(char cCharac
void CPVRChannelSwitchingInputHandler::GetChannelNumbers(std::vector<std::string>& channelNumbers)
{
const CPVRManager& pvrMgr = CServiceBroker::GetPVRManager();
- const std::shared_ptr<CPVRChannel> playingChannel = pvrMgr.PlaybackState()->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> playingChannel =
+ pvrMgr.PlaybackState()->GetPlayingChannel();
if (playingChannel)
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
pvrMgr.ChannelGroups()->GetGroupAll(playingChannel->IsRadio());
if (group)
group->GetChannelNumbers(channelNumbers);
@@ -77,16 +78,42 @@ void CPVRChannelSwitchingInputHandler::OnInputDone()
SwitchToChannel(channelNumber);
}
+namespace
+{
+void UpdateActiveGroup(const std::shared_ptr<CPVRChannelGroupMember>& newChannel)
+{
+ const std::shared_ptr<CPVRPlaybackState> playbackState{
+ CServiceBroker::GetPVRManager().PlaybackState()};
+ const std::shared_ptr<const CPVRChannelGroupsContainer> groups{
+ CServiceBroker::GetPVRManager().ChannelGroups()};
+ const std::shared_ptr<CPVRChannelGroup> group{
+ groups->Get(newChannel->IsRadio())->GetById(newChannel->GroupID())};
+
+ // Switch group if new channel is not in the active group.
+ if (group && group != playbackState->GetActiveChannelGroup(newChannel->IsRadio()))
+ playbackState->SetActiveChannelGroup(group);
+}
+
+void TriggerChannelSwitchAction(const CPVRChannelNumber& channelNumber)
+{
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_INVALID, -1,
+ static_cast<void*>(new CAction(ACTION_CHANNEL_SWITCH,
+ static_cast<float>(channelNumber.GetChannelNumber()),
+ static_cast<float>(channelNumber.GetSubChannelNumber()))));
+}
+} // unnamed namespace
+
void CPVRChannelSwitchingInputHandler::SwitchToChannel(const CPVRChannelNumber& channelNumber)
{
if (channelNumber.IsValid() && CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying())
{
- const std::shared_ptr<CPVRChannel> playingChannel =
+ const std::shared_ptr<const CPVRChannel> playingChannel =
CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
if (playingChannel)
{
bool bRadio = playingChannel->IsRadio();
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
CServiceBroker::GetPVRManager().PlaybackState()->GetActiveChannelGroup(bRadio);
if (channelNumber != group->GetChannelNumber(playingChannel))
@@ -115,11 +142,8 @@ void CPVRChannelSwitchingInputHandler::SwitchToChannel(const CPVRChannelNumber&
if (groupMember)
{
- CServiceBroker::GetAppMessenger()->PostMsg(
- TMSG_GUI_ACTION, WINDOW_INVALID, -1,
- static_cast<void*>(new CAction(
- ACTION_CHANNEL_SWITCH, static_cast<float>(channelNumber.GetChannelNumber()),
- static_cast<float>(channelNumber.GetSubChannelNumber()))));
+ UpdateActiveGroup(groupMember);
+ TriggerChannelSwitchAction(channelNumber);
}
}
}
@@ -128,23 +152,19 @@ void CPVRChannelSwitchingInputHandler::SwitchToChannel(const CPVRChannelNumber&
void CPVRChannelSwitchingInputHandler::SwitchToPreviousChannel()
{
- const std::shared_ptr<CPVRPlaybackState> playbackState =
+ const std::shared_ptr<const CPVRPlaybackState> playbackState =
CServiceBroker::GetPVRManager().PlaybackState();
if (playbackState->IsPlaying())
{
- const std::shared_ptr<CPVRChannel> playingChannel = playbackState->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> playingChannel = playbackState->GetPlayingChannel();
if (playingChannel)
{
const std::shared_ptr<CPVRChannelGroupMember> groupMember =
playbackState->GetPreviousToLastPlayedChannelGroupMember(playingChannel->IsRadio());
if (groupMember)
{
- const CPVRChannelNumber channelNumber = groupMember->ChannelNumber();
- CServiceBroker::GetAppMessenger()->SendMsg(
- TMSG_GUI_ACTION, WINDOW_INVALID, -1,
- static_cast<void*>(new CAction(
- ACTION_CHANNEL_SWITCH, static_cast<float>(channelNumber.GetChannelNumber()),
- static_cast<float>(channelNumber.GetSubChannelNumber()))));
+ UpdateActiveGroup(groupMember);
+ TriggerChannelSwitchAction(groupMember->ChannelNumber());
}
}
}
@@ -307,7 +327,7 @@ bool CPVRGUIActionsChannels::StartChannelScan(int clientId)
}
std::shared_ptr<CPVRChannelGroupMember> CPVRGUIActionsChannels::GetChannelGroupMember(
- const std::shared_ptr<CPVRChannel>& channel) const
+ const std::shared_ptr<const CPVRChannel>& channel) const
{
if (!channel)
return {};
@@ -325,7 +345,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRGUIActionsChannels::GetChannelGroupM
if (std::find(windowIDs.cbegin(), windowIDs.cend(), activeWindowID) == windowIDs.cend())
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
CServiceBroker::GetPVRManager().PlaybackState()->GetActiveChannelGroup(channel->IsRadio());
if (group)
groupMember = group->GetByUniqueID(channel->StorageId());
@@ -334,7 +354,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRGUIActionsChannels::GetChannelGroupM
// as fallback, obtain the member from the 'all channels' group
if (!groupMember)
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(channel->IsRadio());
if (group)
groupMember = group->GetByUniqueID(channel->StorageId());
@@ -406,15 +426,16 @@ std::string CPVRGUIActionsChannels::GetSelectedChannelPath(bool bRadio) const
CPVRManager& mgr = CServiceBroker::GetPVRManager();
// if preselect playing channel is activated, return the path of the playing channel, if any.
- const std::shared_ptr<CPVRChannelGroupMember> playingChannel =
+ const std::shared_ptr<const CPVRChannelGroupMember> playingChannel =
mgr.PlaybackState()->GetPlayingChannelGroupMember();
if (playingChannel && playingChannel->IsRadio() == bRadio)
- return playingChannel->Path();
+ return GetChannelGroupMember(playingChannel->Channel())->Path();
- const std::shared_ptr<CPVREpgInfoTag> playingTag = mgr.PlaybackState()->GetPlayingEpgTag();
+ const std::shared_ptr<const CPVREpgInfoTag> playingTag =
+ mgr.PlaybackState()->GetPlayingEpgTag();
if (playingTag && playingTag->IsRadio() == bRadio)
{
- const std::shared_ptr<CPVRChannel> channel =
+ const std::shared_ptr<const CPVRChannel> channel =
mgr.ChannelGroups()->GetChannelForEpgTag(playingTag);
if (channel)
return GetChannelGroupMember(channel)->Path();
diff --git a/xbmc/pvr/guilib/PVRGUIActionsChannels.h b/xbmc/pvr/guilib/PVRGUIActionsChannels.h
index a7656fead4..956f94f03f 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsChannels.h
+++ b/xbmc/pvr/guilib/PVRGUIActionsChannels.h
@@ -108,7 +108,7 @@ public:
* @return the group member or nullptr if not found.
*/
std::shared_ptr<CPVRChannelGroupMember> GetChannelGroupMember(
- const std::shared_ptr<CPVRChannel>& channel) const;
+ const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @brief Get a channel group member for the given item, either from the currently active group
diff --git a/xbmc/pvr/guilib/PVRGUIActionsEPG.cpp b/xbmc/pvr/guilib/PVRGUIActionsEPG.cpp
index 8b5cc9b1e4..15d3aedd08 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsEPG.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsEPG.cpp
@@ -55,7 +55,7 @@ PVR::CGUIWindowPVRSearchBase* GetSearchWindow(bool bRadio)
bool CPVRGUIActionsEPG::ShowEPGInfo(const CFileItem& item) const
{
- const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel());
+ const std::shared_ptr<const CPVRChannel> channel(CPVRItem(item).GetChannel());
if (channel && CServiceBroker::GetPVRManager().Get<PVR::GUI::Parental>().CheckParentalLock(
channel) != ParentalCheckResult::SUCCESS)
return false;
@@ -83,7 +83,7 @@ bool CPVRGUIActionsEPG::ShowEPGInfo(const CFileItem& item) const
bool CPVRGUIActionsEPG::ShowChannelEPG(const CFileItem& item) const
{
- const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel());
+ const std::shared_ptr<const CPVRChannel> channel(CPVRItem(item).GetChannel());
if (channel && CServiceBroker::GetPVRManager().Get<PVR::GUI::Parental>().CheckParentalLock(
channel) != ParentalCheckResult::SUCCESS)
return false;
diff --git a/xbmc/pvr/guilib/PVRGUIActionsParentalControl.cpp b/xbmc/pvr/guilib/PVRGUIActionsParentalControl.cpp
index 96947c4381..ab4bfd5575 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsParentalControl.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsParentalControl.cpp
@@ -30,7 +30,7 @@ CPVRGUIActionsParentalControl::CPVRGUIActionsParentalControl()
}
ParentalCheckResult CPVRGUIActionsParentalControl::CheckParentalLock(
- const std::shared_ptr<CPVRChannel>& channel) const
+ const std::shared_ptr<const CPVRChannel>& channel) const
{
if (!CServiceBroker::GetPVRManager().IsParentalLocked(channel))
return ParentalCheckResult::SUCCESS;
diff --git a/xbmc/pvr/guilib/PVRGUIActionsParentalControl.h b/xbmc/pvr/guilib/PVRGUIActionsParentalControl.h
index 921f5970ca..b24b633226 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsParentalControl.h
+++ b/xbmc/pvr/guilib/PVRGUIActionsParentalControl.h
@@ -35,7 +35,7 @@ public:
* @param channel The channel to do the check for.
* @return the result of the check (success, failed, or canceled by user).
*/
- ParentalCheckResult CheckParentalLock(const std::shared_ptr<CPVRChannel>& channel) const;
+ ParentalCheckResult CheckParentalLock(const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @brief Open Numeric dialog to check for parental PIN.
diff --git a/xbmc/pvr/guilib/PVRGUIActionsPlayback.cpp b/xbmc/pvr/guilib/PVRGUIActionsPlayback.cpp
index 7c40898bdb..504351698a 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsPlayback.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsPlayback.cpp
@@ -60,13 +60,13 @@ bool CPVRGUIActionsPlayback::CheckResumeRecording(const CFileItem& item) const
{
bool bPlayIt(true);
- const VIDEO::GUILIB::SelectAction action =
+ const VIDEO::GUILIB::Action action =
VIDEO::GUILIB::CVideoSelectActionProcessorBase::ChoosePlayOrResume(item);
- if (action == VIDEO::GUILIB::SELECT_ACTION_RESUME)
+ if (action == VIDEO::GUILIB::ACTION_RESUME)
{
const_cast<CFileItem*>(&item)->SetStartOffset(STARTOFFSET_RESUME);
}
- else if (action == VIDEO::GUILIB::SELECT_ACTION_PLAY)
+ else if (action == VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING)
{
const_cast<CFileItem*>(&item)->SetStartOffset(0);
}
@@ -113,7 +113,7 @@ void CPVRGUIActionsPlayback::StartPlayback(CFileItem* item,
const CPVRStreamProperties* epgProps) const
{
// Obtain dynamic playback url and properties from the respective pvr client
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
+ const std::shared_ptr<const CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
if (client)
{
CPVRStreamProperties props;
@@ -255,7 +255,7 @@ bool CPVRGUIActionsPlayback::PlayEpgTag(const CFileItem& item) const
}
// Obtain dynamic playback url and properties from the respective pvr client
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(epgTag->ClientID());
if (!client)
return false;
@@ -288,7 +288,7 @@ bool CPVRGUIActionsPlayback::SwitchToChannel(const CFileItem& item, bool bCheckR
return false;
std::shared_ptr<CPVRRecording> recording;
- const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel());
+ const std::shared_ptr<const CPVRChannel> channel(CPVRItem(item).GetChannel());
if (channel)
{
bool bSwitchToFullscreen =
@@ -405,7 +405,7 @@ bool CPVRGUIActionsPlayback::SwitchToChannel(PlaybackType type) const
if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV())
return true;
- const std::shared_ptr<CPVRChannelGroup> allGroup =
+ const std::shared_ptr<const CPVRChannelGroup> allGroup =
CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAllTV();
if (allGroup)
groupMember = allGroup->GetLastPlayedChannelGroupMember();
@@ -429,7 +429,7 @@ bool CPVRGUIActionsPlayback::SwitchToChannel(PlaybackType type) const
else
{
// if we don't, find the active channel group of the demanded type and play it's first channel
- const std::shared_ptr<CPVRChannelGroup> channelGroup =
+ const std::shared_ptr<const CPVRChannelGroup> channelGroup =
CServiceBroker::GetPVRManager().PlaybackState()->GetActiveChannelGroup(bIsRadio);
if (channelGroup)
{
@@ -473,7 +473,7 @@ bool CPVRGUIActionsPlayback::PlayChannelOnStartup() const
if (!groupMember)
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
CServiceBroker::GetPVRManager().ChannelGroups()->Get(playRadio)->GetGroupAll();
auto channels = group->GetMembers();
if (channels.empty())
@@ -531,7 +531,7 @@ void CPVRGUIActionsPlayback::SeekForward()
time_t playbackStartTime = CServiceBroker::GetDataCacheCore().GetStartTime();
if (playbackStartTime > 0)
{
- const std::shared_ptr<CPVRChannel> playingChannel =
+ const std::shared_ptr<const CPVRChannel> playingChannel =
CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
if (playingChannel)
{
@@ -569,7 +569,7 @@ void CPVRGUIActionsPlayback::SeekBackward(unsigned int iThreshold)
time_t playbackStartTime = CServiceBroker::GetDataCacheCore().GetStartTime();
if (playbackStartTime > 0)
{
- const std::shared_ptr<CPVRChannel> playingChannel =
+ const std::shared_ptr<const CPVRChannel> playingChannel =
CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
if (playingChannel)
{
diff --git a/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp b/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp
index 1a0a99a7a2..5a5ef23662 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp
@@ -166,7 +166,7 @@ bool CPVRGUIActionsPowerManagement::AllLocalBackendsIdle(
bool CPVRGUIActionsPowerManagement::EventOccursOnLocalBackend(
const std::shared_ptr<CPVRTimerInfoTag>& event) const
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(CFileItem(event));
if (client)
{
diff --git a/xbmc/pvr/guilib/PVRGUIActionsRecordings.cpp b/xbmc/pvr/guilib/PVRGUIActionsRecordings.cpp
index df23f38751..c69c909b9a 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsRecordings.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsRecordings.cpp
@@ -159,7 +159,7 @@ private:
const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
if (client)
{
- const std::shared_ptr<CPVRRecording> recording = item->GetPVRRecordingInfoTag();
+ const std::shared_ptr<const CPVRRecording> recording = item->GetPVRRecordingInfoTag();
return client->SetRecordingPlayCount(*recording, recording->GetLocalPlayCount()) ==
PVR_ERROR_NO_ERROR;
}
diff --git a/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp b/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp
index be8e313aa6..e515a9dd00 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp
@@ -371,7 +371,7 @@ bool CPVRGUIActionsTimers::SetRecordingOnChannel(const std::shared_ptr<CPVRChann
ParentalCheckResult::SUCCESS)
return bReturn;
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(channel->ClientID());
if (client && client->GetClientCapabilities().SupportsTimers())
{
@@ -534,7 +534,7 @@ bool CPVRGUIActionsTimers::ToggleTimer(const CFileItem& item) const
if (!item.HasEPGInfoTag())
return false;
- const std::shared_ptr<CPVRTimerInfoTag> timer(CPVRItem(item).GetTimerInfoTag());
+ const std::shared_ptr<const CPVRTimerInfoTag> timer(CPVRItem(item).GetTimerInfoTag());
if (timer)
{
if (timer->IsRecording())
@@ -654,7 +654,7 @@ bool CPVRGUIActionsTimers::DeleteTimer(const CFileItem& item,
bool bDeleteRule) const
{
std::shared_ptr<CPVRTimerInfoTag> timer;
- const std::shared_ptr<CPVRRecording> recording(CPVRItem(item).GetRecording());
+ const std::shared_ptr<const CPVRRecording> recording(CPVRItem(item).GetRecording());
if (recording)
timer = recording->GetRecordingTimer();
@@ -745,11 +745,11 @@ bool CPVRGUIActionsTimers::DeleteTimer(const std::shared_ptr<CPVRTimerInfoTag>&
return false;
}
-bool CPVRGUIActionsTimers::ConfirmDeleteTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer,
+bool CPVRGUIActionsTimers::ConfirmDeleteTimer(const std::shared_ptr<const CPVRTimerInfoTag>& timer,
bool& bDeleteRule) const
{
bool bConfirmed(false);
- const std::shared_ptr<CPVRTimerInfoTag> parentTimer(
+ const std::shared_ptr<const CPVRTimerInfoTag> parentTimer(
CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer));
if (parentTimer && parentTimer->GetTimerType()->AllowsDelete())
@@ -792,7 +792,7 @@ bool CPVRGUIActionsTimers::StopRecording(const CFileItem& item) const
}
bool CPVRGUIActionsTimers::ConfirmStopRecording(
- const std::shared_ptr<CPVRTimerInfoTag>& timer) const
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer) const
{
return CGUIDialogYesNo::ShowAndGetInput(
CVariant{847}, // "Confirm stop recording"
@@ -802,7 +802,9 @@ bool CPVRGUIActionsTimers::ConfirmStopRecording(
namespace
{
-std::string GetAnnouncerText(const std::shared_ptr<CPVRTimerInfoTag>& timer, int idEpg, int idNoEpg)
+std::string GetAnnouncerText(const std::shared_ptr<const CPVRTimerInfoTag>& timer,
+ int idEpg,
+ int idNoEpg)
{
std::string text;
if (timer->IsEpgBased())
@@ -820,12 +822,12 @@ std::string GetAnnouncerText(const std::shared_ptr<CPVRTimerInfoTag>& timer, int
return text;
}
-void AddEventLogEntry(const std::shared_ptr<CPVRTimerInfoTag>& timer, int idEpg, int idNoEpg)
+void AddEventLogEntry(const std::shared_ptr<const CPVRTimerInfoTag>& timer, int idEpg, int idNoEpg)
{
std::string name;
std::string icon;
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(timer->GetTimerType()->GetClientId());
if (client)
{
@@ -882,7 +884,7 @@ void CPVRGUIActionsTimers::AnnounceReminder(const std::shared_ptr<CPVRTimerInfoT
std::string text = GetAnnouncerText(timer, 19307, 19308); // Reminder for ...
bool bCanRecord = false;
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(timer->ClientID());
if (client && client->GetClientCapabilities().SupportsTimers())
{
diff --git a/xbmc/pvr/guilib/PVRGUIActionsTimers.h b/xbmc/pvr/guilib/PVRGUIActionsTimers.h
index f6ae8fe005..9c08ddaa0b 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsTimers.h
+++ b/xbmc/pvr/guilib/PVRGUIActionsTimers.h
@@ -206,14 +206,15 @@ private:
* timer. out, for one shot timer not scheduled by a timer rule: ignored
* @return true, to proceed with delete, false otherwise.
*/
- bool ConfirmDeleteTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer, bool& bDeleteRule) const;
+ bool ConfirmDeleteTimer(const std::shared_ptr<const CPVRTimerInfoTag>& timer,
+ bool& bDeleteRule) const;
/*!
* @brief Open a dialog to confirm stop recording.
* @param timer the recording to stop (actually the timer to delete).
* @return true, to proceed with delete, false otherwise.
*/
- bool ConfirmStopRecording(const std::shared_ptr<CPVRTimerInfoTag>& timer) const;
+ bool ConfirmStopRecording(const std::shared_ptr<const CPVRTimerInfoTag>& timer) const;
/*!
* @brief Announce and process a reminder timer.
diff --git a/xbmc/pvr/guilib/PVRGUIActionsUtils.cpp b/xbmc/pvr/guilib/PVRGUIActionsUtils.cpp
index 28e5582043..907c645dd3 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsUtils.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActionsUtils.cpp
@@ -10,12 +10,22 @@
#include "FileItem.h"
#include "ServiceBroker.h"
+#include "filesystem/Directory.h"
#include "pvr/PVRManager.h"
+#include "pvr/channels/PVRChannelGroupsContainer.h"
#include "pvr/guilib/PVRGUIActionsEPG.h"
#include "pvr/guilib/PVRGUIActionsRecordings.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
namespace PVR
{
+bool CPVRGUIActionsUtils::HasInfoForItem(const CFileItem& item) const
+{
+ return item.HasPVRRecordingInfoTag() || item.HasPVRChannelInfoTag() ||
+ item.HasPVRTimerInfoTag() || item.HasEPGSearchFilter();
+}
+
bool CPVRGUIActionsUtils::OnInfo(const CFileItem& item)
{
if (item.HasPVRRecordingInfoTag())
@@ -33,4 +43,61 @@ bool CPVRGUIActionsUtils::OnInfo(const CFileItem& item)
return false;
}
+namespace
+{
+std::shared_ptr<CFileItem> LoadRecordingFileOrFolderItem(const CFileItem& item)
+{
+ if (URIUtils::IsPVRRecordingFileOrFolder(item.GetPath()))
+ {
+ //! @todo prop misused to detect loaded state for recording folder item
+ if (item.HasPVRRecordingInfoTag() || item.HasProperty("watchedepisodes"))
+ return std::make_shared<CFileItem>(item); // already loaded
+
+ const std::string parentPath{URIUtils::GetParentPath(item.GetPath())};
+
+ //! @todo optimize, find a way to set the details of the item without loading parent directory.
+ CFileItemList items;
+ if (XFILE::CDirectory::GetDirectory(parentPath, items, "", XFILE::DIR_FLAG_DEFAULTS))
+ {
+ const std::string& path{item.GetPath()};
+ const auto it = std::find_if(items.cbegin(), items.cend(),
+ [&path](const auto& entry) { return entry->GetPath() == path; });
+ if (it != items.cend())
+ return *it;
+ }
+ }
+ return {};
+}
+
+std::shared_ptr<CFileItem> LoadChannelItem(const CFileItem& item)
+{
+ if (URIUtils::IsPVRChannel(item.GetPath()))
+ {
+ if (item.HasPVRChannelInfoTag())
+ return std::make_shared<CFileItem>(item); // already loaded
+
+ const auto groups{CServiceBroker::GetPVRManager().ChannelGroups()};
+ const std::shared_ptr<CPVRChannelGroupMember> groupMember{
+ groups->GetChannelGroupMemberByPath(item.GetPath())};
+ if (groupMember)
+ return std::make_shared<CFileItem>(groupMember);
+ }
+ return {};
+}
+} // unnamed namespace
+
+std::shared_ptr<CFileItem> CPVRGUIActionsUtils::LoadItem(const CFileItem& item)
+{
+ std::shared_ptr<CFileItem> loadedItem{LoadRecordingFileOrFolderItem(item)};
+ if (loadedItem)
+ return loadedItem;
+
+ loadedItem = LoadChannelItem(item);
+ if (loadedItem)
+ return loadedItem;
+
+ CLog::LogFC(LOGWARNING, LOGPVR, "Error loading item details (path={})", item.GetPath());
+ return {};
+}
+
} // namespace PVR
diff --git a/xbmc/pvr/guilib/PVRGUIActionsUtils.h b/xbmc/pvr/guilib/PVRGUIActionsUtils.h
index a8805482e2..199e53607a 100644
--- a/xbmc/pvr/guilib/PVRGUIActionsUtils.h
+++ b/xbmc/pvr/guilib/PVRGUIActionsUtils.h
@@ -10,6 +10,8 @@
#include "pvr/IPVRComponent.h"
+#include <memory>
+
class CFileItem;
namespace PVR
@@ -21,11 +23,26 @@ public:
~CPVRGUIActionsUtils() override = default;
/*!
+ * @brief Check whether OnInfo supports the given item.
+ * @param item The item.
+ * @return True if supported, false otherwise.
+ */
+ bool HasInfoForItem(const CFileItem& item) const;
+
+ /*!
* @brief Process info action for the given item.
* @param item The item.
+ * @return True on success, false otherwise.
*/
bool OnInfo(const CFileItem& item);
+ /*!
+ * @brief Load item details (create recording info tag etc.).
+ * @param item The item.
+ * @return Loaded item on success, nullptr otherwise.
+ */
+ std::shared_ptr<CFileItem> LoadItem(const CFileItem& item);
+
private:
CPVRGUIActionsUtils(const CPVRGUIActionsUtils&) = delete;
CPVRGUIActionsUtils const& operator=(CPVRGUIActionsUtils const&) = delete;
diff --git a/xbmc/pvr/guilib/PVRGUIChannelIconUpdater.cpp b/xbmc/pvr/guilib/PVRGUIChannelIconUpdater.cpp
index a239fe2a92..26859dd63a 100644
--- a/xbmc/pvr/guilib/PVRGUIChannelIconUpdater.cpp
+++ b/xbmc/pvr/guilib/PVRGUIChannelIconUpdater.cpp
@@ -25,6 +25,7 @@
#include "utils/log.h"
#include <map>
+#include <memory>
#include <string>
#include <vector>
@@ -57,8 +58,8 @@ void CPVRGUIChannelIconUpdater::SearchAndUpdateMissingChannelIcons() const
std::unique_ptr<CPVRGUIProgressHandler> progressHandler;
if (!m_groups.empty())
- progressHandler.reset(
- new CPVRGUIProgressHandler(g_localizeStrings.Get(19286))); // Searching for channel icons
+ progressHandler = std::make_unique<CPVRGUIProgressHandler>(
+ g_localizeStrings.Get(19286)); // Searching for channel icons
for (const auto& group : m_groups)
{
diff --git a/xbmc/pvr/guilib/PVRGUIChannelNavigator.cpp b/xbmc/pvr/guilib/PVRGUIChannelNavigator.cpp
index 0ccfdf8822..cb3054371d 100644
--- a/xbmc/pvr/guilib/PVRGUIChannelNavigator.cpp
+++ b/xbmc/pvr/guilib/PVRGUIChannelNavigator.cpp
@@ -23,6 +23,7 @@
#include "utils/JobManager.h"
#include "utils/XTimeUtils.h"
+#include <memory>
#include <mutex>
using namespace KODI::GUILIB::GUIINFO;
@@ -182,7 +183,7 @@ std::shared_ptr<CPVRChannelGroupMember> CPVRGUIChannelNavigator::GetNextOrPrevCh
if (bPlayingTV || bPlayingRadio)
{
- const std::shared_ptr<CPVRChannelGroup> group =
+ const std::shared_ptr<const CPVRChannelGroup> group =
CServiceBroker::GetPVRManager().PlaybackState()->GetActiveChannelGroup(bPlayingRadio);
if (group)
{
@@ -316,7 +317,7 @@ void CPVRGUIChannelNavigator::HideInfo()
{
m_currentChannel = m_playingChannel;
if (m_playingChannel)
- item.reset(new CFileItem(m_playingChannel));
+ item = std::make_shared<CFileItem>(m_playingChannel);
}
CheckAndPublishPreviewAndPlayerShowInfoChangedEvent();
@@ -350,7 +351,7 @@ void CPVRGUIChannelNavigator::SetPlayingChannel(
{
m_currentChannel = m_playingChannel;
if (m_playingChannel)
- item.reset(new CFileItem(m_playingChannel));
+ item = std::make_shared<CFileItem>(m_playingChannel);
}
CheckAndPublishPreviewAndPlayerShowInfoChangedEvent();
diff --git a/xbmc/pvr/guilib/PVRGUIRecordingsPlayActionProcessor.h b/xbmc/pvr/guilib/PVRGUIRecordingsPlayActionProcessor.h
new file mode 100644
index 0000000000..80df2dfc96
--- /dev/null
+++ b/xbmc/pvr/guilib/PVRGUIRecordingsPlayActionProcessor.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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 "FileItem.h"
+#include "ServiceBroker.h"
+#include "pvr/PVRManager.h"
+#include "pvr/guilib/PVRGUIActionsPlayback.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
+
+namespace PVR
+{
+class CGUIPVRRecordingsPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
+{
+public:
+ explicit CGUIPVRRecordingsPlayActionProcessor(const std::shared_ptr<CFileItem>& item)
+ : CVideoPlayActionProcessorBase(item)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ Play();
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ Play();
+ return true;
+ }
+
+private:
+ void Play()
+ {
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(
+ *m_item, false /* no resume check */);
+ }
+};
+} // namespace PVR
diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
index 185e55165e..01b4336501 100644
--- a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
+++ b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
@@ -233,7 +233,7 @@ void CPVRGUIInfo::UpdateQualityData()
CSettings::SETTING_PVRPLAYBACK_SIGNALQUALITY))
return;
- const std::shared_ptr<CPVRPlaybackState> playbackState =
+ const std::shared_ptr<const CPVRPlaybackState> playbackState =
CServiceBroker::GetPVRManager().PlaybackState();
if (!playbackState)
return;
@@ -244,7 +244,7 @@ void CPVRGUIInfo::UpdateQualityData()
const int channelUid = playbackState->GetPlayingChannelUniqueID();
if (channelUid > 0)
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().Clients()->GetCreatedClient(
playbackState->GetPlayingClientID());
if (client)
@@ -257,7 +257,7 @@ void CPVRGUIInfo::UpdateQualityData()
void CPVRGUIInfo::UpdateDescrambleData()
{
- const std::shared_ptr<CPVRPlaybackState> playbackState =
+ const std::shared_ptr<const CPVRPlaybackState> playbackState =
CServiceBroker::GetPVRManager().PlaybackState();
if (!playbackState)
return;
@@ -268,7 +268,7 @@ void CPVRGUIInfo::UpdateDescrambleData()
const int channelUid = playbackState->GetPlayingChannelUniqueID();
if (channelUid > 0)
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().Clients()->GetCreatedClient(
playbackState->GetPlayingClientID());
if (client)
@@ -283,7 +283,7 @@ void CPVRGUIInfo::UpdateMisc()
{
const CPVRManager& mgr = CServiceBroker::GetPVRManager();
bool bStarted = mgr.IsStarted();
- const std::shared_ptr<CPVRPlaybackState> state = mgr.PlaybackState();
+ const std::shared_ptr<const CPVRPlaybackState> state = mgr.PlaybackState();
/* safe to fetch these unlocked, since they're updated from the same thread as this one */
const std::string strPlayingClientName = bStarted ? state->GetPlayingClientName() : "";
@@ -360,7 +360,7 @@ std::string GetAsLocalizedDateTimeString(const CDateTime& datetime)
return datetime.IsValid() ? datetime.GetAsLocalizedDateTime(false, false) : "";
}
-std::string GetEpgTagTitle(const std::shared_ptr<CPVREpgInfoTag>& epgTag)
+std::string GetEpgTagTitle(const std::shared_ptr<const CPVREpgInfoTag>& epgTag)
{
if (epgTag)
{
@@ -383,7 +383,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
const CGUIInfo& info,
std::string& strValue) const
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = item->GetPVRTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = item->GetPVRTimerInfoTag();
if (timer)
{
switch (info.m_info)
@@ -444,7 +444,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
}
}
- const std::shared_ptr<CPVRRecording> recording(item->GetPVRRecordingInfoTag());
+ const std::shared_ptr<const CPVRRecording> recording(item->GetPVRRecordingInfoTag());
if (recording)
{
// Note: CPVRRecoding is derived from CVideoInfoTag. All base class properties will be handled
@@ -508,7 +508,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
case VIDEOPLAYER_CHANNEL_NUMBER:
case LISTITEM_CHANNEL_NUMBER:
{
- const std::shared_ptr<CPVRChannelGroupMember> groupMember =
+ const std::shared_ptr<const CPVRChannelGroupMember> groupMember =
CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetChannelGroupMember(*item);
if (groupMember)
{
@@ -562,7 +562,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
return false;
}
- const std::shared_ptr<CPVREpgSearchFilter> filter = item->GetEPGSearchFilter();
+ const std::shared_ptr<const CPVREpgSearchFilter> filter = item->GetEPGSearchFilter();
if (filter)
{
switch (info.m_info)
@@ -580,8 +580,8 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
return false;
}
- std::shared_ptr<CPVREpgInfoTag> epgTag;
- std::shared_ptr<CPVRChannel> channel;
+ std::shared_ptr<const CPVREpgInfoTag> epgTag;
+ std::shared_ptr<const CPVRChannel> channel;
if (item->IsPVRChannel() || item->IsEPG() || item->IsPVRTimer())
{
CPVRItem pvrItem(item);
@@ -788,7 +788,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
{
case MUSICPLAYER_CHANNEL_NAME:
{
- const std::shared_ptr<CPVRRadioRDSInfoTag> rdsTag = channel->GetRadioRDSInfoTag();
+ const std::shared_ptr<const CPVRRadioRDSInfoTag> rdsTag = channel->GetRadioRDSInfoTag();
if (rdsTag)
{
strValue = rdsTag->GetProgStation();
@@ -841,7 +841,7 @@ bool CPVRGUIInfo::GetPVRLabel(const CFileItem* item,
{
case PVR_EPG_EVENT_ICON:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
if (epgTag)
{
@@ -851,14 +851,14 @@ bool CPVRGUIInfo::GetPVRLabel(const CFileItem* item,
}
case PVR_EPG_EVENT_DURATION:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
strValue = m_timesInfo.GetEpgEventDuration(epgTag, static_cast<TIME_FORMAT>(info.GetData1()));
return true;
}
case PVR_EPG_EVENT_ELAPSED_TIME:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
strValue =
m_timesInfo.GetEpgEventElapsedTime(epgTag, static_cast<TIME_FORMAT>(info.GetData1()));
@@ -866,7 +866,7 @@ bool CPVRGUIInfo::GetPVRLabel(const CFileItem* item,
}
case PVR_EPG_EVENT_REMAINING_TIME:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
strValue =
m_timesInfo.GetEpgEventRemainingTime(epgTag, static_cast<TIME_FORMAT>(info.GetData1()));
@@ -874,7 +874,7 @@ bool CPVRGUIInfo::GetPVRLabel(const CFileItem* item,
}
case PVR_EPG_EVENT_FINISH_TIME:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
strValue =
m_timesInfo.GetEpgEventFinishTime(epgTag, static_cast<TIME_FORMAT>(info.GetData1()));
@@ -1070,7 +1070,7 @@ bool CPVRGUIInfo::GetRadioRDSLabel(const CFileItem* item,
if (!item->HasPVRChannelInfoTag())
return false;
- const std::shared_ptr<CPVRRadioRDSInfoTag> tag =
+ const std::shared_ptr<const CPVRRadioRDSInfoTag> tag =
item->GetPVRChannelInfoTag()->GetRadioRDSInfoTag();
if (tag)
{
@@ -1250,7 +1250,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerInt(const CFileItem* item,
case LISTITEM_PROGRESS:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
if (epgTag)
iValue = static_cast<int>(epgTag->ProgressPercentage());
}
@@ -1267,14 +1267,14 @@ bool CPVRGUIInfo::GetPVRInt(const CFileItem* item, const CGUIInfo& info, int& iV
{
case PVR_EPG_EVENT_DURATION:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
iValue = m_timesInfo.GetEpgEventDuration(epgTag);
return true;
}
case PVR_EPG_EVENT_PROGRESS:
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
(item->IsPVRChannel() || item->IsEPG()) ? CPVRItem(item).GetEpgInfoTag() : nullptr;
iValue = m_timesInfo.GetEpgEventProgress(epgTag);
return true;
@@ -1361,7 +1361,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
}
else if (item->IsEPG() || item->IsPVRTimer())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = timer->IsRecording();
return true;
@@ -1375,7 +1375,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_INPROGRESS:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
if (epgTag)
bValue = epgTag->IsActive();
return true;
@@ -1384,7 +1384,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_HASTIMER:
if (item->IsPVRChannel() || item->IsEPG() || item->IsPVRTimer())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = true;
return true;
@@ -1393,7 +1393,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_HASTIMERSCHEDULE:
if (item->IsPVRChannel() || item->IsEPG() || item->IsPVRTimer())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = timer->HasParent();
return true;
@@ -1402,7 +1402,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_HASREMINDER:
if (item->IsPVRChannel() || item->IsEPG() || item->IsPVRTimer())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = timer->IsReminder();
return true;
@@ -1411,7 +1411,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_HASREMINDERRULE:
if (item->IsPVRChannel() || item->IsEPG() || item->IsPVRTimer())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = timer->IsReminder() && timer->HasParent();
return true;
@@ -1420,7 +1420,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_TIMERISACTIVE:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = timer->IsActive();
break;
@@ -1429,7 +1429,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_TIMERHASCONFLICT:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = timer->HasConflict();
return true;
@@ -1438,7 +1438,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_TIMERHASERROR:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = CPVRItem(item).GetTimerInfoTag();
if (timer)
bValue = (timer->IsBroken() && !timer->HasConflict());
return true;
@@ -1447,7 +1447,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_HASRECORDING:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
if (epgTag)
bValue = !!CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(epgTag);
return true;
@@ -1456,7 +1456,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_HAS_EPG:
if (item->IsPVRChannel() || item->IsEPG() || item->IsPVRTimer())
{
- const std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag();
bValue = (epgTag != nullptr);
return true;
}
@@ -1464,7 +1464,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case LISTITEM_ISENCRYPTED:
if (item->IsPVRChannel() || item->IsEPG())
{
- const std::shared_ptr<CPVRChannel> channel = CPVRItem(item).GetChannel();
+ const std::shared_ptr<const CPVRChannel> channel = CPVRItem(item).GetChannel();
if (channel)
bValue = channel->IsEncrypted();
return true;
@@ -1491,7 +1491,8 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
}
else if (item->IsPVRChannel())
{
- const std::shared_ptr<CPVREpgInfoTag> epgNow = item->GetPVRChannelInfoTag()->GetEPGNow();
+ const std::shared_ptr<const CPVREpgInfoTag> epgNow =
+ item->GetPVRChannelInfoTag()->GetEPGNow();
bValue = epgNow ? epgNow->IsNew() : false;
return true;
}
@@ -1514,7 +1515,8 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
}
else if (item->IsPVRChannel())
{
- const std::shared_ptr<CPVREpgInfoTag> epgNow = item->GetPVRChannelInfoTag()->GetEPGNow();
+ const std::shared_ptr<const CPVREpgInfoTag> epgNow =
+ item->GetPVRChannelInfoTag()->GetEPGNow();
bValue = epgNow ? epgNow->IsPremiere() : false;
return true;
}
@@ -1537,7 +1539,8 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
}
else if (item->IsPVRChannel())
{
- const std::shared_ptr<CPVREpgInfoTag> epgNow = item->GetPVRChannelInfoTag()->GetEPGNow();
+ const std::shared_ptr<const CPVREpgInfoTag> epgNow =
+ item->GetPVRChannelInfoTag()->GetEPGNow();
bValue = epgNow ? epgNow->IsFinale() : false;
return true;
}
@@ -1560,11 +1563,25 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
}
else if (item->IsPVRChannel())
{
- const std::shared_ptr<CPVREpgInfoTag> epgNow = item->GetPVRChannelInfoTag()->GetEPGNow();
+ const std::shared_ptr<const CPVREpgInfoTag> epgNow =
+ item->GetPVRChannelInfoTag()->GetEPGNow();
bValue = epgNow ? epgNow->IsLive() : false;
return true;
}
break;
+ case LISTITEM_ISPLAYING:
+ if (item->IsPVRChannel())
+ {
+ const std::shared_ptr<const CPVRChannel> playingChannel{
+ CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel()};
+ if (playingChannel)
+ {
+ const std::shared_ptr<const CPVRChannel> channel{item->GetPVRChannelInfoTag()};
+ bValue = (channel->StorageId() == playingChannel->StorageId());
+ return true;
+ }
+ }
+ break;
case MUSICPLAYER_CONTENT:
case VIDEOPLAYER_CONTENT:
if (item->IsPVRChannel())
@@ -1590,10 +1607,10 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem* item,
case VIDEOPLAYER_CAN_RESUME_LIVE_TV:
if (item->IsPVRRecording())
{
- const std::shared_ptr<CPVRRecording> recording = item->GetPVRRecordingInfoTag();
- const std::shared_ptr<CPVREpg> epg =
+ const std::shared_ptr<const CPVRRecording> recording = item->GetPVRRecordingInfoTag();
+ const std::shared_ptr<const CPVREpg> epg =
recording->Channel() ? recording->Channel()->GetEPG() : nullptr;
- const std::shared_ptr<CPVREpgInfoTag> epgTag =
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag =
CServiceBroker::GetPVRManager().EpgContainer().GetTagById(epg,
recording->BroadcastUid());
bValue = (epgTag && epgTag->IsActive());
@@ -1695,7 +1712,7 @@ bool CPVRGUIInfo::GetRadioRDSBool(const CFileItem* item, const CGUIInfo& info, b
if (!item->HasPVRChannelInfoTag())
return false;
- const std::shared_ptr<CPVRRadioRDSInfoTag> tag =
+ const std::shared_ptr<const CPVRRadioRDSInfoTag> tag =
item->GetPVRChannelInfoTag()->GetRadioRDSInfoTag();
if (tag)
{
@@ -1872,7 +1889,7 @@ void CPVRGUIInfo::CharInfoEncryption(std::string& strValue) const
}
else
{
- const std::shared_ptr<CPVRChannel> channel =
+ const std::shared_ptr<const CPVRChannel> channel =
CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
if (channel)
{
diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimerInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUITimerInfo.cpp
index 24a468a0b5..0f9dab9703 100644
--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimerInfo.cpp
+++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimerInfo.cpp
@@ -95,7 +95,7 @@ void CPVRGUITimerInfo::UpdateTimersToggle()
std::vector<std::shared_ptr<CPVRTimerInfoTag>> activeTags = GetActiveRecordings();
if (m_iTimerInfoToggleCurrent < activeTags.size())
{
- const std::shared_ptr<CPVRTimerInfoTag> tag = activeTags.at(m_iTimerInfoToggleCurrent);
+ const std::shared_ptr<const CPVRTimerInfoTag> tag = activeTags.at(m_iTimerInfoToggleCurrent);
strActiveTimerTitle = tag->Title();
strActiveTimerChannelName = tag->ChannelName();
strActiveTimerChannelIcon = tag->ChannelIcon();
@@ -133,7 +133,7 @@ void CPVRGUITimerInfo::UpdateNextTimer()
std::string strNextRecordingTime;
std::string strNextTimerInfo;
- const std::shared_ptr<CPVRTimerInfoTag> timer = GetNextActiveTimer();
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = GetNextActiveTimer();
if (timer)
{
strNextRecordingTitle = timer->Title();
diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
index e5dfdb9ea0..4691fd6033 100644
--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
+++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
@@ -53,7 +53,8 @@ void CPVRGUITimesInfo::Reset()
void CPVRGUITimesInfo::UpdatePlayingTag()
{
- const std::shared_ptr<CPVRChannel> currentChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> currentChannel =
+ CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
std::shared_ptr<CPVREpgInfoTag> currentTag = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingEpgTag();
if (currentChannel || currentTag)
@@ -61,11 +62,12 @@ void CPVRGUITimesInfo::UpdatePlayingTag()
if (currentChannel && !currentTag)
currentTag = currentChannel->GetEPGNow();
- const std::shared_ptr<CPVRChannelGroupsContainer> groups = CServiceBroker::GetPVRManager().ChannelGroups();
+ const std::shared_ptr<const CPVRChannelGroupsContainer> groups =
+ CServiceBroker::GetPVRManager().ChannelGroups();
std::unique_lock<CCriticalSection> lock(m_critSection);
- const std::shared_ptr<CPVRChannel> playingChannel =
+ const std::shared_ptr<const CPVRChannel> playingChannel =
m_playingEpgTag ? groups->GetChannelForEpgTag(m_playingEpgTag) : nullptr;
if (!m_playingEpgTag || !currentTag || !playingChannel || !currentChannel ||
@@ -91,7 +93,8 @@ void CPVRGUITimesInfo::UpdatePlayingTag()
}
else
{
- const std::shared_ptr<CPVRRecording> recording = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingRecording();
+ const std::shared_ptr<const CPVRRecording> recording =
+ CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingRecording();
if (recording)
{
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -115,7 +118,8 @@ void CPVRGUITimesInfo::UpdateTimeshiftData()
int64_t iPlayTime, iMinTime, iMaxTime;
CServiceBroker::GetDataCacheCore().GetPlayTimes(iStartTime, iPlayTime, iMinTime, iMaxTime);
bool bPlaying = CServiceBroker::GetDataCacheCore().GetSpeed() == 1.0f;
- const std::shared_ptr<CPVRChannel> playingChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
+ const std::shared_ptr<const CPVRChannel> playingChannel =
+ CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -284,13 +288,15 @@ std::string CPVRGUITimesInfo::GetTimeshiftProgressEndTime(TIME_FORMAT format) co
return TimeToTimeString(m_iTimeshiftProgressEndTime, format, false);
}
-std::string CPVRGUITimesInfo::GetEpgEventDuration(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
+std::string CPVRGUITimesInfo::GetEpgEventDuration(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
return StringUtils::SecondsToTimeString(GetEpgEventDuration(epgTag), format);
}
-std::string CPVRGUITimesInfo::GetEpgEventElapsedTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
+std::string CPVRGUITimesInfo::GetEpgEventElapsedTime(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
{
int iElapsed = 0;
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -302,13 +308,15 @@ std::string CPVRGUITimesInfo::GetEpgEventElapsedTime(const std::shared_ptr<CPVRE
return StringUtils::SecondsToTimeString(iElapsed, format);
}
-std::string CPVRGUITimesInfo::GetEpgEventRemainingTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
+std::string CPVRGUITimesInfo::GetEpgEventRemainingTime(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
return StringUtils::SecondsToTimeString(GetRemainingTime(epgTag), format);
}
-std::string CPVRGUITimesInfo::GetEpgEventFinishTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
+std::string CPVRGUITimesInfo::GetEpgEventFinishTime(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
{
CDateTime finish = CDateTime::GetCurrentDateTime();
finish += CDateTimeSpan(0, 0, 0, GetRemainingTime(epgTag));
@@ -336,7 +344,7 @@ int CPVRGUITimesInfo::GetElapsedTime() const
}
}
-int CPVRGUITimesInfo::GetRemainingTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+int CPVRGUITimesInfo::GetRemainingTime(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
@@ -399,7 +407,7 @@ int CPVRGUITimesInfo::GetTimeshiftProgressBufferEnd() const
return std::lrintf(static_cast<float>(m_iTimeshiftEndTime - m_iTimeshiftProgressStartTime) / m_iTimeshiftProgressDuration * 100);
}
-int CPVRGUITimesInfo::GetEpgEventDuration(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+int CPVRGUITimesInfo::GetEpgEventDuration(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
@@ -408,7 +416,7 @@ int CPVRGUITimesInfo::GetEpgEventDuration(const std::shared_ptr<CPVREpgInfoTag>&
return m_iDuration;
}
-int CPVRGUITimesInfo::GetEpgEventProgress(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+int CPVRGUITimesInfo::GetEpgEventProgress(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.h b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.h
index 47dd9f5a60..73b194f78e 100644
--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.h
+++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.h
@@ -36,10 +36,14 @@ namespace PVR
std::string GetTimeshiftProgressStartTime(TIME_FORMAT format) const;
std::string GetTimeshiftProgressEndTime(TIME_FORMAT format) const;
- std::string GetEpgEventDuration(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const;
- std::string GetEpgEventElapsedTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const;
- std::string GetEpgEventRemainingTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const;
- std::string GetEpgEventFinishTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const;
+ std::string GetEpgEventDuration(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
+ TIME_FORMAT format) const;
+ std::string GetEpgEventElapsedTime(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
+ TIME_FORMAT format) const;
+ std::string GetEpgEventRemainingTime(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
+ TIME_FORMAT format) const;
+ std::string GetEpgEventFinishTime(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
+ TIME_FORMAT format) const;
std::string GetEpgEventSeekTime(int iSeekSize, TIME_FORMAT format) const;
// GUI info ints
@@ -51,8 +55,8 @@ namespace PVR
int GetTimeshiftProgressBufferStart() const;
int GetTimeshiftProgressBufferEnd() const;
- int GetEpgEventDuration(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
- int GetEpgEventProgress(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ int GetEpgEventDuration(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
+ int GetEpgEventProgress(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
// GUI info bools
bool IsTimeshifting() const;
@@ -65,12 +69,12 @@ namespace PVR
static std::string TimeToTimeString(time_t datetime, TIME_FORMAT format, bool withSeconds);
int GetElapsedTime() const;
- int GetRemainingTime(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ int GetRemainingTime(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
mutable CCriticalSection m_critSection;
- std::shared_ptr<CPVREpgInfoTag> m_playingEpgTag;
- std::shared_ptr<CPVRChannel> m_playingChannel;
+ std::shared_ptr<const CPVREpgInfoTag> m_playingEpgTag;
+ std::shared_ptr<const CPVRChannel> m_playingChannel;
time_t m_iStartTime;
unsigned int m_iDuration;
diff --git a/xbmc/pvr/providers/PVRProviders.cpp b/xbmc/pvr/providers/PVRProviders.cpp
index 6d751a2fb9..547026a13d 100644
--- a/xbmc/pvr/providers/PVRProviders.cpp
+++ b/xbmc/pvr/providers/PVRProviders.cpp
@@ -102,7 +102,8 @@ void CPVRProviders::Unload()
bool CPVRProviders::LoadFromDatabase(const std::vector<std::shared_ptr<CPVRClient>>& clients)
{
- const std::shared_ptr<CPVRDatabase> database = CServiceBroker::GetPVRManager().GetTVDatabase();
+ const std::shared_ptr<const CPVRDatabase> database =
+ CServiceBroker::GetPVRManager().GetTVDatabase();
if (database)
{
m_iLastId = database->GetMaxProviderId();
@@ -174,7 +175,7 @@ bool CPVRProviders::UpdateDefaultEntries(const CPVRProvidersContainer& newProvid
for (std::vector<std::shared_ptr<CPVRProvider>>::iterator it = m_providers.begin();
it != m_providers.end();)
{
- const std::shared_ptr<CPVRProvider> provider = *it;
+ const std::shared_ptr<const CPVRProvider> provider = *it;
if (!newProviders.GetByClient(provider->GetClientId(), provider->GetUniqueId()))
{
// provider was not found
@@ -222,7 +223,7 @@ bool CPVRProviders::UpdateClientEntries(const CPVRProvidersContainer& newProvide
// check for deleted providers
for (auto it = m_providers.begin(); it != m_providers.end();)
{
- const std::shared_ptr<CPVRProvider> provider = *it;
+ const std::shared_ptr<const CPVRProvider> provider = *it;
if (!newProviders.GetByClient(provider->GetClientId(), provider->GetUniqueId()))
{
const bool bIgnoreProvider =
@@ -355,7 +356,7 @@ void CPVRProviders::RemoveEntry(const std::shared_ptr<CPVRProvider>& provider)
m_providers.erase(
std::remove_if(m_providers.begin(), m_providers.end(),
- [&provider](const std::shared_ptr<CPVRProvider>& providerToRemove) {
+ [&provider](const std::shared_ptr<const CPVRProvider>& providerToRemove) {
return provider->GetClientId() == providerToRemove->GetClientId() &&
provider->GetUniqueId() == providerToRemove->GetUniqueId();
}),
diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp
index a2e1cbd131..362bf8c064 100644
--- a/xbmc/pvr/recordings/PVRRecording.cpp
+++ b/xbmc/pvr/recordings/PVRRecording.cpp
@@ -124,14 +124,14 @@ CPVRRecording::CPVRRecording(const PVR_RECORDING& recording, unsigned int iClien
}
else
{
- const std::shared_ptr<CPVRChannel> channel(Channel());
+ const std::shared_ptr<const CPVRChannel> channel(Channel());
if (channel)
{
m_bRadio = channel->IsRadio();
}
else
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(m_iClientId);
bool bSupportsRadio = client && client->GetClientCapabilities().SupportsRadio();
if (bSupportsRadio && client && client->GetClientCapabilities().SupportsTV())
@@ -364,7 +364,8 @@ bool CPVRRecording::SetResumePoint(double timeInSeconds,
CBookmark CPVRRecording::GetResumePoint() const
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_iClientId);
if (client && client->GetClientCapabilities().SupportsRecordingsLastPlayedPosition() &&
m_resumePointRefetchTimeout.IsTimePast())
{
@@ -388,7 +389,8 @@ CBookmark CPVRRecording::GetResumePoint() const
bool CPVRRecording::UpdateRecordingSize()
{
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_iClientId);
if (client && client->GetClientCapabilities().SupportsRecordingsSize() &&
m_recordingSizeRefetchTimeout.IsTimePast())
{
@@ -433,7 +435,8 @@ std::vector<PVR_EDL_ENTRY> CPVRRecording::GetEdl() const
{
std::vector<PVR_EDL_ENTRY> edls;
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_iClientId);
if (client && client->GetClientCapabilities().SupportsRecordingsEdl())
client->GetRecordingEdl(*this, edls);
diff --git a/xbmc/pvr/recordings/PVRRecordings.cpp b/xbmc/pvr/recordings/PVRRecordings.cpp
index 2e6edede63..b6049ee6fb 100644
--- a/xbmc/pvr/recordings/PVRRecordings.cpp
+++ b/xbmc/pvr/recordings/PVRRecordings.cpp
@@ -219,7 +219,7 @@ void CPVRRecordings::UpdateFromClient(const std::shared_ptr<CPVRRecording>& tag,
}
std::shared_ptr<CPVRRecording> CPVRRecordings::GetRecordingForEpgTag(
- const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (!epgTag)
return {};
@@ -329,7 +329,7 @@ CVideoDatabase& CPVRRecordings::GetVideoDatabase()
{
if (!m_database)
{
- m_database.reset(new CVideoDatabase());
+ m_database = std::make_unique<CVideoDatabase>();
m_database->Open();
if (!m_database->IsOpen())
diff --git a/xbmc/pvr/recordings/PVRRecordings.h b/xbmc/pvr/recordings/PVRRecordings.h
index 1a7753f5ae..7184b6c3e8 100644
--- a/xbmc/pvr/recordings/PVRRecordings.h
+++ b/xbmc/pvr/recordings/PVRRecordings.h
@@ -98,7 +98,7 @@ public:
* @return The requested recording, or an empty recordingptr if none was found.
*/
std::shared_ptr<CPVRRecording> GetRecordingForEpgTag(
- const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
/*!
* @brief Erase stale texture db entries and image files.
diff --git a/xbmc/pvr/recordings/PVRRecordingsPath.cpp b/xbmc/pvr/recordings/PVRRecordingsPath.cpp
index 29bb7cff22..7e413571f3 100644
--- a/xbmc/pvr/recordings/PVRRecordingsPath.cpp
+++ b/xbmc/pvr/recordings/PVRRecordingsPath.cpp
@@ -38,9 +38,9 @@ CPVRRecordingsPath::CPVRRecordingsPath(const std::string& strPath)
((segments.at(3) == "active") || (segments.at(3) == "deleted")));
if (m_bValid)
{
- m_bRoot = (m_bValid && (segments.size() == 4));
- m_bRadio = (m_bValid && (segments.at(2) == "radio"));
- m_bActive = (m_bValid && (segments.at(3) == "active"));
+ m_bRoot = (segments.size() == 4);
+ m_bRadio = (segments.at(2) == "radio");
+ m_bActive = (segments.at(3) == "active");
if (m_bRoot)
strVarPath.append("/");
@@ -132,6 +132,20 @@ std::string CPVRRecordingsPath::GetUnescapedDirectoryPath() const
return CURL::Decode(m_directoryPath);
}
+namespace
+{
+bool PathHasParent(const std::string& path, const std::string& parent)
+{
+ if (path == parent)
+ return true;
+
+ if (!parent.empty() && parent.back() != '/')
+ return StringUtils::StartsWith(path, parent + '/');
+
+ return StringUtils::StartsWith(path, parent);
+}
+} // unnamed namespace
+
std::string CPVRRecordingsPath::GetUnescapedSubDirectoryPath(const std::string& strPath) const
{
// note: strPath must be unescaped.
@@ -144,7 +158,7 @@ std::string CPVRRecordingsPath::GetUnescapedSubDirectoryPath(const std::string&
/* adding "/" to make sure that base matches the complete folder name and not only parts of it */
if (!strUnescapedDirectoryPath.empty() &&
(strUsePath.size() <= strUnescapedDirectoryPath.size() ||
- !URIUtils::PathHasParent(strUsePath, strUnescapedDirectoryPath)))
+ !PathHasParent(strUsePath, strUnescapedDirectoryPath)))
return strReturn;
strUsePath.erase(0, strUnescapedDirectoryPath.size());
diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.cpp b/xbmc/pvr/timers/PVRTimerInfoTag.cpp
index 0a11cf86e0..a965492765 100644
--- a/xbmc/pvr/timers/PVRTimerInfoTag.cpp
+++ b/xbmc/pvr/timers/PVRTimerInfoTag.cpp
@@ -61,7 +61,8 @@ CPVRTimerInfoTag::CPVRTimerInfoTag(bool bRadio /* = false */)
std::shared_ptr<CPVRTimerType> type;
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_iClientId);
if (client && client->GetClientCapabilities().SupportsTimers())
{
// default to first available type for given client
@@ -132,7 +133,8 @@ CPVRTimerInfoTag::CPVRTimerInfoTag(const PVR_TIMER& timer,
if (m_iClientIndex == PVR_TIMER_NO_CLIENT_INDEX)
CLog::LogF(LOGERROR, "Invalid client index supplied by client {} (must be > 0)!", m_iClientId);
- const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
+ const std::shared_ptr<const CPVRClient> client =
+ CServiceBroker::GetPVRManager().GetClient(m_iClientId);
if (client && client->GetClientCapabilities().SupportsTimers())
{
// begin compat section
@@ -233,7 +235,7 @@ void CPVRTimerInfoTag::FillAddonData(PVR_TIMER& timer) const
StartAsUTC().GetAsTime(start);
EndAsUTC().GetAsTime(end);
FirstDayAsUTC().GetAsTime(firstDay);
- const std::shared_ptr<CPVREpgInfoTag> epgTag = GetEpgInfoTag();
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag = GetEpgInfoTag();
const int iPVRTimeCorrection =
CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection;
@@ -611,7 +613,7 @@ bool CPVRTimerInfoTag::DeleteFromDatabase()
return false;
}
-bool CPVRTimerInfoTag::UpdateEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag)
+bool CPVRTimerInfoTag::UpdateEntry(const std::shared_ptr<const CPVRTimerInfoTag>& tag)
{
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -664,7 +666,7 @@ bool CPVRTimerInfoTag::UpdateEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag)
return true;
}
-bool CPVRTimerInfoTag::UpdateChildState(const std::shared_ptr<CPVRTimerInfoTag>& childTimer,
+bool CPVRTimerInfoTag::UpdateChildState(const std::shared_ptr<const CPVRTimerInfoTag>& childTimer,
bool bAdd)
{
if (!childTimer || childTimer->m_iParentClientIndex != m_iClientIndex)
@@ -829,7 +831,7 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimerInfoTag::CreateFromDate(
if (!newTimer)
{
- newTimer.reset(new CPVRTimerInfoTag);
+ newTimer = std::make_shared<CPVRTimerInfoTag>();
newTimer->m_iClientIndex = PVR_TIMER_NO_CLIENT_INDEX;
newTimer->m_iParentClientIndex = PVR_TIMER_NO_PARENT;
diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.h b/xbmc/pvr/timers/PVRTimerInfoTag.h
index ad2d3d87ef..664bdb1d42 100644
--- a/xbmc/pvr/timers/PVRTimerInfoTag.h
+++ b/xbmc/pvr/timers/PVRTimerInfoTag.h
@@ -140,7 +140,7 @@ public:
* @param tag A timer containing the data that shall be merged into this timer's data.
* @return true if the timer was updated successfully
*/
- bool UpdateEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag);
+ bool UpdateEntry(const std::shared_ptr<const CPVRTimerInfoTag>& tag);
/*!
* @brief merge in the state of this child timer.
@@ -148,7 +148,7 @@ public:
* @param bAdd If true, add child's data to parent's state, otherwise subtract.
* @return true if the child timer's state was merged successfully
*/
- bool UpdateChildState(const std::shared_ptr<CPVRTimerInfoTag>& childTimer, bool bAdd);
+ bool UpdateChildState(const std::shared_ptr<const CPVRTimerInfoTag>& childTimer, bool bAdd);
/*!
* @brief reset the state of children related to this timer.
diff --git a/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp b/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp
index 9178e85ec2..093b039dff 100644
--- a/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp
+++ b/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp
@@ -14,6 +14,8 @@
#include "pvr/timers/PVRTimerInfoTag.h"
#include "utils/RegExp.h"
+#include <memory>
+
using namespace PVR;
CPVRTimerRuleMatcher::CPVRTimerRuleMatcher(const std::shared_ptr<CPVRTimerInfoTag>& timerRule,
@@ -24,7 +26,7 @@ CPVRTimerRuleMatcher::CPVRTimerRuleMatcher(const std::shared_ptr<CPVRTimerInfoTa
CPVRTimerRuleMatcher::~CPVRTimerRuleMatcher() = default;
-std::shared_ptr<CPVRChannel> CPVRTimerRuleMatcher::GetChannel() const
+std::shared_ptr<const CPVRChannel> CPVRTimerRuleMatcher::GetChannel() const
{
if (m_timerRule->GetTimerType()->SupportsChannels())
return m_timerRule->Channel();
@@ -69,14 +71,15 @@ CDateTime CPVRTimerRuleMatcher::GetNextTimerStart() const
return nextStart.GetAsUTCDateTime();
}
-bool CPVRTimerRuleMatcher::Matches(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::Matches(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
return epgTag && CPVRTimerInfoTag::ConvertUTCToLocalTime(epgTag->EndAsUTC()) > m_start &&
MatchSeriesLink(epgTag) && MatchChannel(epgTag) && MatchStart(epgTag) &&
MatchEnd(epgTag) && MatchDayOfWeek(epgTag) && MatchSearchText(epgTag);
}
-bool CPVRTimerRuleMatcher::MatchSeriesLink(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::MatchSeriesLink(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (m_timerRule->GetTimerType()->RequiresEpgSeriesLinkOnCreate())
return epgTag->SeriesLink() == m_timerRule->SeriesLink();
@@ -84,7 +87,7 @@ bool CPVRTimerRuleMatcher::MatchSeriesLink(const std::shared_ptr<CPVREpgInfoTag>
return true;
}
-bool CPVRTimerRuleMatcher::MatchChannel(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::MatchChannel(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (m_timerRule->GetTimerType()->SupportsAnyChannel() &&
m_timerRule->ClientChannelUID() == PVR_CHANNEL_INVALID_UID)
@@ -98,7 +101,7 @@ bool CPVRTimerRuleMatcher::MatchChannel(const std::shared_ptr<CPVREpgInfoTag>& e
return true;
}
-bool CPVRTimerRuleMatcher::MatchStart(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::MatchStart(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (m_timerRule->GetTimerType()->SupportsFirstDay())
{
@@ -130,7 +133,7 @@ bool CPVRTimerRuleMatcher::MatchStart(const std::shared_ptr<CPVREpgInfoTag>& epg
return true;
}
-bool CPVRTimerRuleMatcher::MatchEnd(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::MatchEnd(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (m_timerRule->GetTimerType()->SupportsEndAnyTime() && m_timerRule->IsEndAnyTime())
return true; // matches any end time
@@ -148,7 +151,7 @@ bool CPVRTimerRuleMatcher::MatchEnd(const std::shared_ptr<CPVREpgInfoTag>& epgTa
return true;
}
-bool CPVRTimerRuleMatcher::MatchDayOfWeek(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::MatchDayOfWeek(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (m_timerRule->GetTimerType()->SupportsWeekdays())
{
@@ -165,13 +168,14 @@ bool CPVRTimerRuleMatcher::MatchDayOfWeek(const std::shared_ptr<CPVREpgInfoTag>&
return true;
}
-bool CPVRTimerRuleMatcher::MatchSearchText(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+bool CPVRTimerRuleMatcher::MatchSearchText(
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (m_timerRule->GetTimerType()->SupportsEpgFulltextMatch() && m_timerRule->IsFullTextEpgSearch())
{
if (!m_textSearch)
{
- m_textSearch.reset(new CRegExp(true /* case insensitive */));
+ m_textSearch = std::make_unique<CRegExp>(true /* case insensitive */);
m_textSearch->RegComp(m_timerRule->EpgSearchString());
}
return m_textSearch->RegFind(epgTag->Title()) >= 0 ||
@@ -183,7 +187,7 @@ bool CPVRTimerRuleMatcher::MatchSearchText(const std::shared_ptr<CPVREpgInfoTag>
{
if (!m_textSearch)
{
- m_textSearch.reset(new CRegExp(true /* case insensitive */));
+ m_textSearch = std::make_unique<CRegExp>(true /* case insensitive */);
m_textSearch->RegComp(m_timerRule->EpgSearchString());
}
return m_textSearch->RegFind(epgTag->Title()) >= 0;
diff --git a/xbmc/pvr/timers/PVRTimerRuleMatcher.h b/xbmc/pvr/timers/PVRTimerRuleMatcher.h
index 9d502afa77..09e7109b68 100644
--- a/xbmc/pvr/timers/PVRTimerRuleMatcher.h
+++ b/xbmc/pvr/timers/PVRTimerRuleMatcher.h
@@ -28,17 +28,17 @@ public:
std::shared_ptr<CPVRTimerInfoTag> GetTimerRule() const { return m_timerRule; }
- std::shared_ptr<CPVRChannel> GetChannel() const;
+ std::shared_ptr<const CPVRChannel> GetChannel() const;
CDateTime GetNextTimerStart() const;
- bool Matches(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ bool Matches(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
private:
- bool MatchSeriesLink(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
- bool MatchChannel(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
- bool MatchStart(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
- bool MatchEnd(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
- bool MatchDayOfWeek(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
- bool MatchSearchText(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ bool MatchSeriesLink(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
+ bool MatchChannel(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
+ bool MatchStart(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
+ bool MatchEnd(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
+ bool MatchDayOfWeek(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
+ bool MatchSearchText(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
const std::shared_ptr<CPVRTimerInfoTag> m_timerRule;
CDateTime m_start;
diff --git a/xbmc/pvr/timers/PVRTimerType.cpp b/xbmc/pvr/timers/PVRTimerType.cpp
index fd4cc0eba6..b678071ff9 100644
--- a/xbmc/pvr/timers/PVRTimerType.cpp
+++ b/xbmc/pvr/timers/PVRTimerType.cpp
@@ -103,7 +103,8 @@ const std::vector<std::shared_ptr<CPVRTimerType>> CPVRTimerType::GetAllTypes()
return allTypes;
}
-const std::shared_ptr<CPVRTimerType> CPVRTimerType::GetFirstAvailableType(const std::shared_ptr<CPVRClient>& client)
+const std::shared_ptr<CPVRTimerType> CPVRTimerType::GetFirstAvailableType(
+ const std::shared_ptr<const CPVRClient>& client)
{
if (client)
{
diff --git a/xbmc/pvr/timers/PVRTimerType.h b/xbmc/pvr/timers/PVRTimerType.h
index 34405a7e1b..a17cf5f402 100644
--- a/xbmc/pvr/timers/PVRTimerType.h
+++ b/xbmc/pvr/timers/PVRTimerType.h
@@ -39,7 +39,8 @@ namespace PVR
* @param client the PVR client.
* @return A timer type or NULL if none available.
*/
- static const std::shared_ptr<CPVRTimerType> GetFirstAvailableType(const std::shared_ptr<CPVRClient>& client);
+ static const std::shared_ptr<CPVRTimerType> GetFirstAvailableType(
+ const std::shared_ptr<const CPVRClient>& client);
/*!
* @brief Create a timer type from given timer type id and client id.
diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp
index 9537a52016..5b899d21b6 100644
--- a/xbmc/pvr/timers/PVRTimers.cpp
+++ b/xbmc/pvr/timers/PVRTimers.cpp
@@ -109,7 +109,8 @@ bool CPVRTimers::Update(const std::vector<std::shared_ptr<CPVRClient>>& clients)
bool CPVRTimers::LoadFromDatabase(const std::vector<std::shared_ptr<CPVRClient>>& clients)
{
// load local timers from database
- const std::shared_ptr<CPVRDatabase> database = CServiceBroker::GetPVRManager().GetTVDatabase();
+ const std::shared_ptr<const CPVRDatabase> database =
+ CServiceBroker::GetPVRManager().GetTVDatabase();
if (database)
{
const std::vector<std::shared_ptr<CPVRTimerInfoTag>> timers =
@@ -192,7 +193,7 @@ bool CPVRTimers::IsRecording() const
return false;
}
-void CPVRTimers::RemoveEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag)
+void CPVRTimers::RemoveEntry(const std::shared_ptr<const CPVRTimerInfoTag>& tag)
{
std::unique_lock<CCriticalSection> lock(m_critSection);
@@ -200,7 +201,7 @@ void CPVRTimers::RemoveEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag)
if (it != m_tags.end())
{
it->second.erase(std::remove_if(it->second.begin(), it->second.end(),
- [&tag](const std::shared_ptr<CPVRTimerInfoTag>& timer) {
+ [&tag](const std::shared_ptr<const CPVRTimerInfoTag>& timer) {
return tag->ClientID() == timer->ClientID() &&
tag->ClientIndex() == timer->ClientIndex();
}),
@@ -213,7 +214,7 @@ void CPVRTimers::RemoveEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag)
bool CPVRTimers::CheckAndAppendTimerNotification(
std::vector<std::pair<int, std::string>>& timerNotifications,
- const std::shared_ptr<CPVRTimerInfoTag>& tag,
+ const std::shared_ptr<const CPVRTimerInfoTag>& tag,
bool bDeleted) const
{
// no notification on first update or if previous update failed for tag's client.
@@ -222,7 +223,7 @@ bool CPVRTimers::CheckAndAppendTimerNotification(
{
const std::string strMessage =
bDeleted ? tag->GetDeletedNotificationText() : tag->GetNotificationText();
- timerNotifications.emplace_back(std::make_pair(tag->ClientID(), strMessage));
+ timerNotifications.emplace_back(tag->ClientID(), strMessage);
return true;
}
return false;
@@ -264,8 +265,7 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer& timers,
else
{
/* new timer */
- std::shared_ptr<CPVRTimerInfoTag> newTimer =
- std::shared_ptr<CPVRTimerInfoTag>(new CPVRTimerInfoTag);
+ std::shared_ptr<CPVRTimerInfoTag> newTimer = std::make_shared<CPVRTimerInfoTag>();
newTimer->UpdateEntry(timersEntry);
newTimer->SetTimerID(++m_iLastId);
InsertEntry(newTimer);
@@ -385,7 +385,7 @@ bool CPVRTimers::UpdateEntries(const CPVRTimersContainer& timers,
/* queue notifications / fill eventlog */
for (const auto& entry : timerNotifications)
{
- const std::shared_ptr<CPVRClient> client =
+ const std::shared_ptr<const CPVRClient> client =
CServiceBroker::GetPVRManager().GetClient(entry.first);
if (client)
{
@@ -409,11 +409,11 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> GetEpgTagsForTimerRule(
{
std::vector<std::shared_ptr<CPVREpgInfoTag>> matches;
- const std::shared_ptr<CPVRChannel> channel = matcher.GetChannel();
+ const std::shared_ptr<const CPVRChannel> channel = matcher.GetChannel();
if (channel)
{
// match single channel
- const std::shared_ptr<CPVREpg> epg = channel->GetEPG();
+ const std::shared_ptr<const CPVREpg> epg = channel->GetEPG();
if (epg)
{
const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags = epg->GetTags();
@@ -444,7 +444,7 @@ void AddTimerRuleToEpgMap(
std::map<std::shared_ptr<CPVREpg>, std::vector<std::shared_ptr<CPVRTimerRuleMatcher>>>& epgMap,
bool& bFetchedAllEpgs)
{
- const std::shared_ptr<CPVRChannel> channel = timer->Channel();
+ const std::shared_ptr<const CPVRChannel> channel = timer->Channel();
if (channel)
{
const std::shared_ptr<CPVREpg> epg = channel->GetEPG();
@@ -514,7 +514,7 @@ bool CPVRTimers::UpdateEntries(int iMaxNotificationDelay)
if (timer->IsEpgBased())
{
// update epg tag
- const std::shared_ptr<CPVREpg> epg =
+ const std::shared_ptr<const CPVREpg> epg =
CServiceBroker::GetPVRManager().EpgContainer().GetByChannelUid(
timer->Channel()->ClientID(), timer->Channel()->UniqueID());
if (epg)
@@ -602,10 +602,11 @@ bool CPVRTimers::UpdateEntries(int iMaxNotificationDelay)
if (it1 == m_tags.end())
bCreate = true;
else
- bCreate = std::none_of(it1->second.cbegin(), it1->second.cend(),
- [&timer](const std::shared_ptr<CPVRTimerInfoTag>& tmr) {
- return tmr->ParentClientIndex() == timer->ClientIndex();
- });
+ bCreate =
+ std::none_of(it1->second.cbegin(), it1->second.cend(),
+ [&timer](const std::shared_ptr<const CPVRTimerInfoTag>& tmr) {
+ return tmr->ParentClientIndex() == timer->ClientIndex();
+ });
if (bCreate)
{
const CDateTimeSpan duration = timer->EndAsUTC() - timer->StartAsUTC();
@@ -615,8 +616,8 @@ bool CPVRTimers::UpdateEntries(int iMaxNotificationDelay)
if (childTimer)
{
bChanged = true;
- childTimersToInsert.emplace_back(
- std::make_pair(timer, childTimer)); // remember and insert/save later
+ childTimersToInsert.emplace_back(timer,
+ childTimer); // remember and insert/save later
}
}
}
@@ -663,8 +664,8 @@ bool CPVRTimers::UpdateEntries(int iMaxNotificationDelay)
if (childTimer)
{
bChanged = true;
- childTimersToInsert.emplace_back(std::make_pair(
- matcher->GetTimerRule(), childTimer)); // remember and insert/save later
+ childTimersToInsert.emplace_back(matcher->GetTimerRule(),
+ childTimer); // remember and insert/save later
}
}
}
@@ -708,7 +709,7 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetNextReminderToAnnnounce()
}
bool CPVRTimers::KindMatchesTag(const TimerKind& eKind,
- const std::shared_ptr<CPVRTimerInfoTag>& tag) const
+ const std::shared_ptr<const CPVRTimerInfoTag>& tag) const
{
return (eKind == TimerKindAny) || (eKind == TimerKindTV && !tag->IsRadio()) ||
(eKind == TimerKindRadio && tag->IsRadio());
@@ -905,7 +906,7 @@ bool CPVRTimers::DeleteTimersOnChannel(const std::shared_ptr<CPVRChannel>& chann
}
std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::UpdateEntry(
- const std::shared_ptr<CPVRTimerInfoTag>& timer)
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer)
{
bool bChanged = false;
@@ -928,7 +929,7 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::UpdateEntry(
}
else
{
- tag.reset(new CPVRTimerInfoTag());
+ tag = std::make_shared<CPVRTimerInfoTag>();
if (tag->UpdateEntry(timer))
{
tag->SetTimerID(++m_iLastId);
@@ -1148,7 +1149,7 @@ bool CPVRTimers::IsRecordingOnChannel(const CPVRChannel& channel) const
}
std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetActiveTimerForChannel(
- const std::shared_ptr<CPVRChannel>& channel) const
+ const std::shared_ptr<const CPVRChannel>& channel) const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
for (const auto& tagsEntry : m_tags)
@@ -1167,7 +1168,7 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetActiveTimerForChannel(
}
std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetTimerForEpgTag(
- const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
{
if (epgTag)
{
@@ -1204,7 +1205,7 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetTimerForEpgTag(
}
std::shared_ptr<CPVRTimerInfoTag> CPVRTimers::GetTimerRule(
- const std::shared_ptr<CPVRTimerInfoTag>& timer) const
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer) const
{
if (timer)
{
@@ -1262,7 +1263,7 @@ CDateTime CPVRTimers::GetNextEventTime() const
CDateTime wakeuptime;
/* Check next active time */
- const std::shared_ptr<CPVRTimerInfoTag> timer = GetNextActiveTimer(false);
+ const std::shared_ptr<const CPVRTimerInfoTag> timer = GetNextActiveTimer(false);
if (timer)
{
const CDateTimeSpan prestart(0, 0, timer->MarginStart(), 0);
diff --git a/xbmc/pvr/timers/PVRTimers.h b/xbmc/pvr/timers/PVRTimers.h
index 6b0e4e7f54..c811994685 100644
--- a/xbmc/pvr/timers/PVRTimers.h
+++ b/xbmc/pvr/timers/PVRTimers.h
@@ -181,7 +181,7 @@ public:
* @return the timer, null otherwise.
*/
std::shared_ptr<CPVRTimerInfoTag> GetActiveTimerForChannel(
- const std::shared_ptr<CPVRChannel>& channel) const;
+ const std::shared_ptr<const CPVRChannel>& channel) const;
/*!
* @return The amount of tv and radio timers that are currently recording
@@ -250,7 +250,7 @@ public:
* @return The requested timer tag, or nullptr if none was found.
*/
std::shared_ptr<CPVRTimerInfoTag> GetTimerForEpgTag(
- const std::shared_ptr<CPVREpgInfoTag>& epgTag) const;
+ const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const;
/*!
* @brief Get the timer rule for a given timer tag
@@ -258,7 +258,7 @@ public:
* @return The timer rule, or nullptr if none was found.
*/
std::shared_ptr<CPVRTimerInfoTag> GetTimerRule(
- const std::shared_ptr<CPVRTimerInfoTag>& timer) const;
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer) const;
/*!
* @brief Update the channel pointers.
@@ -288,10 +288,11 @@ private:
*/
bool LoadFromDatabase(const std::vector<std::shared_ptr<CPVRClient>>& clients);
- void RemoveEntry(const std::shared_ptr<CPVRTimerInfoTag>& tag);
+ void RemoveEntry(const std::shared_ptr<const CPVRTimerInfoTag>& tag);
bool UpdateEntries(const CPVRTimersContainer& timers, const std::vector<int>& failedClients);
bool UpdateEntries(int iMaxNotificationDelay);
- std::shared_ptr<CPVRTimerInfoTag> UpdateEntry(const std::shared_ptr<CPVRTimerInfoTag>& timer);
+ std::shared_ptr<CPVRTimerInfoTag> UpdateEntry(
+ const std::shared_ptr<const CPVRTimerInfoTag>& timer);
bool AddLocalTimer(const std::shared_ptr<CPVRTimerInfoTag>& tag, bool bNotify);
bool DeleteLocalTimer(const std::shared_ptr<CPVRTimerInfoTag>& tag, bool bNotify);
@@ -308,7 +309,8 @@ private:
TimerKindRadio
};
- bool KindMatchesTag(const TimerKind& eKind, const std::shared_ptr<CPVRTimerInfoTag>& tag) const;
+ bool KindMatchesTag(const TimerKind& eKind,
+ const std::shared_ptr<const CPVRTimerInfoTag>& tag) const;
std::shared_ptr<CPVRTimerInfoTag> GetNextActiveTimer(const TimerKind& eKind,
bool bIgnoreReminders) const;
@@ -317,7 +319,7 @@ private:
int AmountActiveRecordings(const TimerKind& eKind) const;
bool CheckAndAppendTimerNotification(std::vector<std::pair<int, std::string>>& timerNotifications,
- const std::shared_ptr<CPVRTimerInfoTag>& tag,
+ const std::shared_ptr<const CPVRTimerInfoTag>& tag,
bool bDeleted) const;
bool m_bIsUpdating = false;
diff --git a/xbmc/pvr/windows/GUIWindowPVRBase.cpp b/xbmc/pvr/windows/GUIWindowPVRBase.cpp
index aa11d866f6..71a800feda 100644
--- a/xbmc/pvr/windows/GUIWindowPVRBase.cpp
+++ b/xbmc/pvr/windows/GUIWindowPVRBase.cpp
@@ -214,7 +214,7 @@ bool CGUIWindowPVRBase::OnAction(const CAction& action)
bool CGUIWindowPVRBase::ActivatePreviousChannelGroup()
{
- const std::shared_ptr<CPVRChannelGroup> channelGroup = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> channelGroup = GetChannelGroup();
if (channelGroup)
{
const CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(channelGroup->IsRadio());
@@ -229,7 +229,7 @@ bool CGUIWindowPVRBase::ActivatePreviousChannelGroup()
bool CGUIWindowPVRBase::ActivateNextChannelGroup()
{
- const std::shared_ptr<CPVRChannelGroup> channelGroup = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> channelGroup = GetChannelGroup();
if (channelGroup)
{
const CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(channelGroup->IsRadio());
@@ -246,7 +246,7 @@ void CGUIWindowPVRBase::ClearData()
{
std::unique_lock<CCriticalSection> lock(m_critSection);
m_channelGroup.reset();
- m_channelGroupsSelector.reset(new CGUIPVRChannelGroupsSelector);
+ m_channelGroupsSelector = std::make_unique<CGUIPVRChannelGroupsSelector>();
}
void CGUIWindowPVRBase::OnInitWindow()
@@ -352,6 +352,16 @@ bool CGUIWindowPVRBase::OnMessage(CGUIMessage& message)
bReturn = true;
break;
}
+ case GUI_MSG_UPDATE:
+ {
+ if (IsActive() && m_bUpdating)
+ {
+ // no concurrent updates
+ CLog::LogF(LOGWARNING, "GUI_MSG_UPDATE: Updating in progress");
+ bReturn = true;
+ }
+ break;
+ }
}
break;
}
@@ -405,7 +415,7 @@ bool CGUIWindowPVRBase::OpenChannelGroupSelectionDialog()
std::string selectedName;
std::string selectedClient;
- const std::shared_ptr<CPVRChannelGroup> channelGroup = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> channelGroup = GetChannelGroup();
if (channelGroup)
{
selectedName = channelGroup->GroupName();
@@ -420,7 +430,7 @@ bool CGUIWindowPVRBase::OpenChannelGroupSelectionDialog()
for (auto& group : options)
{
// set client name as label2
- const std::shared_ptr<CPVRClient> client = pvrMgr.GetClient(*group);
+ const std::shared_ptr<const CPVRClient> client = pvrMgr.GetClient(*group);
if (client)
group->SetLabel2(client->GetFriendlyName());
@@ -444,7 +454,7 @@ bool CGUIWindowPVRBase::OpenChannelGroupSelectionDialog()
}
else
{
- const std::shared_ptr<CPVRChannelGroup> channelGroup = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> channelGroup = GetChannelGroup();
if (channelGroup)
{
int idx = -1;
@@ -553,6 +563,7 @@ bool CGUIWindowPVRBase::Update(const std::string& strDirectory, bool updateFilte
if (m_bUpdating)
{
// no concurrent updates
+ CLog::LogF(LOGWARNING, "Updating in progress");
return false;
}
diff --git a/xbmc/pvr/windows/GUIWindowPVRChannels.cpp b/xbmc/pvr/windows/GUIWindowPVRChannels.cpp
index 6e97a504b7..a002386cab 100644
--- a/xbmc/pvr/windows/GUIWindowPVRChannels.cpp
+++ b/xbmc/pvr/windows/GUIWindowPVRChannels.cpp
@@ -320,7 +320,7 @@ bool CGUIWindowPVRChannelsBase::OnContextButtonManage(const CFileItemPtr& item,
void CGUIWindowPVRChannelsBase::UpdateEpg(const CFileItemPtr& item)
{
- const std::shared_ptr<CPVRChannel> channel(item->GetPVRChannelInfoTag());
+ const std::shared_ptr<const CPVRChannel> channel(item->GetPVRChannelInfoTag());
if (!CGUIDialogYesNo::ShowAndGetInput(
CVariant{19251}, // "Update guide information"
@@ -400,7 +400,7 @@ void CGUIWindowPVRChannelsBase::OnInputDone()
void CGUIWindowPVRChannelsBase::GetChannelNumbers(std::vector<std::string>& channelNumbers)
{
- const std::shared_ptr<CPVRChannelGroup> group = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> group = GetChannelGroup();
if (group)
group->GetChannelNumbers(channelNumbers);
}
diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
index f4c247f910..3923cae6bb 100644
--- a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
+++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
@@ -144,7 +144,7 @@ void CGUIWindowPVRGuideBase::OnDeinitWindow(int nextWindowID)
void CGUIWindowPVRGuideBase::StartRefreshTimelineItemsThread()
{
StopRefreshTimelineItemsThread();
- m_refreshTimelineItemsThread.reset(new CPVRRefreshTimelineItemsThread(this));
+ m_refreshTimelineItemsThread = std::make_unique<CPVRRefreshTimelineItemsThread>(this);
m_refreshTimelineItemsThread->Create();
}
@@ -197,7 +197,7 @@ void CGUIWindowPVRGuideBase::UpdateSelectedItemPath()
CGUIEPGGridContainer* epgGridContainer = GetGridControl();
if (epgGridContainer)
{
- const std::shared_ptr<CPVRChannelGroupMember> groupMember =
+ const std::shared_ptr<const CPVRChannelGroupMember> groupMember =
epgGridContainer->GetSelectedChannelGroupMember();
if (groupMember)
CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().SetSelectedChannelPath(
@@ -211,7 +211,7 @@ void CGUIWindowPVRGuideBase::UpdateButtons()
SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1, g_localizeStrings.Get(19032));
- const std::shared_ptr<CPVRChannelGroup> group = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> group = GetChannelGroup();
SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, group ? group->GroupName() : "");
}
@@ -280,7 +280,7 @@ CFileItemPtr CGUIWindowPVRGuideBase::GetCurrentListItem(int offset /*= 0*/)
return {};
}
-int CGUIWindowPVRGuideBase::GetCurrentListItemIndex(const std::shared_ptr<CFileItem>& item)
+int CGUIWindowPVRGuideBase::GetCurrentListItemIndex(const std::shared_ptr<const CFileItem>& item)
{
return item ? item->GetProperty("TimelineIndex").asInteger() : -1;
}
@@ -463,7 +463,7 @@ bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message)
break;
case EPG_SELECT_ACTION_SMART_SELECT:
{
- const std::shared_ptr<CPVREpgInfoTag> tag(pItem->GetEPGInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> tag(pItem->GetEPGInfoTag());
if (tag)
{
const CDateTime start(tag->StartAsUTC());
@@ -484,7 +484,8 @@ bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message)
else
{
bool bCanRecord = true;
- const std::shared_ptr<CPVRChannel> channel = CPVRItem(pItem).GetChannel();
+ const std::shared_ptr<const CPVRChannel> channel =
+ CPVRItem(pItem).GetChannel();
if (channel)
bCanRecord = channel->CanRecord();
@@ -782,7 +783,8 @@ bool CGUIWindowPVRGuideBase::GotoCurrentProgramme()
if (!channel)
{
- const std::shared_ptr<CPVREpgInfoTag> playingTag = mgr.PlaybackState()->GetPlayingEpgTag();
+ const std::shared_ptr<const CPVREpgInfoTag> playingTag =
+ mgr.PlaybackState()->GetPlayingEpgTag();
if (playingTag)
channel = mgr.ChannelGroups()->GetChannelForEpgTag(playingTag);
}
@@ -850,7 +852,8 @@ bool CGUIWindowPVRGuideBase::GotoPlayingChannel()
if (!channel)
{
- const std::shared_ptr<CPVREpgInfoTag> playingTag = mgr.PlaybackState()->GetPlayingEpgTag();
+ const std::shared_ptr<const CPVREpgInfoTag> playingTag =
+ mgr.PlaybackState()->GetPlayingEpgTag();
if (playingTag)
channel = mgr.ChannelGroups()->GetChannelForEpgTag(playingTag);
}
@@ -874,7 +877,7 @@ void CGUIWindowPVRGuideBase::OnInputDone()
void CGUIWindowPVRGuideBase::GetChannelNumbers(std::vector<std::string>& channelNumbers)
{
- const std::shared_ptr<CPVRChannelGroup> group = GetChannelGroup();
+ const std::shared_ptr<const CPVRChannelGroup> group = GetChannelGroup();
if (group)
group->GetChannelNumbers(channelNumbers);
}
diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.h b/xbmc/pvr/windows/GUIWindowPVRGuide.h
index 87477ca0da..5ae5f54ab1 100644
--- a/xbmc/pvr/windows/GUIWindowPVRGuide.h
+++ b/xbmc/pvr/windows/GUIWindowPVRGuide.h
@@ -85,7 +85,7 @@ private:
void RefreshView(CGUIMessage& message, bool bInitGridControl);
- int GetCurrentListItemIndex(const std::shared_ptr<CFileItem>& item);
+ int GetCurrentListItemIndex(const std::shared_ptr<const CFileItem>& item);
std::unique_ptr<CPVRRefreshTimelineItemsThread> m_refreshTimelineItemsThread;
std::atomic_bool m_bRefreshTimelineItems{false};
diff --git a/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
index c5f3ba1bb3..3f545098de 100644
--- a/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
+++ b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
@@ -29,6 +29,7 @@
#include "utils/URIUtils.h"
#include "video/VideoLibraryQueue.h"
#include "video/VideoUtils.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
#include "video/guilib/VideoSelectActionProcessor.h"
#include "video/windows/GUIWindowVideoBase.h"
@@ -226,7 +227,9 @@ namespace
class CVideoSelectActionProcessor : public VIDEO::GUILIB::CVideoSelectActionProcessorBase
{
public:
- CVideoSelectActionProcessor(CGUIWindowPVRRecordingsBase& window, CFileItem& item, int itemIndex)
+ CVideoSelectActionProcessor(CGUIWindowPVRRecordingsBase& window,
+ const std::shared_ptr<CFileItem>& item,
+ int itemIndex)
: CVideoSelectActionProcessorBase(item), m_window(window), m_itemIndex(itemIndex)
{
}
@@ -241,27 +244,26 @@ protected:
bool OnResumeSelected() override
{
CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().ResumePlayRecording(
- m_item, true /* fall back to play if no resume possible */);
+ *m_item, true /* fall back to play if no resume possible */);
return true;
}
bool OnPlaySelected() override
{
CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(
- m_item, false /* no resume check */);
+ *m_item, false /* no resume check */);
return true;
}
bool OnQueueSelected() override
{
- VIDEO_UTILS::QueueItem(std::make_shared<CFileItem>(m_item),
- VIDEO_UTILS::QueuePosition::POSITION_END);
+ VIDEO_UTILS::QueueItem(m_item, VIDEO_UTILS::QueuePosition::POSITION_END);
return true;
}
bool OnInfoSelected() override
{
- CServiceBroker::GetPVRManager().Get<PVR::GUI::Recordings>().ShowRecordingInfo(m_item);
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Recordings>().ShowRecordingInfo(*m_item);
return true;
}
@@ -275,6 +277,48 @@ private:
CGUIWindowPVRRecordingsBase& m_window;
const int m_itemIndex{-1};
};
+
+class CVideoPlayActionProcessor : public CVideoPlayActionProcessorBase
+{
+public:
+ explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item)
+ : CVideoPlayActionProcessorBase(item)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ if (m_item->m_bIsFolder)
+ {
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecordingFolder(
+ *m_item, false /* no resume check */);
+ }
+ else
+ {
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().ResumePlayRecording(
+ *m_item, true /* fall back to play if no resume possible */);
+ }
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ if (m_item->m_bIsFolder)
+ {
+ m_item->SetStartOffset(0);
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecordingFolder(
+ *m_item, false /* no resume check */);
+ }
+ else
+ {
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(
+ *m_item, false /* no resume check */);
+ }
+ return true;
+ }
+};
} // namespace
bool CGUIWindowPVRRecordingsBase::OnMessage(CGUIMessage& message)
@@ -306,17 +350,8 @@ bool CGUIWindowPVRRecordingsBase::OnMessage(CGUIMessage& message)
if (!item->IsParentFolder() && message.GetParam1() == ACTION_PLAYER_PLAY)
{
- if (item->m_bIsFolder)
- {
- CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecordingFolder(
- *item, true /* check resume */);
- }
- else
- {
- CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(
- *item, true /* check resume */);
- }
- bReturn = true;
+ CVideoPlayActionProcessor proc{item};
+ bReturn = proc.ProcessDefaultAction();
}
else if (item->m_bIsFolder)
{
@@ -325,8 +360,8 @@ bool CGUIWindowPVRRecordingsBase::OnMessage(CGUIMessage& message)
}
else
{
- CVideoSelectActionProcessor proc(*this, *item, iItem);
- bReturn = proc.Process();
+ CVideoSelectActionProcessor proc(*this, item, iItem);
+ bReturn = proc.ProcessDefaultAction();
}
break;
}
diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.cpp b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp
index 1cf62a6302..0c129c3e00 100644
--- a/xbmc/pvr/windows/GUIWindowPVRSearch.cpp
+++ b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp
@@ -144,7 +144,7 @@ void CGUIWindowPVRSearchBase::SetItemToSearch(const CFileItem& item)
{
SetSearchFilter(std::make_shared<CPVREpgSearchFilter>(m_bRadio));
- const std::shared_ptr<CPVREpgInfoTag> epgTag(CPVRItem(item).GetEpgInfoTag());
+ const std::shared_ptr<const CPVREpgInfoTag> epgTag(CPVRItem(item).GetEpgInfoTag());
if (epgTag && !CServiceBroker::GetPVRManager().IsParentalLocked(epgTag))
m_searchfilter->SetSearchPhrase(epgTag->Title());
}
diff --git a/xbmc/rendering/RenderSystem.cpp b/xbmc/rendering/RenderSystem.cpp
index 3f10562e7b..cabbea77b1 100644
--- a/xbmc/rendering/RenderSystem.cpp
+++ b/xbmc/rendering/RenderSystem.cpp
@@ -15,6 +15,8 @@
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
+#include <memory>
+
CRenderSystemBase::CRenderSystemBase()
{
m_bRenderCreated = false;
@@ -62,8 +64,11 @@ void CRenderSystemBase::ShowSplash(const std::string& message)
if (!m_splashImage)
{
- m_splashImage = std::unique_ptr<CGUIImage>(new CGUIImage(0, 0, 0, 0, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(),
- CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(), CTextureInfo(CUtil::GetSplashPath())));
+ m_splashImage = std::make_unique<CGUIImage>(
+ 0, 0, .0f, .0f,
+ static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
+ static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()),
+ CTextureInfo(CUtil::GetSplashPath()));
m_splashImage->SetAspectRatio(CAspectRatio::AR_SCALE);
}
@@ -86,7 +91,7 @@ void CRenderSystemBase::ShowSplash(const std::string& message)
{
auto messageFont = g_fontManager.LoadTTF("__splash__", "arial.ttf", 0xFFFFFFFF, 0, 20, FONT_STYLE_NORMAL, false, 1.0f, 1.0f, &res);
if (messageFont)
- m_splashMessageLayout = std::unique_ptr<CGUITextLayout>(new CGUITextLayout(messageFont, true, 0));
+ m_splashMessageLayout = std::make_unique<CGUITextLayout>(messageFont, true, .0f);
}
if (m_splashMessageLayout)
diff --git a/xbmc/rendering/dx/DeviceResources.cpp b/xbmc/rendering/dx/DeviceResources.cpp
index 103809d591..ff1cea0cca 100644
--- a/xbmc/rendering/dx/DeviceResources.cpp
+++ b/xbmc/rendering/dx/DeviceResources.cpp
@@ -131,6 +131,28 @@ void DX::DeviceResources::GetOutput(IDXGIOutput** ppOutput) const
*ppOutput = pOutput.Detach();
}
+void DX::DeviceResources::GetCachedOutputAndDesc(IDXGIOutput** ppOutput,
+ DXGI_OUTPUT_DESC* outputDesc) const
+{
+ ComPtr<IDXGIOutput> pOutput;
+ if (m_output)
+ {
+ m_output.As(&pOutput);
+ *outputDesc = m_outputDesc;
+ }
+ else if (m_swapChain && SUCCEEDED(m_swapChain->GetContainingOutput(pOutput.GetAddressOf())) &&
+ pOutput)
+ {
+ pOutput->GetDesc(outputDesc);
+ }
+
+ if (!pOutput)
+ CLog::LogF(LOGWARNING, "unable to retrieve current output");
+
+ *ppOutput = pOutput.Detach();
+ return;
+}
+
DXGI_ADAPTER_DESC DX::DeviceResources::GetAdapterDesc() const
{
DXGI_ADAPTER_DESC desc{};
@@ -1045,6 +1067,7 @@ void DX::DeviceResources::HandleOutputChange(const std::function<bool(DXGI_OUTPU
if (cmpFunc(outputDesc))
{
output.As(&m_output);
+ m_outputDesc = outputDesc;
// check if adapter is changed
if (currentDesc.AdapterLuid.HighPart != foundDesc.AdapterLuid.HighPart
|| currentDesc.AdapterLuid.LowPart != foundDesc.AdapterLuid.LowPart)
@@ -1331,6 +1354,11 @@ HDR_STATUS DX::DeviceResources::ToggleHDR()
DXGI_MODE_DESC md = {};
GetDisplayMode(&md);
+ // Xbox uses only full screen windowed mode and not needs recreate swapchain.
+ // Recreate swapchain causes native 4K resolution is lost and quality obtained
+ // is equivalent to 1080p upscaled to 4K (TO DO: investigate root cause).
+ const bool isXbox = (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox);
+
DX::Windowing()->SetTogglingHDR(true);
DX::Windowing()->SetAlteringWindow(true);
@@ -1338,7 +1366,7 @@ HDR_STATUS DX::DeviceResources::ToggleHDR()
HDR_STATUS hdrStatus = CWIN32Util::ToggleWindowsHDR(md);
// Kill swapchain
- if (m_swapChain && hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
+ if (!isXbox && m_swapChain && hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
{
CLog::LogF(LOGDEBUG, "Re-create swapchain due HDR <-> SDR switch");
DestroySwapChain();
@@ -1347,13 +1375,28 @@ HDR_STATUS DX::DeviceResources::ToggleHDR()
DX::Windowing()->SetAlteringWindow(false);
// Re-create swapchain
- if (hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
+ if (!isXbox && hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
{
CreateWindowSizeDependentResources();
DX::Windowing()->NotifyAppFocusChange(true);
}
+ // On Xbox set new color space in same swapchain
+ if (isXbox && hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
+ {
+ if (hdrStatus == HDR_STATUS::HDR_ON)
+ {
+ SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
+ m_IsHDROutput = true;
+ }
+ else
+ {
+ SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
+ m_IsHDROutput = false;
+ }
+ }
+
return hdrStatus;
}
diff --git a/xbmc/rendering/dx/DeviceResources.h b/xbmc/rendering/dx/DeviceResources.h
index 8d325ee6dc..0d9937c864 100644
--- a/xbmc/rendering/dx/DeviceResources.h
+++ b/xbmc/rendering/dx/DeviceResources.h
@@ -68,6 +68,13 @@ namespace DX
CD3DTexture& GetBackBuffer() { return m_backBufferTex; }
void GetOutput(IDXGIOutput** ppOutput) const;
+ /*!
+ * \brief Retrieve current output and output description. Use cached data first to avoid delays due
+ * to dxgi internal multithreading synchronization.
+ * \param output The output
+ * \param outputDesc The output description
+ */
+ void GetCachedOutputAndDesc(IDXGIOutput** output, DXGI_OUTPUT_DESC* outputDesc) const;
DXGI_ADAPTER_DESC GetAdapterDesc() const;
void GetDisplayMode(DXGI_MODE_DESC *mode) const;
@@ -152,6 +159,7 @@ namespace DX
Microsoft::WRL::ComPtr<IDXGIFactory2> m_dxgiFactory;
Microsoft::WRL::ComPtr<IDXGIAdapter1> m_adapter;
Microsoft::WRL::ComPtr<IDXGIOutput1> m_output;
+ DXGI_OUTPUT_DESC m_outputDesc{};
Microsoft::WRL::ComPtr<ID3D11Device1> m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_d3dContext;
diff --git a/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp b/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp
index 022611bece..257bad601b 100644
--- a/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp
+++ b/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp
@@ -14,6 +14,7 @@
#include "utils/Screenshot.h"
#include "windowing/GraphicContext.h"
+#include <memory>
#include <mutex>
#include <vector>
@@ -26,7 +27,7 @@ void CScreenshotSurfaceGL::Register()
std::unique_ptr<IScreenshotSurface> CScreenshotSurfaceGL::CreateSurface()
{
- return std::unique_ptr<CScreenshotSurfaceGL>(new CScreenshotSurfaceGL());
+ return std::make_unique<CScreenshotSurfaceGL>();
}
bool CScreenshotSurfaceGL::Capture()
diff --git a/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp b/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp
index 3c290ec25c..d25da6cd65 100644
--- a/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp
+++ b/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp
@@ -14,6 +14,7 @@
#include "utils/Screenshot.h"
#include "windowing/GraphicContext.h"
+#include <memory>
#include <mutex>
#include <vector>
@@ -26,7 +27,7 @@ void CScreenshotSurfaceGLES::Register()
std::unique_ptr<IScreenshotSurface> CScreenshotSurfaceGLES::CreateSurface()
{
- return std::unique_ptr<CScreenshotSurfaceGLES>(new CScreenshotSurfaceGLES());
+ return std::make_unique<CScreenshotSurfaceGLES>();
}
bool CScreenshotSurfaceGLES::Capture()
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index dc75c31586..85cba8597a 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -195,6 +195,7 @@ void CAdvancedSettings::Initialize()
m_fullScreenOnMovieStart = true;
m_cachePath = "special://temp/";
+ m_videoFilenameIdentifierRegExp = R"([\{\[](\w+?)(?:id)?[-=](\w+)[\}|\]])";
m_videoCleanDateTimeRegExp = "(.*[^ _\\,\\.\\(\\)\\[\\]\\-])[ _\\.\\(\\)\\[\\]\\-]+(19[0-9][0-9]|20[0-9][0-9])([ _\\,\\.\\(\\)\\[\\]\\-]|[^0-9]$)?";
m_videoCleanStringRegExps.clear();
@@ -254,25 +255,29 @@ void CAdvancedSettings::Initialize()
m_tvshowEnumRegExps.clear();
// foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02, S01xE02
- m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"s([0-9]+)[ ._x-]*e([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ m_tvshowEnumRegExps.emplace_back(
+ false, "s([0-9]+)[ ._x-]*e([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$");
// foo.ep01, foo.EP_01, foo.E01
- m_tvshowEnumRegExps.push_back(TVShowRegexp(
- false, "[\\._ -]?()e(?:p[ ._-]?)?([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ m_tvshowEnumRegExps.emplace_back(
+ false, "[\\._ -]?()e(?:p[ ._-]?)?([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$");
// foo.yyyy.mm.dd.* (byDate=true)
- m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})"));
+ m_tvshowEnumRegExps.emplace_back(true, "([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})");
// foo.mm.dd.yyyy.* (byDate=true)
- m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})"));
+ m_tvshowEnumRegExps.emplace_back(true, "([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})");
// foo.1x09* or just /1x09*
- m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ m_tvshowEnumRegExps.emplace_back(
+ false, "[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$");
// Part I, Pt.VI, Part 1
- m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$"));
+ m_tvshowEnumRegExps.emplace_back(false,
+ "[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$");
// This regexp is for matching special episodes by their title, e.g. foo.special.mp4
- m_tvshowEnumRegExps.push_back(
- TVShowRegexp(false, "[\\\\/]([^\\\\/]+)\\.special\\.[a-z0-9]+$", 0, true));
+ m_tvshowEnumRegExps.emplace_back(false, "[\\\\/]([^\\\\/]+)\\.special\\.[a-z0-9]+$", 0, true);
// foo.103*, 103 foo
// XXX: This regex is greedy and will match years in show names. It should always be last.
- m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ -]([0-9]+)([0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$"));
+ m_tvshowEnumRegExps.emplace_back(
+ false,
+ "[\\\\/\\._ -]([0-9]+)([0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$");
m_tvshowMultiPartEnumRegExp = "^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)";
@@ -397,14 +402,6 @@ void CAdvancedSettings::Initialize()
m_PVRDefaultSortOrder.sortBy = SortByDate;
m_PVRDefaultSortOrder.sortOrder = SortOrderDescending;
- m_cacheMemSize = 1024 * 1024 * 20; // 20 MiB
- m_cacheBufferMode = CACHE_BUFFER_MODE_NETWORK; // Default (buffer all network filesystems)
- m_cacheChunkSize = 128 * 1024; // 128 KiB
-
- // the following setting determines the readRate of a player data
- // as multiply of the default data read rate
- m_cacheReadFactor = 4.0f;
-
m_addonPackageFolderSize = 200;
m_jsonOutputCompact = true;
@@ -636,6 +633,7 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
if (pVideoExcludes)
GetCustomRegexps(pVideoExcludes, m_videoCleanStringRegExps);
+ XMLUtils::GetString(pElement, "filenameidentifier", m_videoFilenameIdentifierRegExp);
XMLUtils::GetString(pElement,"cleandatetime", m_videoCleanDateTimeRegExp);
XMLUtils::GetString(pElement,"ppffmpegpostprocessing",m_videoPPFFmpegPostProc);
XMLUtils::GetInt(pElement,"vdpauscaling",m_videoVDPAUScaling);
@@ -849,15 +847,6 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
XMLUtils::GetString(pElement, "catrustfile", m_caTrustFile);
}
- pElement = pRootElement->FirstChildElement("cache");
- if (pElement)
- {
- XMLUtils::GetUInt(pElement, "memorysize", m_cacheMemSize);
- XMLUtils::GetUInt(pElement, "buffermode", m_cacheBufferMode, 0, 4);
- XMLUtils::GetUInt(pElement, "chunksize", m_cacheChunkSize, 256, 1024 * 1024);
- XMLUtils::GetFloat(pElement, "readfactor", m_cacheReadFactor);
- }
-
pElement = pRootElement->FirstChildElement("jsonrpc");
if (pElement)
{
@@ -1044,7 +1033,7 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
CLog::Log(LOGDEBUG," Registering substitution pair:");
CLog::Log(LOGDEBUG, " From: [{}]", CURL::GetRedacted(strFrom));
CLog::Log(LOGDEBUG, " To: [{}]", CURL::GetRedacted(strTo));
- m_pathSubstitutions.push_back(std::make_pair(strFrom,strTo));
+ m_pathSubstitutions.emplace_back(strFrom, strTo);
}
else
{
@@ -1318,7 +1307,7 @@ void CAdvancedSettings::GetCustomTVRegexps(TiXmlElement *pRootElement, SETTINGS_
}
else
{
- settings.push_back(TVShowRegexp(bByDate, regExp, iDefaultSeason, byTitle));
+ settings.emplace_back(bByDate, regExp, iDefaultSeason, byTitle);
}
}
pRegExp = pRegExp->NextSibling("regexp");
@@ -1435,14 +1424,3 @@ void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vecto
arttype = arttype->NextSibling("arttype");
}
}
-
-void ConvertToWhitelist(const std::vector<std::string>& oldlist, std::vector<CVariant>& whitelist)
-{
- for (auto& it : oldlist)
- {
- size_t last_index = it.find_last_not_of("0123456789");
- std::string strFamilyType = it.substr(0, last_index + 1); // "fanart" of "fanart16"
- if (std::find(whitelist.begin(), whitelist.end(), strFamilyType) == whitelist.end())
- whitelist.emplace_back(strFamilyType);
- }
-}
diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
index fc92f1e3c4..a18e7aaacf 100644
--- a/xbmc/settings/AdvancedSettings.h
+++ b/xbmc/settings/AdvancedSettings.h
@@ -18,12 +18,6 @@
#include <utility>
#include <vector>
-#define CACHE_BUFFER_MODE_INTERNET 0
-#define CACHE_BUFFER_MODE_ALL 1
-#define CACHE_BUFFER_MODE_TRUE_INTERNET 2
-#define CACHE_BUFFER_MODE_NONE 3
-#define CACHE_BUFFER_MODE_NETWORK 4
-
class CProfileManager;
class CSettingsManager;
class CVariant;
@@ -205,6 +199,7 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
bool m_fullScreenOnMovieStart;
std::string m_cachePath;
std::string m_videoCleanDateTimeRegExp;
+ std::string m_videoFilenameIdentifierRegExp;
std::vector<std::string> m_videoCleanStringRegExps;
std::vector<std::string> m_videoExcludeFromListingRegExps;
std::vector<std::string> m_allExcludeFromScanRegExps;
@@ -335,11 +330,6 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
bool m_guiSmartRedraw;
unsigned int m_addonPackageFolderSize;
- unsigned int m_cacheMemSize;
- unsigned int m_cacheBufferMode;
- unsigned int m_cacheChunkSize;
- float m_cacheReadFactor;
-
bool m_jsonOutputCompact;
unsigned int m_jsonTcpPort;
diff --git a/xbmc/settings/CMakeLists.txt b/xbmc/settings/CMakeLists.txt
index f53d12b741..d477f51d5d 100644
--- a/xbmc/settings/CMakeLists.txt
+++ b/xbmc/settings/CMakeLists.txt
@@ -4,6 +4,7 @@ set(SOURCES AdvancedSettings.cpp
LibExportSettings.cpp
MediaSettings.cpp
MediaSourceSettings.cpp
+ ServicesSettings.cpp
SettingAddon.cpp
SettingConditions.cpp
SettingControl.cpp
@@ -27,6 +28,7 @@ set(HEADERS AdvancedSettings.h
LibExportSettings.h
MediaSettings.h
MediaSourceSettings.h
+ ServicesSettings.h
SettingAddon.h
SettingConditions.h
SettingControl.h
diff --git a/xbmc/settings/ServicesSettings.cpp b/xbmc/settings/ServicesSettings.cpp
new file mode 100644
index 0000000000..99467ca10a
--- /dev/null
+++ b/xbmc/settings/ServicesSettings.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 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 "ServicesSettings.h"
+
+#include "filesystem/IFileTypes.h"
+#include "guilib/LocalizeStrings.h"
+#include "utils/StringUtils.h"
+
+using namespace XFILE;
+
+void CServicesSettings::SettingOptionsChunkSizesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const auto& kb = g_localizeStrings.Get(37121);
+ const auto& mb = g_localizeStrings.Get(37122);
+
+ list.emplace_back(StringUtils::Format(kb, 16), 16);
+ list.emplace_back(StringUtils::Format(kb, 32), 32);
+ list.emplace_back(StringUtils::Format(kb, 64), 64);
+ list.emplace_back(StringUtils::Format(kb, 128), 128);
+ list.emplace_back(StringUtils::Format(kb, 256), 256);
+ list.emplace_back(StringUtils::Format(kb, 512), 512);
+ list.emplace_back(StringUtils::Format(mb, 1), 1024);
+}
+
+void CServicesSettings::SettingOptionsBufferModesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(37110), CACHE_BUFFER_MODE_NONE);
+ list.emplace_back(g_localizeStrings.Get(37111), CACHE_BUFFER_MODE_TRUE_INTERNET);
+ list.emplace_back(g_localizeStrings.Get(37112), CACHE_BUFFER_MODE_INTERNET);
+ list.emplace_back(g_localizeStrings.Get(37113), CACHE_BUFFER_MODE_NETWORK);
+ list.emplace_back(g_localizeStrings.Get(37114), CACHE_BUFFER_MODE_ALL);
+}
+
+void CServicesSettings::SettingOptionsMemorySizesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const auto& mb = g_localizeStrings.Get(37122);
+ const auto& gb = g_localizeStrings.Get(37123);
+
+ list.emplace_back(StringUtils::Format(mb, 16), 16);
+ list.emplace_back(StringUtils::Format(mb, 20), 20);
+ list.emplace_back(StringUtils::Format(mb, 24), 24);
+ list.emplace_back(StringUtils::Format(mb, 32), 32);
+ list.emplace_back(StringUtils::Format(mb, 48), 48);
+ list.emplace_back(StringUtils::Format(mb, 64), 64);
+ list.emplace_back(StringUtils::Format(mb, 96), 96);
+ list.emplace_back(StringUtils::Format(mb, 128), 128);
+ list.emplace_back(StringUtils::Format(mb, 192), 192);
+ list.emplace_back(StringUtils::Format(mb, 256), 256);
+ list.emplace_back(StringUtils::Format(mb, 384), 384);
+ list.emplace_back(StringUtils::Format(mb, 512), 512);
+ list.emplace_back(StringUtils::Format(mb, 768), 768);
+ list.emplace_back(StringUtils::Format(gb, 1), 1024);
+ list.emplace_back(g_localizeStrings.Get(37115), 0);
+}
+
+void CServicesSettings::SettingOptionsReadFactorsFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back("1.1x", 110);
+ list.emplace_back("1.25x", 125);
+ list.emplace_back("1.5x", 150);
+ list.emplace_back("1.75x", 175);
+ list.emplace_back("2x", 200);
+ list.emplace_back("2.5x", 250);
+ list.emplace_back("3x", 300);
+ list.emplace_back("4x", 400);
+ list.emplace_back("5x", 500);
+ list.emplace_back("7x", 700);
+ list.emplace_back("10x", 1000);
+ list.emplace_back("15x", 1500);
+ list.emplace_back("20x", 2000);
+ list.emplace_back("30x", 3000);
+ list.emplace_back("50x", 5000);
+}
+
+void CServicesSettings::SettingOptionsCacheChunkSizesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const auto& byte = g_localizeStrings.Get(37120);
+ const auto& kb = g_localizeStrings.Get(37121);
+ const auto& mb = g_localizeStrings.Get(37122);
+
+ list.emplace_back(StringUtils::Format(byte, 256), 256);
+ list.emplace_back(StringUtils::Format(byte, 512), 512);
+ list.emplace_back(StringUtils::Format(kb, 1), 1024);
+ list.emplace_back(StringUtils::Format(kb, 2), 2 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 4), 4 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 8), 8 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 16), 16 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 32), 32 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 64), 64 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 128), 128 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 256), 256 * 1024);
+ list.emplace_back(StringUtils::Format(kb, 512), 512 * 1024);
+ list.emplace_back(StringUtils::Format(mb, 1), 1024 * 1024);
+}
diff --git a/xbmc/settings/ServicesSettings.h b/xbmc/settings/ServicesSettings.h
new file mode 100644
index 0000000000..5b8917b228
--- /dev/null
+++ b/xbmc/settings/ServicesSettings.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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 "settings/ISubSettings.h"
+#include "settings/lib/Setting.h"
+
+#include <vector>
+
+class CServicesSettings : public ISubSettings
+{
+public:
+ static void SettingOptionsChunkSizesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsBufferModesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsMemorySizesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsReadFactorsFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsCacheChunkSizesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+};
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index 7f30770710..7f29766365 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -43,6 +43,7 @@
#include "settings/DisplaySettings.h"
#include "settings/MediaSettings.h"
#include "settings/MediaSourceSettings.h"
+#include "settings/ServicesSettings.h"
#include "settings/SettingConditions.h"
#include "settings/SettingsComponent.h"
#include "settings/SkinSettings.h"
@@ -436,6 +437,16 @@ void CSettings::InitializeOptionFillers()
GetSettingsManager()->RegisterSettingOptionsFiller("timezones", CPosixTimezone::SettingOptionsTimezonesFiller);
#endif
GetSettingsManager()->RegisterSettingOptionsFiller("keyboardlayouts", CKeyboardLayoutManager::SettingOptionsKeyboardLayoutsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "filechunksizes", CServicesSettings::SettingOptionsChunkSizesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "filecachebuffermodes", CServicesSettings::SettingOptionsBufferModesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "filecachememorysizes", CServicesSettings::SettingOptionsMemorySizesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "filecachereadfactors", CServicesSettings::SettingOptionsReadFactorsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "filecachechunksizes", CServicesSettings::SettingOptionsCacheChunkSizesFiller);
}
void CSettings::UninitializeOptionFillers()
@@ -482,6 +493,11 @@ void CSettings::UninitializeOptionFillers()
#endif // defined(TARGET_LINUX)
GetSettingsManager()->UnregisterSettingOptionsFiller("verticalsyncs");
GetSettingsManager()->UnregisterSettingOptionsFiller("keyboardlayouts");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("filechunksizes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("filecachebuffermodes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("filecachememorysizes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("filecachereadfactors");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("filecachechunksizes");
}
void CSettings::InitializeConditions()
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
index 349a153065..8a62a03b4d 100644
--- a/xbmc/settings/Settings.h
+++ b/xbmc/settings/Settings.h
@@ -96,6 +96,11 @@ public:
"videolibrary.musicvideoartwhitelist";
static constexpr auto SETTING_VIDEOLIBRARY_SHOWPERFORMERS =
"videolibrary.musicvideosallperformers";
+ static constexpr auto SETTING_VIDEOLIBRARY_IGNOREVIDEOVERSIONS =
+ "videolibrary.ignorevideoversions";
+ static constexpr auto SETTING_VIDEOLIBRARY_IGNOREVIDEOEXTRAS = "videolibrary.ignorevideoextras";
+ static constexpr auto SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER =
+ "videolibrary.showvideoversionsasfolder";
static constexpr auto SETTING_LOCALE_AUDIOLANGUAGE = "locale.audiolanguage";
static constexpr auto SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG = "videoplayer.preferdefaultflag";
static constexpr auto SETTING_VIDEOPLAYER_AUTOPLAYNEXTITEM = "videoplayer.autoplaynextitem";
@@ -131,6 +136,8 @@ public:
static constexpr auto SETTING_VIDEOPLAYER_SUPPORTMVC = "videoplayer.supportmvc";
static constexpr auto SETTING_VIDEOPLAYER_CONVERTDOVI = "videoplayer.convertdovi";
static constexpr auto SETTING_MYVIDEOS_SELECTACTION = "myvideos.selectaction";
+ static constexpr auto SETTING_MYVIDEOS_SELECTDEFAULTVERSION = "myvideos.selectdefaultversion";
+ static constexpr auto SETTING_MYVIDEOS_PLAYACTION = "myvideos.playaction";
static constexpr auto SETTING_MYVIDEOS_USETAGS = "myvideos.usetags";
static constexpr auto SETTING_MYVIDEOS_EXTRACTFLAGS = "myvideos.extractflags";
static constexpr auto SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS = "myvideos.extractchapterthumbs";
@@ -347,6 +354,7 @@ public:
static constexpr auto SETTING_SMB_MINPROTOCOL = "smb.minprotocol";
static constexpr auto SETTING_SMB_MAXPROTOCOL = "smb.maxprotocol";
static constexpr auto SETTING_SMB_LEGACYSECURITY = "smb.legacysecurity";
+ static constexpr auto SETTING_SMB_CHUNKSIZE = "smb.chunksize";
static constexpr auto SETTING_SERVICES_WSDISCOVERY = "services.wsdiscovery";
static constexpr auto SETTING_VIDEOSCREEN_MONITOR = "videoscreen.monitor";
static constexpr auto SETTING_VIDEOSCREEN_SCREEN = "videoscreen.screen";
@@ -459,6 +467,10 @@ public:
static constexpr auto SETTING_SOURCE_VIDEOS = "source.videos";
static constexpr auto SETTING_SOURCE_MUSIC = "source.music";
static constexpr auto SETTING_SOURCE_PICTURES = "source.pictures";
+ static constexpr auto SETTING_FILECACHE_BUFFERMODE = "filecache.buffermode";
+ static constexpr auto SETTING_FILECACHE_MEMORYSIZE = "filecache.memorysize"; // in MBytes
+ static constexpr auto SETTING_FILECACHE_READFACTOR = "filecache.readfactor"; // as integer (x100)
+ static constexpr auto SETTING_FILECACHE_CHUNKSIZE = "filecache.chunksize"; // in Bytes
// values for SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS
static const int VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_MOVIES = 0;
diff --git a/xbmc/settings/dialogs/GUIDialogContentSettings.cpp b/xbmc/settings/dialogs/GUIDialogContentSettings.cpp
index da4caf1b6c..61a5edc454 100644
--- a/xbmc/settings/dialogs/GUIDialogContentSettings.cpp
+++ b/xbmc/settings/dialogs/GUIDialogContentSettings.cpp
@@ -367,16 +367,29 @@ void CGUIDialogContentSettings::InitializeSettings()
// define an enable dependency with (m_useDirectoryNames && !m_containsSingleItem) || !m_useDirectoryNames
CSettingDependency dependencyScanRecursive(SettingDependencyType::Enable, GetSettingsManager());
dependencyScanRecursive.Or()
- ->Add(CSettingDependencyConditionCombinationPtr((new CSettingDependencyConditionCombination(BooleanLogicOperationAnd, GetSettingsManager())) // m_useDirectoryNames && !m_containsSingleItem
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_USE_DIRECTORY_NAMES, "true", SettingDependencyOperator::Equals, false, GetSettingsManager()))) // m_useDirectoryNames
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_CONTAINS_SINGLE_ITEM, "false", SettingDependencyOperator::Equals, false, GetSettingsManager()))))) // !m_containsSingleItem
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_USE_DIRECTORY_NAMES, "false", SettingDependencyOperator::Equals, false, GetSettingsManager()))); // !m_useDirectoryNames
+ ->Add(CSettingDependencyConditionCombinationPtr(
+ (new CSettingDependencyConditionCombination(
+ BooleanLogicOperationAnd,
+ GetSettingsManager())) // m_useDirectoryNames && !m_containsSingleItem
+ ->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_USE_DIRECTORY_NAMES, "true", SettingDependencyOperator::Equals, false,
+ GetSettingsManager())) // m_useDirectoryNames
+ ->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_CONTAINS_SINGLE_ITEM, "false", SettingDependencyOperator::Equals,
+ false, GetSettingsManager())))) // !m_containsSingleItem
+ ->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_USE_DIRECTORY_NAMES, "false", SettingDependencyOperator::Equals, false,
+ GetSettingsManager())); // !m_useDirectoryNames
// define an enable dependency with m_useDirectoryNames && !m_scanRecursive
CSettingDependency dependencyContainsSingleItem(SettingDependencyType::Enable, GetSettingsManager());
dependencyContainsSingleItem.And()
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_USE_DIRECTORY_NAMES, "true", SettingDependencyOperator::Equals, false, GetSettingsManager()))) // m_useDirectoryNames
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_SCAN_RECURSIVE, "false", SettingDependencyOperator::Equals, false, GetSettingsManager()))); // !m_scanRecursive
+ ->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_USE_DIRECTORY_NAMES, "true", SettingDependencyOperator::Equals, false,
+ GetSettingsManager())) // m_useDirectoryNames
+ ->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_SCAN_RECURSIVE, "false", SettingDependencyOperator::Equals, false,
+ GetSettingsManager())); // !m_scanRecursive
SettingDependencies deps;
deps.push_back(dependencyScanRecursive);
diff --git a/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp b/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
index 7e88553af9..bfcca80941 100644
--- a/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
+++ b/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
@@ -348,23 +348,21 @@ void CGUIDialogLibExportSettings::InitializeSettings()
TranslatableIntegerSettingOptions entries;
- entries.push_back(TranslatableIntegerSettingOption(38301, ELIBEXPORT_SINGLEFILE));
- entries.push_back(TranslatableIntegerSettingOption(38303, ELIBEXPORT_TOLIBRARYFOLDER));
- entries.push_back(TranslatableIntegerSettingOption(38302, ELIBEXPORT_SEPARATEFILES));
- entries.push_back(TranslatableIntegerSettingOption(38321, ELIBEXPORT_ARTISTFOLDERS));
+ entries.emplace_back(38301, ELIBEXPORT_SINGLEFILE);
+ entries.emplace_back(38303, ELIBEXPORT_TOLIBRARYFOLDER);
+ entries.emplace_back(38302, ELIBEXPORT_SEPARATEFILES);
+ entries.emplace_back(38321, ELIBEXPORT_ARTISTFOLDERS);
AddList(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE, 38304, SettingLevel::Basic, m_settings.GetExportType(), entries, 38304); // "Choose kind of export output"
AddButton(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, 38305, SettingLevel::Basic);
entries.clear();
if (!m_settings.IsArtistFoldersOnly())
- entries.push_back(TranslatableIntegerSettingOption(132, ELIBEXPORT_ALBUMS)); //ablums
+ entries.emplace_back(132, ELIBEXPORT_ALBUMS); //ablums
if (m_settings.IsSingleFile())
- entries.push_back(TranslatableIntegerSettingOption(134, ELIBEXPORT_SONGS)); //songs
- entries.push_back(
- TranslatableIntegerSettingOption(38043, ELIBEXPORT_ALBUMARTISTS)); //album artists
- entries.push_back(TranslatableIntegerSettingOption(38312, ELIBEXPORT_SONGARTISTS)); //song artists
- entries.push_back(
- TranslatableIntegerSettingOption(38313, ELIBEXPORT_OTHERARTISTS)); //other artists
+ entries.emplace_back(134, ELIBEXPORT_SONGS); //songs
+ entries.emplace_back(38043, ELIBEXPORT_ALBUMARTISTS); //album artists
+ entries.emplace_back(38312, ELIBEXPORT_SONGARTISTS); //song artists
+ entries.emplace_back(38313, ELIBEXPORT_OTHERARTISTS); //other artists
std::vector<int> items;
if (m_settings.IsArtistFoldersOnly())
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp b/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp
index d466e5da1f..77086caf2a 100644
--- a/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp
+++ b/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp
@@ -30,6 +30,7 @@
#include "utils/StringUtils.h"
#include "utils/Variant.h"
+#include <memory>
#include <set>
#include <string>
#include <vector>
@@ -675,8 +676,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUIRadioButtonControl*>(pControl)->SetLabel(label);
- pSettingControl.reset(new CGUIControlRadioButtonSetting(
- static_cast<CGUIRadioButtonControl*>(pControl), iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlRadioButtonSetting>(
+ static_cast<CGUIRadioButtonControl*>(pControl), iControlID, pSetting, this);
}
else if (controlType == "spinner")
{
@@ -686,8 +687,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUISpinControlEx*>(pControl)->SetText(label);
- pSettingControl.reset(new CGUIControlSpinExSetting(static_cast<CGUISpinControlEx*>(pControl),
- iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlSpinExSetting>(
+ static_cast<CGUISpinControlEx*>(pControl), iControlID, pSetting, this);
}
else if (controlType == "edit")
{
@@ -697,8 +698,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUIEditControl*>(pControl)->SetLabel(label);
- pSettingControl.reset(new CGUIControlEditSetting(static_cast<CGUIEditControl*>(pControl),
- iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlEditSetting>(
+ static_cast<CGUIEditControl*>(pControl), iControlID, pSetting, this);
}
else if (controlType == "list")
{
@@ -708,8 +709,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUIButtonControl*>(pControl)->SetLabel(label);
- pSettingControl.reset(new CGUIControlListSetting(static_cast<CGUIButtonControl*>(pControl),
- iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlListSetting>(
+ static_cast<CGUIButtonControl*>(pControl), iControlID, pSetting, this);
}
else if (controlType == "button" || controlType == "slider")
{
@@ -722,8 +723,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUIButtonControl*>(pControl)->SetLabel(label);
- pSettingControl.reset(new CGUIControlButtonSetting(static_cast<CGUIButtonControl*>(pControl),
- iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlButtonSetting>(
+ static_cast<CGUIButtonControl*>(pControl), iControlID, pSetting, this);
}
else
{
@@ -733,8 +734,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUISettingsSliderControl*>(pControl)->SetText(label);
- pSettingControl.reset(new CGUIControlSliderSetting(
- static_cast<CGUISettingsSliderControl*>(pControl), iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlSliderSetting>(
+ static_cast<CGUISettingsSliderControl*>(pControl), iControlID, pSetting, this);
}
}
else if (controlType == "range")
@@ -745,8 +746,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUISettingsSliderControl*>(pControl)->SetText(label);
- pSettingControl.reset(new CGUIControlRangeSetting(
- static_cast<CGUISettingsSliderControl*>(pControl), iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlRangeSetting>(
+ static_cast<CGUISettingsSliderControl*>(pControl), iControlID, pSetting, this);
}
else if (controlType == "label")
{
@@ -756,8 +757,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return NULL;
static_cast<CGUIButtonControl*>(pControl)->SetLabel(label);
- pSettingControl.reset(new CGUIControlLabelSetting(static_cast<CGUIButtonControl*>(pControl),
- iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlLabelSetting>(
+ static_cast<CGUIButtonControl*>(pControl), iControlID, pSetting, this);
}
else if (controlType == "colorbutton")
{
@@ -767,8 +768,8 @@ CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>&
return nullptr;
static_cast<CGUIColorButtonControl*>(pControl)->SetLabel(label);
- pSettingControl.reset(new CGUIControlColorButtonSetting(
- static_cast<CGUIColorButtonControl*>(pControl), iControlID, pSetting, this));
+ pSettingControl = std::make_shared<CGUIControlColorButtonSetting>(
+ static_cast<CGUIColorButtonControl*>(pControl), iControlID, pSetting, this);
}
else
return nullptr;
diff --git a/xbmc/settings/windows/GUIControlSettings.cpp b/xbmc/settings/windows/GUIControlSettings.cpp
index 63897792f6..fdb1a439fd 100644
--- a/xbmc/settings/windows/GUIControlSettings.cpp
+++ b/xbmc/settings/windows/GUIControlSettings.cpp
@@ -124,8 +124,7 @@ static bool GetIntegerOptions(const SettingConstPtr& setting,
const TranslatableIntegerSettingOptions& settingOptions =
pSettingInt->GetTranslatableOptions();
for (const auto& option : settingOptions)
- options.push_back(
- IntegerSettingOption(Localize(option.label, localizer, option.addonId), option.value));
+ options.emplace_back(Localize(option.label, localizer, option.addonId), option.value);
break;
}
@@ -163,7 +162,7 @@ static bool GetIntegerOptions(const SettingConstPtr& setting,
else
strLabel = StringUtils::Format(control->GetFormatString(), i);
- options.push_back(IntegerSettingOption(strLabel, i));
+ options.emplace_back(strLabel, i);
}
break;
@@ -230,7 +229,7 @@ static bool GetStringOptions(const SettingConstPtr& setting,
const TranslatableStringSettingOptions& settingOptions =
pSettingString->GetTranslatableOptions();
for (const auto& option : settingOptions)
- options.push_back(StringSettingOption(Localize(option.first, localizer), option.second));
+ options.emplace_back(Localize(option.first, localizer), option.second);
break;
}
diff --git a/xbmc/test/TestUtil.cpp b/xbmc/test/TestUtil.cpp
index 6215b9356e..57767ea67f 100644
--- a/xbmc/test/TestUtil.cpp
+++ b/xbmc/test/TestUtil.cpp
@@ -97,6 +97,8 @@ struct TestUtilCleanStringData
std::string expTitle;
std::string expTitleYear;
std::string expYear;
+ std::string expIdentifierType{};
+ std::string expIdentifier{};
};
std::ostream& operator<<(std::ostream& os, const TestUtilCleanStringData& rhs)
@@ -110,6 +112,15 @@ class TestUtilCleanString : public Test, public WithParamInterface<TestUtilClean
{
};
+TEST_P(TestUtilCleanString, GetFilenameIdentifier)
+{
+ std::string identifierType;
+ std::string identifier;
+ CUtil::GetFilenameIdentifier(GetParam().input, identifierType, identifier);
+ EXPECT_EQ(identifierType, GetParam().expIdentifierType);
+ EXPECT_EQ(identifier, GetParam().expIdentifier);
+}
+
TEST_P(TestUtilCleanString, CleanString)
{
std::string title, titleYear, year;
@@ -131,6 +142,12 @@ const TestUtilCleanStringData values[] = {
{"Some.Movie.1954.BDRip.1080p.mkv", true, "Some Movie", "Some Movie (1954)", "1954"},
{"Some «Movie».2021.WEB-DL.2160p.HDR.mkv", true, "Some «Movie»", "Some «Movie» (2021)", "2021"},
{"Some Movie (2013).mp4", true, "Some Movie", "Some Movie (2013)", "2013"},
+ {"Some Movie (2013) [imdbid-tt123].mp4", true, "Some Movie", "Some Movie (2013)", "2013",
+ "imdb", "tt123"},
+ {"Some Movie (2013) {tmdb-123}.mp4", true, "Some Movie", "Some Movie (2013)", "2013", "tmdb",
+ "123"},
+ {"Some Movie (2013) {tmdb=123}.mp4", true, "Some Movie", "Some Movie (2013)", "2013", "tmdb",
+ "123"},
// no result because of the text (Director Cut), it can also a be a movie translation
{"Some (Director Cut).BDRemux.mkv", true, "Some (Director Cut)", "Some (Director Cut)", ""}};
diff --git a/xbmc/threads/Event.cpp b/xbmc/threads/Event.cpp
index 1c67f4eac4..9ec7f3f1c0 100644
--- a/xbmc/threads/Event.cpp
+++ b/xbmc/threads/Event.cpp
@@ -12,6 +12,7 @@
#include <algorithm>
#include <limits>
+#include <memory>
#include <mutex>
using namespace std::chrono_literals;
@@ -20,7 +21,7 @@ void CEvent::addGroup(XbmcThreads::CEventGroup* group)
{
std::unique_lock<CCriticalSection> lock(groupListMutex);
if (!groups)
- groups.reset(new std::vector<XbmcThreads::CEventGroup*>);
+ groups = std::make_unique<std::vector<XbmcThreads::CEventGroup*>>();
groups->push_back(group);
}
diff --git a/xbmc/threads/test/TestEvent.cpp b/xbmc/threads/test/TestEvent.cpp
index 5f9fd10fb1..0109a9c513 100644
--- a/xbmc/threads/test/TestEvent.cpp
+++ b/xbmc/threads/test/TestEvent.cpp
@@ -572,7 +572,7 @@ template <class W> void RunMassEventTest(std::vector<std::shared_ptr<W>>& m, boo
{
std::vector<std::shared_ptr<thread>> t(NUMTHREADS);
for(size_t i=0; i<NUMTHREADS; i++)
- t[i].reset(new thread(*m[i]));
+ t[i] = std::make_shared<thread>(*m[i]);
EXPECT_TRUE(waitForThread(g_mutex, NUMTHREADS, 10000ms));
if (canWaitOnEvent)
@@ -608,7 +608,7 @@ TEST(TestMassEvent, General)
std::vector<std::shared_ptr<mass_waiter>> m(NUMTHREADS);
for(size_t i=0; i<NUMTHREADS; i++)
- m[i].reset(new mass_waiter());
+ m[i] = std::make_shared<mass_waiter>();
RunMassEventTest(m,true);
delete g_event;
@@ -620,7 +620,7 @@ TEST(TestMassEvent, Polling)
std::vector<std::shared_ptr<poll_mass_waiter>> m(NUMTHREADS);
for(size_t i=0; i<NUMTHREADS; i++)
- m[i].reset(new poll_mass_waiter());
+ m[i] = std::make_shared<poll_mass_waiter>();
RunMassEventTest(m,false);
delete g_event;
diff --git a/xbmc/utils/BooleanLogic.cpp b/xbmc/utils/BooleanLogic.cpp
index 7ccc54750c..f58a05b987 100644
--- a/xbmc/utils/BooleanLogic.cpp
+++ b/xbmc/utils/BooleanLogic.cpp
@@ -12,6 +12,8 @@
#include "utils/XBMCTinyXML.h"
#include "utils/log.h"
+#include <memory>
+
bool CBooleanLogicValue::Deserialize(const TiXmlNode *node)
{
if (node == NULL)
@@ -112,7 +114,7 @@ bool CBooleanLogic::Deserialize(const TiXmlNode *node)
if (m_operation == NULL)
{
- m_operation = CBooleanLogicOperationPtr(new CBooleanLogicOperation());
+ m_operation = std::make_shared<CBooleanLogicOperation>();
if (m_operation == NULL)
return false;
diff --git a/xbmc/utils/DatabaseUtils.h b/xbmc/utils/DatabaseUtils.h
index c9bf6a6fa2..7e548d08c1 100644
--- a/xbmc/utils/DatabaseUtils.h
+++ b/xbmc/utils/DatabaseUtils.h
@@ -126,6 +126,7 @@ typedef enum
FieldSubtitleLanguage,
FieldProductionCode,
FieldTag,
+ FieldVideoVersion,
FieldChannelName,
FieldChannelNumber,
FieldInstruments,
diff --git a/xbmc/utils/EGLFence.cpp b/xbmc/utils/EGLFence.cpp
index 535e3bce31..9d0065bdaf 100644
--- a/xbmc/utils/EGLFence.cpp
+++ b/xbmc/utils/EGLFence.cpp
@@ -22,6 +22,14 @@ CEGLFence::CEGLFence(EGLDisplay display)
m_eglGetSyncAttribKHR(
CEGLUtils::GetRequiredProcAddress<PFNEGLGETSYNCATTRIBKHRPROC>("eglGetSyncAttribKHR"))
{
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ m_eglDupNativeFenceFDANDROID =
+ CEGLUtils::GetRequiredProcAddress<PFNEGLDUPNATIVEFENCEFDANDROIDPROC>(
+ "eglDupNativeFenceFDANDROID");
+ m_eglClientWaitSyncKHR =
+ CEGLUtils::GetRequiredProcAddress<PFNEGLCLIENTWAITSYNCKHRPROC>("eglClientWaitSyncKHR");
+ m_eglWaitSyncKHR = CEGLUtils::GetRequiredProcAddress<PFNEGLWAITSYNCKHRPROC>("eglWaitSyncKHR");
+#endif
}
void CEGLFence::CreateFence()
@@ -71,3 +79,65 @@ bool CEGLFence::IsSignaled()
return false;
}
+
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+EGLSyncKHR CEGLFence::CreateFence(int fd)
+{
+ CEGLAttributes<1> attributeList;
+ attributeList.Add({{EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd}});
+
+ EGLSyncKHR fence =
+ m_eglCreateSyncKHR(m_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attributeList.Get());
+
+ if (fence == EGL_NO_SYNC_KHR)
+ {
+ CEGLUtils::Log(LOGERROR, "failed to create EGL sync object");
+ return nullptr;
+ }
+
+ return fence;
+}
+
+void CEGLFence::CreateGPUFence()
+{
+ m_gpuFence = CreateFence(EGL_NO_NATIVE_FENCE_FD_ANDROID);
+}
+
+void CEGLFence::CreateKMSFence(int fd)
+{
+ m_kmsFence = CreateFence(fd);
+}
+
+EGLint CEGLFence::FlushFence()
+{
+ EGLint fd = m_eglDupNativeFenceFDANDROID(m_display, m_gpuFence);
+ if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID)
+ CEGLUtils::Log(LOGERROR, "failed to duplicate EGL fence fd");
+
+ m_eglDestroySyncKHR(m_display, m_gpuFence);
+
+ return fd;
+}
+
+void CEGLFence::WaitSyncGPU()
+{
+ if (!m_kmsFence)
+ return;
+
+ if (m_eglWaitSyncKHR(m_display, m_kmsFence, 0) != EGL_TRUE)
+ CEGLUtils::Log(LOGERROR, "failed to create EGL sync point");
+}
+
+void CEGLFence::WaitSyncCPU()
+{
+ if (!m_kmsFence)
+ return;
+
+ EGLint status{EGL_FALSE};
+
+ while (status != EGL_CONDITION_SATISFIED_KHR)
+ status = m_eglClientWaitSyncKHR(m_display, m_kmsFence, 0, EGL_FOREVER_KHR);
+
+ m_eglDestroySyncKHR(m_display, m_kmsFence);
+}
+#endif
diff --git a/xbmc/utils/EGLFence.h b/xbmc/utils/EGLFence.h
index bd96444e47..03c246b60b 100644
--- a/xbmc/utils/EGLFence.h
+++ b/xbmc/utils/EGLFence.h
@@ -30,6 +30,14 @@ public:
void DestroyFence();
bool IsSignaled();
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ void CreateKMSFence(int fd);
+ void CreateGPUFence();
+ EGLint FlushFence();
+ void WaitSyncGPU();
+ void WaitSyncCPU();
+#endif
+
private:
EGLDisplay m_display{nullptr};
EGLSyncKHR m_fence{nullptr};
@@ -37,6 +45,17 @@ private:
PFNEGLCREATESYNCKHRPROC m_eglCreateSyncKHR{nullptr};
PFNEGLDESTROYSYNCKHRPROC m_eglDestroySyncKHR{nullptr};
PFNEGLGETSYNCATTRIBKHRPROC m_eglGetSyncAttribKHR{nullptr};
+
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ EGLSyncKHR CreateFence(int fd);
+
+ EGLSyncKHR m_gpuFence{EGL_NO_SYNC_KHR};
+ EGLSyncKHR m_kmsFence{EGL_NO_SYNC_KHR};
+
+ PFNEGLDUPNATIVEFENCEFDANDROIDPROC m_eglDupNativeFenceFDANDROID{nullptr};
+ PFNEGLCLIENTWAITSYNCKHRPROC m_eglClientWaitSyncKHR{nullptr};
+ PFNEGLWAITSYNCKHRPROC m_eglWaitSyncKHR{nullptr};
+#endif
};
}
diff --git a/xbmc/utils/EGLImage.cpp b/xbmc/utils/EGLImage.cpp
index 36757995f4..623721fd57 100644
--- a/xbmc/utils/EGLImage.cpp
+++ b/xbmc/utils/EGLImage.cpp
@@ -14,6 +14,7 @@
#include "utils/StringUtils.h"
#include "utils/log.h"
+#include <algorithm>
#include <map>
namespace
diff --git a/xbmc/utils/ExecString.cpp b/xbmc/utils/ExecString.cpp
index 7b55af0183..43d986e018 100644
--- a/xbmc/utils/ExecString.cpp
+++ b/xbmc/utils/ExecString.cpp
@@ -33,6 +33,25 @@ CExecString::CExecString(const std::string& function, const std::vector<std::str
SetExecString();
}
+CExecString::CExecString(const std::string& function,
+ const CFileItem& target,
+ const std::string& param)
+ : m_function(function)
+{
+ m_valid = !m_function.empty() && !target.GetPath().empty();
+
+ m_params.emplace_back(StringUtils::Paramify(target.GetPath()));
+
+ if (target.m_bIsFolder)
+ m_params.emplace_back("isdir");
+
+ if (!param.empty())
+ m_params.emplace_back(param);
+
+ if (m_valid)
+ SetExecString();
+}
+
CExecString::CExecString(const CFileItem& item, const std::string& contextWindow)
{
m_valid = Parse(item, contextWindow);
diff --git a/xbmc/utils/ExecString.h b/xbmc/utils/ExecString.h
index fda234aaa6..c66b6d2185 100644
--- a/xbmc/utils/ExecString.h
+++ b/xbmc/utils/ExecString.h
@@ -19,6 +19,7 @@ public:
CExecString() = default;
explicit CExecString(const std::string& execString);
CExecString(const std::string& function, const std::vector<std::string>& params);
+ CExecString(const std::string& function, const CFileItem& target, const std::string& param);
CExecString(const CFileItem& item, const std::string& contextWindow);
virtual ~CExecString() = default;
diff --git a/xbmc/utils/FileOperationJob.cpp b/xbmc/utils/FileOperationJob.cpp
index 8b5066a7e0..b9466b29b8 100644
--- a/xbmc/utils/FileOperationJob.cpp
+++ b/xbmc/utils/FileOperationJob.cpp
@@ -22,6 +22,8 @@
#include "utils/URIUtils.h"
#include "utils/log.h"
+#include <memory>
+
using namespace XFILE;
CFileOperationJob::CFileOperationJob()
@@ -58,7 +60,7 @@ void CFileOperationJob::SetFileOperation(FileAction action,
m_items.Clear();
for (int i = 0; i < items.Size(); i++)
- m_items.Add(CFileItemPtr(new CFileItem(*items[i])));
+ m_items.Add(std::make_shared<CFileItem>(*items[i]));
}
bool CFileOperationJob::DoWork()
@@ -99,7 +101,7 @@ bool CFileOperationJob::DoProcessFile(FileAction action, const std::string& strF
time += data.st_size;
}
- fileOperations.push_back(CFileOperation(action, strFileA, strFileB, time));
+ fileOperations.emplace_back(action, strFileA, strFileB, time);
totalTime += time;
@@ -133,7 +135,7 @@ bool CFileOperationJob::DoProcessFolder(FileAction action, const std::string& st
if (action == ActionMove)
{
- fileOperations.push_back(CFileOperation(ActionDeleteFolder, strPath, "", 1));
+ fileOperations.emplace_back(ActionDeleteFolder, strPath, "", 1);
totalTime += 1.0;
}
diff --git a/xbmc/utils/HttpHeader.cpp b/xbmc/utils/HttpHeader.cpp
index ad73bb2910..b0dfbf8585 100644
--- a/xbmc/utils/HttpHeader.cpp
+++ b/xbmc/utils/HttpHeader.cpp
@@ -81,7 +81,7 @@ bool CHttpHeader::ParseLine(const std::string& headerLine)
StringUtils::Trim(strValue, m_whitespaceChars);
if (!strParam.empty() && !strValue.empty())
- m_params.push_back(HeaderParams::value_type(strParam, strValue));
+ m_params.emplace_back(strParam, strValue);
else
return false;
}
@@ -117,7 +117,7 @@ void CHttpHeader::AddParam(const std::string& param, const std::string& value, c
if (valueTrim.empty())
return;
- m_params.push_back(HeaderParams::value_type(paramLower, valueTrim));
+ m_params.emplace_back(paramLower, valueTrim);
}
std::string CHttpHeader::GetValue(const std::string& strParam) const
diff --git a/xbmc/utils/HttpRangeUtils.cpp b/xbmc/utils/HttpRangeUtils.cpp
index 18ad32ba00..2a210e9cc9 100644
--- a/xbmc/utils/HttpRangeUtils.cpp
+++ b/xbmc/utils/HttpRangeUtils.cpp
@@ -306,7 +306,7 @@ bool CHttpRanges::Parse(const std::string& header, uint64_t totalLength)
if (end < start)
return false;
- m_ranges.push_back(CHttpRange(start, end));
+ m_ranges.emplace_back(start, end);
}
if (m_ranges.empty())
diff --git a/xbmc/utils/JobManager.cpp b/xbmc/utils/JobManager.cpp
index a1ca34bbb2..55d35e985b 100644
--- a/xbmc/utils/JobManager.cpp
+++ b/xbmc/utils/JobManager.cpp
@@ -118,9 +118,9 @@ bool CJobQueue::AddJob(CJob *job)
}
if (m_lifo)
- m_jobQueue.push_back(CJobPointer(job));
+ m_jobQueue.emplace_back(job);
else
- m_jobQueue.push_front(CJobPointer(job));
+ m_jobQueue.emplace_front(job);
QueueNextJob();
return true;
diff --git a/xbmc/utils/LangCodeExpander.cpp b/xbmc/utils/LangCodeExpander.cpp
index f683905049..aa629c2b08 100644
--- a/xbmc/utils/LangCodeExpander.cpp
+++ b/xbmc/utils/LangCodeExpander.cpp
@@ -592,8 +592,7 @@ std::string CLangCodeExpander::ConvertToISO6392T(const std::string& lang)
std::string CLangCodeExpander::FindLanguageCodeWithSubtag(const std::string& str)
{
CRegExp regLangCode;
- if (regLangCode.RegComp(
- "(?:^|\\s|\\()(([A-Za-z]{2,3})-([A-Za-z]{2}|[0-9]{3}|[A-Za-z]{4}))(?:$|\\s|\\))") &&
+ if (regLangCode.RegComp("\\{(([A-Za-z]{2,3})-([A-Za-z]{2}|[0-9]{3}|[A-Za-z]{4}))\\}") &&
regLangCode.RegFind(str) >= 0)
{
return regLangCode.GetMatch(1);
diff --git a/xbmc/utils/LangCodeExpander.h b/xbmc/utils/LangCodeExpander.h
index dc9e5dc48a..80f02d7649 100644
--- a/xbmc/utils/LangCodeExpander.h
+++ b/xbmc/utils/LangCodeExpander.h
@@ -120,7 +120,7 @@ public:
* \brief Find a language code with subtag (e.g. zh-tw, zh-Hans) in to a string.
* This function find a limited set of IETF BCP47 specs, so:
* language tag + region subtag, or, language tag + script subtag.
- * The language code can be found also if wrapped with round brackets.
+ * The language code can be found if wrapped by curly brackets e.g. {pt-br}.
* \param str The string where find the language code.
* \return The language code found in the string, otherwise empty string
*/
diff --git a/xbmc/utils/StringUtils.cpp b/xbmc/utils/StringUtils.cpp
index dce34da879..b74217fe8c 100644
--- a/xbmc/utils/StringUtils.cpp
+++ b/xbmc/utils/StringUtils.cpp
@@ -591,6 +591,39 @@ std::string& StringUtils::RemoveDuplicatedSpacesAndTabs(std::string& str)
return str;
}
+bool StringUtils::IsSpecialCharacter(char c)
+{
+ static constexpr std::string_view view(" .-_+,!'\"\t/\\*?#$%&@()[]{}");
+ if (std::any_of(view.begin(), view.end(), [c](char ch) { return ch == c; }))
+ return true;
+ else
+ return false;
+}
+
+std::string StringUtils::ReplaceSpecialCharactersWithSpace(const std::string& str)
+{
+ std::string result;
+ bool prevCharWasSpecial = false;
+
+ for (char c : str)
+ {
+ if (IsSpecialCharacter(c))
+ {
+ if (!prevCharWasSpecial)
+ {
+ result += ' ';
+ }
+ prevCharWasSpecial = true;
+ }
+ else
+ {
+ result += c;
+ prevCharWasSpecial = false;
+ }
+ }
+ return result;
+}
+
int StringUtils::Replace(std::string &str, char oldChar, char newChar)
{
int replacedChars = 0;
diff --git a/xbmc/utils/StringUtils.h b/xbmc/utils/StringUtils.h
index 110988f9b0..65471f9725 100644
--- a/xbmc/utils/StringUtils.h
+++ b/xbmc/utils/StringUtils.h
@@ -116,6 +116,16 @@ public:
static std::string& TrimRight(std::string &str);
static std::string& TrimRight(std::string &str, const char* const chars);
static std::string& RemoveDuplicatedSpacesAndTabs(std::string& str);
+
+ /*! \brief Check if the character is a special character.
+
+ A special character is not an alphanumeric character, and is not useful to provide information
+
+ \param c Input character to be checked
+ */
+ static bool IsSpecialCharacter(char c);
+
+ static std::string ReplaceSpecialCharactersWithSpace(const std::string& str);
static int Replace(std::string &str, char oldChar, char newChar);
static int Replace(std::string &str, const std::string &oldStr, const std::string &newStr);
static int Replace(std::wstring &str, const std::wstring &oldStr, const std::wstring &newStr);
diff --git a/xbmc/utils/SystemInfo.cpp b/xbmc/utils/SystemInfo.cpp
index d1e53c41bb..92ef021efb 100644
--- a/xbmc/utils/SystemInfo.cpp
+++ b/xbmc/utils/SystemInfo.cpp
@@ -1033,7 +1033,7 @@ int CSysInfo::GetKernelBitness(void)
std::string machine(un.machine);
if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" ||
machine == "ppc64" || machine == "ppc64el" || machine == "ppc64le" || machine == "ia64" ||
- machine == "mips64" || machine == "s390x" || machine == "riscv64")
+ machine == "loongarch64" || machine == "mips64" || machine == "s390x" || machine == "riscv64")
kernelBitness = 64;
else
kernelBitness = 32;
@@ -1080,6 +1080,8 @@ const std::string& CSysInfo::GetKernelCpuFamily(void)
std::string machine(un.machine);
if (machine.compare(0, 3, "arm", 3) == 0 || machine.compare(0, 7, "aarch64", 7) == 0)
kernelCpuFamily = "ARM";
+ else if (machine.compare(0, 9, "loongarch", 9) == 0 || machine.compare(0, 7, "loong64", 7) == 0)
+ kernelCpuFamily = "LoongArch";
else if (machine.compare(0, 4, "mips", 4) == 0)
kernelCpuFamily = "MIPS";
else if (machine.compare(0, 4, "i686", 4) == 0 || machine == "i386" || machine == "amd64" || machine.compare(0, 3, "x86", 3) == 0)
@@ -1466,6 +1468,8 @@ std::string CSysInfo::GetBuildTargetCpuFamily(void)
return "ARM (Thumb)";
#elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
return "ARM";
+#elif defined(__loongarch__)
+ return "LoongArch";
#elif defined(__mips__) || defined(mips) || defined(__mips)
return "MIPS";
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
diff --git a/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp
index 4a435e93d6..ca65b57b65 100644
--- a/xbmc/utils/URIUtils.cpp
+++ b/xbmc/utils/URIUtils.cpp
@@ -195,6 +195,19 @@ std::string URIUtils::GetFileName(const std::string& strFileNameAndPath)
return strFileNameAndPath.substr(slash+1);
}
+std::string URIUtils::GetFileOrFolderName(const std::string& path)
+{
+ std::string temp = path;
+
+ char ch = path[path.size() - 1];
+ if (ch == '/' || ch == '\\')
+ {
+ temp = path.substr(0, path.size() - 1);
+ }
+
+ return temp.substr(temp.find_last_of("/\\") + 1);
+}
+
void URIUtils::Split(const std::string& strFileNameAndPath,
std::string& strPath, std::string& strFileName)
{
diff --git a/xbmc/utils/URIUtils.h b/xbmc/utils/URIUtils.h
index 06d1a712a0..10f9d3995e 100644
--- a/xbmc/utils/URIUtils.h
+++ b/xbmc/utils/URIUtils.h
@@ -36,6 +36,7 @@ public:
static std::string GetFileName(const CURL& url);
static std::string GetFileName(const std::string& strFileNameAndPath);
+ static std::string GetFileOrFolderName(const std::string& path);
static std::string GetExtension(const CURL& url);
static std::string GetExtension(const std::string& strFileName);
diff --git a/xbmc/utils/XBMCTinyXML2.cpp b/xbmc/utils/XBMCTinyXML2.cpp
index c49ce16348..eed58a31a1 100644
--- a/xbmc/utils/XBMCTinyXML2.cpp
+++ b/xbmc/utils/XBMCTinyXML2.cpp
@@ -57,8 +57,8 @@ bool CXBMCTinyXML2::SaveFile(const std::string& filename) const
{
tinyxml2::XMLPrinter printer;
Accept(&printer);
- bool suc =
- file.Write(printer.CStr(), printer.CStrSize()) == static_cast<ssize_t>(printer.CStrSize());
+ const ssize_t sizeToWrite = printer.CStrSize() - 1; // strip trailing '\0'
+ bool suc = file.Write(printer.CStr(), sizeToWrite) == sizeToWrite;
if (suc)
file.Flush();
diff --git a/xbmc/utils/guilib/CMakeLists.txt b/xbmc/utils/guilib/CMakeLists.txt
new file mode 100644
index 0000000000..84e060b1fb
--- /dev/null
+++ b/xbmc/utils/guilib/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(SOURCES GUIContentUtils.cpp)
+
+set(HEADERS GUIContentUtils.h)
+
+core_add_library(utils_guilib)
diff --git a/xbmc/utils/guilib/GUIContentUtils.cpp b/xbmc/utils/guilib/GUIContentUtils.cpp
new file mode 100644
index 0000000000..d98fd66a2f
--- /dev/null
+++ b/xbmc/utils/guilib/GUIContentUtils.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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 "GUIContentUtils.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "addons/gui/GUIDialogAddonInfo.h"
+#include "music/dialogs/GUIDialogMusicInfo.h"
+#include "pvr/PVRManager.h"
+#include "pvr/guilib/PVRGUIActionsUtils.h"
+#include "video/VideoInfoTag.h"
+#include "video/dialogs/GUIDialogVideoInfo.h"
+
+using namespace UTILS::GUILIB;
+
+bool CGUIContentUtils::HasInfoForItem(const CFileItem& item)
+{
+ if (item.HasVideoInfoTag() && !item.HasPVRRecordingInfoTag())
+ {
+ auto mediaType = item.GetVideoInfoTag()->m_type;
+ return (mediaType == MediaTypeMovie || mediaType == MediaTypeTvShow ||
+ mediaType == MediaTypeSeason || mediaType == MediaTypeEpisode ||
+ mediaType == MediaTypeVideo || mediaType == MediaTypeVideoCollection ||
+ mediaType == MediaTypeMusicVideo);
+ }
+
+ return (item.HasMusicInfoTag() || item.HasAddonInfo() ||
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Utils>().HasInfoForItem(item));
+}
+
+bool CGUIContentUtils::ShowInfoForItem(const CFileItem& item)
+{
+ if (item.HasAddonInfo())
+ {
+ return CGUIDialogAddonInfo::ShowForItem(std::make_shared<CFileItem>(item));
+ }
+ else if (CServiceBroker::GetPVRManager().Get<PVR::GUI::Utils>().HasInfoForItem(item))
+ {
+ return CServiceBroker::GetPVRManager().Get<PVR::GUI::Utils>().OnInfo(item);
+ }
+ else if (item.HasVideoInfoTag())
+ {
+ CGUIDialogVideoInfo::ShowFor(item);
+ return true;
+ }
+ else if (item.HasMusicInfoTag())
+ {
+ CGUIDialogMusicInfo::ShowFor(std::make_shared<CFileItem>(item).get());
+ return true;
+ }
+ return false;
+}
diff --git a/xbmc/utils/guilib/GUIContentUtils.h b/xbmc/utils/guilib/GUIContentUtils.h
new file mode 100644
index 0000000000..d395ecbb81
--- /dev/null
+++ b/xbmc/utils/guilib/GUIContentUtils.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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
+
+class CFileItem;
+
+namespace UTILS
+{
+namespace GUILIB
+{
+class CGUIContentUtils
+{
+public:
+ /*! \brief Check whether an information dialog is available for the given item.
+ \param item The item to process
+ \return True if an information dialog is available, false otherwise
+ */
+ static bool HasInfoForItem(const CFileItem& item);
+
+ /*! \brief Show an information dialog for the given item.
+ \param item The item to process
+ \return True if an information dialog was displayed, false otherwise
+ */
+ static bool ShowInfoForItem(const CFileItem& item);
+};
+} // namespace GUILIB
+} // namespace UTILS
diff --git a/xbmc/utils/test/TestCPUInfo.cpp b/xbmc/utils/test/TestCPUInfo.cpp
index b8ca20e70e..a411e2cfcc 100644
--- a/xbmc/utils/test/TestCPUInfo.cpp
+++ b/xbmc/utils/test/TestCPUInfo.cpp
@@ -51,6 +51,7 @@ TEST_F(TestCPUInfo, GetTemperature)
CTemperature t;
EXPECT_TRUE(CServiceBroker::GetCPUInfo()->GetTemperature(t));
EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ(t.ToCelsius(), 50);
}
TEST_F(TestCPUInfo, CoreInfo)
diff --git a/xbmc/utils/test/TestGPUInfo.cpp b/xbmc/utils/test/TestGPUInfo.cpp
index 331a62c3bd..495bc0cc24 100644
--- a/xbmc/utils/test/TestGPUInfo.cpp
+++ b/xbmc/utils/test/TestGPUInfo.cpp
@@ -24,7 +24,7 @@ protected:
#if defined(TARGET_WINDOWS)
TEST_F(TestGPUInfo, DISABLED_GetTemperatureFromCmd)
#else
-TEST_F(TestGPUInfo, GetTemperature)
+TEST_F(TestGPUInfo, GetTemperatureFromCmd)
#endif
{
CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_gpuTempCmd = "echo '50 c'";
diff --git a/xbmc/utils/test/TestURIUtils.cpp b/xbmc/utils/test/TestURIUtils.cpp
index 7122fe9762..3c720b4980 100644
--- a/xbmc/utils/test/TestURIUtils.cpp
+++ b/xbmc/utils/test/TestURIUtils.cpp
@@ -189,15 +189,18 @@ TEST_F(TestURIUtils, SubstitutePath)
from = "C:\\My Videos";
to = "https://myserver/some%20other%20path";
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.push_back(std::make_pair(from, to));
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.emplace_back(
+ from, to);
from = "/this/path1";
to = "/some/other/path2";
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.push_back(std::make_pair(from, to));
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.emplace_back(
+ from, to);
from = "davs://otherserver/my%20music%20path";
to = "D:\\Local Music\\MP3 Collection";
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.push_back(std::make_pair(from, to));
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pathSubstitutions.emplace_back(
+ from, to);
ref = "https://myserver/some%20other%20path/sub%20dir/movie%20name.avi";
var = URIUtils::SubstitutePath("C:\\My Videos\\sub dir\\movie name.avi");
diff --git a/xbmc/utils/test/TestVariant.cpp b/xbmc/utils/test/TestVariant.cpp
index c38370480c..f5bf687ea6 100644
--- a/xbmc/utils/test/TestVariant.cpp
+++ b/xbmc/utils/test/TestVariant.cpp
@@ -294,7 +294,7 @@ TEST(TestVariant, empty)
std::map<std::string, std::string> strmap;
EXPECT_TRUE(CVariant(strmap).empty());
- strmap.emplace(std::make_pair(std::string("key"), std::string("value")));
+ strmap.emplace(std::string("key"), std::string("value"));
EXPECT_FALSE(CVariant(strmap).empty());
std::string str;
diff --git a/xbmc/video/ContextMenus.cpp b/xbmc/video/ContextMenus.cpp
index c72c321e66..955ad2b3a1 100644
--- a/xbmc/video/ContextMenus.cpp
+++ b/xbmc/video/ContextMenus.cpp
@@ -9,17 +9,23 @@
#include "ContextMenus.h"
#include "Autorun.h"
+#include "ContextMenuManager.h"
#include "FileItem.h"
#include "GUIUserMessages.h"
#include "ServiceBroker.h"
#include "application/Application.h"
+#include "cores/playercorefactory/PlayerCoreFactory.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/LocalizeStrings.h"
+#include "utils/ExecString.h"
+#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "video/VideoInfoTag.h"
#include "video/VideoUtils.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
+#include "video/guilib/VideoSelectActionProcessor.h"
#include <utility>
@@ -124,10 +130,8 @@ bool CVideoMarkUnWatched::Execute(const std::shared_ptr<CFileItem>& item) const
bool CVideoBrowse::IsVisible(const CFileItem& item) const
{
- if (item.IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
- return false; // handled by CMediaWindow
-
- return item.m_bIsFolder && VIDEO_UTILS::IsItemPlayable(item);
+ return ((item.m_bIsFolder || item.IsFileFolder(EFILEFOLDER_MASK_ONBROWSE)) &&
+ VIDEO_UTILS::IsItemPlayable(item));
}
bool CVideoBrowse::Execute(const std::shared_ptr<CFileItem>& item) const
@@ -142,19 +146,101 @@ bool CVideoBrowse::Execute(const std::shared_ptr<CFileItem>& item) const
auto& windowMgr = CServiceBroker::GetGUI()->GetWindowManager();
+ // For file directory browsing, we need item's dyn path, for everything else the path.
+ const std::string path{item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE) ? item->GetDynPath()
+ : item->GetPath()};
+
if (target == windowMgr.GetActiveWindow())
{
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, target, 0, GUI_MSG_UPDATE);
- msg.SetStringParam(item->GetPath());
+ msg.SetStringParam(path);
windowMgr.SendMessage(msg);
}
else
{
- windowMgr.ActivateWindow(target, {item->GetPath(), "return"});
+ windowMgr.ActivateWindow(target, {path, "return"});
}
return true;
}
+namespace
+{
+bool ExecuteAction(const CExecString& execute)
+{
+ const std::string execStr{execute.GetExecString()};
+ if (!execStr.empty())
+ {
+ CGUIMessage message(GUI_MSG_EXECUTE, 0, 0);
+ message.SetStringParam(execStr);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+ return true;
+ }
+ return false;
+}
+
+class CVideoSelectActionProcessor : public VIDEO::GUILIB::CVideoSelectActionProcessorBase
+{
+public:
+ explicit CVideoSelectActionProcessor(const std::shared_ptr<CFileItem>& item)
+ : CVideoSelectActionProcessorBase(item)
+ {
+ }
+
+protected:
+ bool OnPlayPartSelected(unsigned int part) override
+ {
+ // part numbers are 1-based
+ ExecuteAction({"PlayMedia", *m_item, StringUtils::Format("playoffset={}", part - 1)});
+ return true;
+ }
+
+ bool OnResumeSelected() override
+ {
+ ExecuteAction({"PlayMedia", *m_item, "resume"});
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ ExecuteAction({"PlayMedia", *m_item, "noresume"});
+ return true;
+ }
+
+ bool OnQueueSelected() override
+ {
+ ExecuteAction({"QueueMedia", *m_item, ""});
+ return true;
+ }
+
+ bool OnInfoSelected() override
+ {
+ CGUIDialogVideoInfo::ShowFor(*m_item);
+ return true;
+ }
+
+ bool OnMoreSelected() override
+ {
+ CONTEXTMENU::ShowFor(m_item, CContextMenuManager::MAIN);
+ return true;
+ }
+};
+} // unnamed namespace
+
+bool CVideoChooseVersion::IsVisible(const CFileItem& item) const
+{
+ return item.HasVideoVersions();
+}
+
+bool CVideoChooseVersion::Execute(const std::shared_ptr<CFileItem>& item) const
+{
+ // force selection dialog, regardless of any settings like 'Select default video version'
+ item->SetProperty("force_choose_video_version", true);
+ CVideoSelectActionProcessor proc{item};
+ const bool ret = proc.ProcessDefaultAction();
+ item->ClearProperty("force_choose_video_version");
+ return ret;
+}
+
std::string CVideoResume::GetLabel(const CFileItem& item) const
{
return VIDEO_UTILS::GetResumeString(item.GetItemToPlay());
@@ -171,41 +257,100 @@ bool CVideoResume::IsVisible(const CFileItem& itemIn) const
namespace
{
-void SetPathAndPlay(CFileItem& item)
+class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
{
- if (!item.m_bIsFolder && item.IsVideoDb())
+public:
+ explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item,
+ const std::string& player)
+ : CVideoPlayActionProcessorBase(item), m_player(player)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ Play();
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ Play();
+ return true;
+ }
+
+private:
+ void Play()
{
- item.SetProperty("original_listitem_url", item.GetPath());
- item.SetPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
+ m_item->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
+ const ContentUtils::PlayMode mode{m_item->GetProperty("CheckAutoPlayNextItem").asBoolean()
+ ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
+ : ContentUtils::PlayMode::PLAY_ONLY_THIS};
+ VIDEO_UTILS::PlayItem(m_item, m_player, mode);
}
- item.SetProperty("check_resume", false);
- if (item.IsLiveTV()) // pvr tv or pvr radio?
+ const std::string m_player;
+};
+
+void SetPathAndPlay(const std::shared_ptr<CFileItem>& item, const std::string& player, bool resume)
+{
+ item->SetProperty("check_resume", false);
+
+ if (item->IsLiveTV()) // pvr tv or pvr radio?
{
- g_application.PlayMedia(item, "", PLAYLIST::TYPE_VIDEO);
+ g_application.PlayMedia(*item, "", PLAYLIST::TYPE_VIDEO);
}
else
{
- item.SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
+ if (!item->m_bIsFolder && item->IsVideoDb())
+ {
+ item->SetProperty("original_listitem_url", item->GetPath());
+ item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
+ }
- const ContentUtils::PlayMode mode = item.GetProperty("CheckAutoPlayNextItem").asBoolean()
- ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
- : ContentUtils::PlayMode::PLAY_ONLY_THIS;
- VIDEO_UTILS::PlayItem(std::make_shared<CFileItem>(item), mode);
+ // play the given/default video version, if multiple versions are available
+ item->SetProperty("prohibit_choose_video_version", true);
+
+ CVideoPlayActionProcessor proc{item, player};
+ if (resume && (item->GetStartOffset() == STARTOFFSET_RESUME ||
+ VIDEO_UTILS::GetItemResumeInformation(*item).isResumable))
+ proc.ProcessAction(VIDEO::GUILIB::ACTION_RESUME);
+ else
+ proc.ProcessAction(VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING);
+
+ item->ClearProperty("prohibit_choose_video_version");
}
}
+
+std::vector<std::string> GetPlayers(const CPlayerCoreFactory& playerCoreFactory,
+ const CFileItem& item)
+{
+ std::vector<std::string> players;
+ if (item.IsVideoDb())
+ {
+ //! @todo CPlayerCoreFactory and classes called from there do not handle dyn path correctly.
+ CFileItem item2{item};
+ item2.SetPath(item.GetDynPath());
+ playerCoreFactory.GetPlayers(item2, players);
+ }
+ else
+ playerCoreFactory.GetPlayers(item, players);
+
+ return players;
+}
} // unnamed namespace
bool CVideoResume::Execute(const std::shared_ptr<CFileItem>& itemIn) const
{
- CFileItem item(itemIn->GetItemToPlay());
+ const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
#ifdef HAS_OPTICAL_DRIVE
- if (item.IsDVD() || item.IsCDDA())
- return MEDIA_DETECT::CAutorun::PlayDisc(item.GetPath(), true, false);
+ if (item->IsDVD() || item->IsCDDA())
+ return MEDIA_DETECT::CAutorun::PlayDisc(item->GetPath(), true, false);
#endif
- item.SetStartOffset(STARTOFFSET_RESUME);
- SetPathAndPlay(item);
+ item->SetStartOffset(STARTOFFSET_RESUME);
+ SetPathAndPlay(item, "", true);
return true;
};
@@ -226,15 +371,36 @@ bool CVideoPlay::IsVisible(const CFileItem& item) const
bool CVideoPlay::Execute(const std::shared_ptr<CFileItem>& itemIn) const
{
- CFileItem item(itemIn->GetItemToPlay());
+ const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
#ifdef HAS_OPTICAL_DRIVE
- if (item.IsDVD() || item.IsCDDA())
- return MEDIA_DETECT::CAutorun::PlayDisc(item.GetPath(), true, true);
+ if (item->IsDVD() || item->IsCDDA())
+ return MEDIA_DETECT::CAutorun::PlayDisc(item->GetPath(), true, true);
#endif
- SetPathAndPlay(item);
+ SetPathAndPlay(item, "", false);
return true;
};
+bool CVideoPlayUsing::IsVisible(const CFileItem& item) const
+{
+ const CPlayerCoreFactory& playerCoreFactory{CServiceBroker::GetPlayerCoreFactory()};
+ return (GetPlayers(playerCoreFactory, item).size() > 1) && VIDEO_UTILS::IsItemPlayable(item);
+}
+
+bool CVideoPlayUsing::Execute(const std::shared_ptr<CFileItem>& itemIn) const
+{
+ const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
+
+ const CPlayerCoreFactory& playerCoreFactory{CServiceBroker::GetPlayerCoreFactory()};
+ const std::vector<std::string> players{GetPlayers(playerCoreFactory, *item)};
+ const std::string player{playerCoreFactory.SelectPlayerDialog(players)};
+ if (!player.empty())
+ {
+ SetPathAndPlay(item, player, false);
+ return true;
+ }
+ return false;
+}
+
namespace
{
void SelectNextItem(int windowID)
@@ -254,14 +420,23 @@ void SelectNextItem(int windowID)
}
}
}
-} // unnamed namespace
-bool CVideoQueue::IsVisible(const CFileItem& item) const
+bool CanQueue(const CFileItem& item)
{
- if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
+ if (!item.CanQueue())
+ return false;
+
+ const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
+ if (windowId == WINDOW_VIDEO_PLAYLIST)
return false; // Already queued
- if (!item.CanQueue())
+ return true;
+}
+} // unnamed namespace
+
+bool CVideoQueue::IsVisible(const CFileItem& item) const
+{
+ if (!CanQueue(item))
return false;
return VIDEO_UTILS::IsItemPlayable(item);
@@ -269,13 +444,10 @@ bool CVideoQueue::IsVisible(const CFileItem& item) const
bool CVideoQueue::Execute(const std::shared_ptr<CFileItem>& item) const
{
- const int windowID = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
- if (windowID == WINDOW_VIDEO_PLAYLIST)
- return false; // Already queued
-
VIDEO_UTILS::QueueItem(item, VIDEO_UTILS::QueuePosition::POSITION_END);
// Set selection to next item in active window's view.
+ const int windowID = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
SelectNextItem(windowID);
return true;
@@ -283,10 +455,7 @@ bool CVideoQueue::Execute(const std::shared_ptr<CFileItem>& item) const
bool CVideoPlayNext::IsVisible(const CFileItem& item) const
{
- if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
- return false; // Already queued
-
- if (!item.CanQueue())
+ if (!CanQueue(item))
return false;
return VIDEO_UTILS::IsItemPlayable(item);
@@ -294,9 +463,6 @@ bool CVideoPlayNext::IsVisible(const CFileItem& item) const
bool CVideoPlayNext::Execute(const std::shared_ptr<CFileItem>& item) const
{
- if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
- return false; // Already queued
-
VIDEO_UTILS::QueueItem(item, VIDEO_UTILS::QueuePosition::POSITION_BEGIN);
return true;
};
@@ -311,10 +477,10 @@ std::string CVideoPlayAndQueue::GetLabel(const CFileItem& item) const
bool CVideoPlayAndQueue::IsVisible(const CFileItem& item) const
{
- const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
- if (windowId == WINDOW_VIDEO_PLAYLIST)
- return false; // Already queued
+ if (!CanQueue(item))
+ return false;
+ const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
if ((windowId == WINDOW_TV_RECORDINGS || windowId == WINDOW_RADIO_RECORDINGS) &&
item.IsUsablePVRRecording())
return true;
@@ -325,16 +491,13 @@ bool CVideoPlayAndQueue::IsVisible(const CFileItem& item) const
bool CVideoPlayAndQueue::Execute(const std::shared_ptr<CFileItem>& item) const
{
const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
- if (windowId == WINDOW_VIDEO_PLAYLIST)
- return false; // Already queued
-
if ((windowId == WINDOW_TV_RECORDINGS || windowId == WINDOW_RADIO_RECORDINGS) &&
item->IsUsablePVRRecording())
{
const ContentUtils::PlayMode mode = VIDEO_UTILS::IsAutoPlayNextItem(*item)
? ContentUtils::PlayMode::PLAY_ONLY_THIS
: ContentUtils::PlayMode::PLAY_FROM_HERE;
- VIDEO_UTILS::PlayItem(item, mode);
+ VIDEO_UTILS::PlayItem(item, "", mode);
return true;
}
diff --git a/xbmc/video/ContextMenus.h b/xbmc/video/ContextMenus.h
index a53c7c2217..6728f58d54 100644
--- a/xbmc/video/ContextMenus.h
+++ b/xbmc/video/ContextMenus.h
@@ -86,6 +86,13 @@ struct CVideoBrowse : CStaticContextMenuAction
bool Execute(const std::shared_ptr<CFileItem>& item) const override;
};
+struct CVideoChooseVersion : CStaticContextMenuAction
+{
+ CVideoChooseVersion() : CStaticContextMenuAction(40208) {} // Choose version
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& item) const override;
+};
+
struct CVideoResume : IContextMenuItem
{
std::string GetLabel(const CFileItem& item) const override;
@@ -100,6 +107,13 @@ struct CVideoPlay : IContextMenuItem
bool Execute(const std::shared_ptr<CFileItem>& _item) const override;
};
+struct CVideoPlayUsing : CStaticContextMenuAction
+{
+ CVideoPlayUsing() : CStaticContextMenuAction(15213) {} // Play using...
+ bool IsVisible(const CFileItem& item) const override;
+ bool Execute(const std::shared_ptr<CFileItem>& _item) const override;
+};
+
struct CVideoQueue : CStaticContextMenuAction
{
CVideoQueue() : CStaticContextMenuAction(13347) {} // Queue item
diff --git a/xbmc/video/GUIViewStateVideo.cpp b/xbmc/video/GUIViewStateVideo.cpp
index 30652720f7..278bec56b4 100644
--- a/xbmc/video/GUIViewStateVideo.cpp
+++ b/xbmc/video/GUIViewStateVideo.cpp
@@ -181,14 +181,16 @@ CGUIViewStateWindowVideoNav::CGUIViewStateWindowVideoNav(const CFileItemList& it
}
break;
case NODE_TYPE_TAGS:
- {
- AddSortMethod(SortByLabel, sortAttributes, 551, LABEL_MASKS("%T","", "%T","")); // Title, empty | Title, empty
- SetSortMethod(SortByLabel);
+ case NODE_TYPE_VIDEOVERSIONS:
+ {
+ AddSortMethod(SortByLabel, sortAttributes, 551,
+ LABEL_MASKS("%T", "", "%T", "")); // Title, empty | Title, empty
+ SetSortMethod(SortByLabel);
- const CViewState *viewState = CViewStateSettings::GetInstance().Get("videonavgenres");
- SetViewAsControl(viewState->m_viewMode);
- SetSortOrder(viewState->m_sortDescription.sortOrder);
- }
+ const CViewState* viewState = CViewStateSettings::GetInstance().Get("videonavgenres");
+ SetViewAsControl(viewState->m_viewMode);
+ SetSortOrder(viewState->m_sortDescription.sortOrder);
+ }
break;
case NODE_TYPE_EPISODES:
{
diff --git a/xbmc/video/Teletext.cpp b/xbmc/video/Teletext.cpp
index 1a638296a4..aa64ec1c96 100644
--- a/xbmc/video/Teletext.cpp
+++ b/xbmc/video/Teletext.cpp
@@ -447,6 +447,25 @@ CTeletextDecoder::CTeletextDecoder()
CTeletextDecoder::~CTeletextDecoder() = default;
+bool CTeletextDecoder::Changed()
+{
+ std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
+ if (IsSubtitlePage(m_txtCache->Page))
+ {
+ m_updateTexture = true;
+ return true;
+ }
+
+ /* Update on every changed second */
+ if (m_txtCache->TimeString[7] != prevTimeSec)
+ {
+ prevTimeSec = m_txtCache->TimeString[7];
+ m_updateTexture = true;
+ return true;
+ }
+ return false;
+}
+
bool CTeletextDecoder::HandleAction(const CAction &action)
{
if (m_txtCache == NULL)
@@ -1321,20 +1340,6 @@ void CTeletextDecoder::RenderPage()
SetPosX(33+i);
}
}
-
- if (!IsSubtitlePage(m_txtCache->Page))
- {
- /* Update on every changed second */
- if (m_txtCache->TimeString[7] != prevTimeSec)
- {
- prevTimeSec = m_txtCache->TimeString[7];
- m_updateTexture = true;
- }
- }
- else
- {
- m_updateTexture = true;
- }
}
DoFlashing(StartRow);
m_txtCache->NationalSubset = national_subset_bak;
diff --git a/xbmc/video/Teletext.h b/xbmc/video/Teletext.h
index 105d8424cc..b0a8865a17 100644
--- a/xbmc/video/Teletext.h
+++ b/xbmc/video/Teletext.h
@@ -46,6 +46,13 @@ public:
}
int GetHeight() { return m_RenderInfo.Height; }
int GetWidth() { return m_RenderInfo.Width; }
+
+ /*!
+ \brief Checks if the data in the decoder has changed
+ \return true if the data in the decoder has changed, false otherwise
+ */
+ bool Changed();
+
bool InitDecoder();
void EndDecoder();
void RenderPage();
diff --git a/xbmc/video/VideoChapterImageFileLoader.cpp b/xbmc/video/VideoChapterImageFileLoader.cpp
index 5cb0704ff4..49bfd2dc06 100644
--- a/xbmc/video/VideoChapterImageFileLoader.cpp
+++ b/xbmc/video/VideoChapterImageFileLoader.cpp
@@ -30,7 +30,7 @@ std::unique_ptr<CTexture> VIDEO::CVideoChapterImageFileLoader::Load(
// "goofy" chapter path because these paths don't yet conform to 'image://' path standard
// 10 = length of "chapter://" string prefix from GUIDialogVideoBookmarks
- size_t lastSlashPos = goofyChapterPath.rfind("/");
+ size_t lastSlashPos = goofyChapterPath.rfind('/');
std::string cleanname = goofyChapterPath.substr(10, lastSlashPos - 10);
int chapterNum = 0;
diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp
index 087232e4f2..a8e460d84d 100644
--- a/xbmc/video/VideoDatabase.cpp
+++ b/xbmc/video/VideoDatabase.cpp
@@ -198,6 +198,14 @@ void CVideoDatabase::CreateTables()
CLog::Log(LOGINFO, "create uniqueid table");
m_pDS->exec("CREATE TABLE uniqueid (uniqueid_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, value TEXT, type TEXT)");
+
+ CLog::Log(LOGINFO, "create videoversiontype table");
+ m_pDS->exec("CREATE TABLE videoversiontype (id INTEGER PRIMARY KEY, name TEXT, owner INTEGER)");
+ InitializeVideoVersionTypeTable();
+
+ CLog::Log(LOGINFO, "create videoversion table");
+ m_pDS->exec("CREATE TABLE videoversion (idFile INTEGER PRIMARY KEY, idMedia INTEGER, mediaType "
+ "TEXT, itemType INTEGER, idType INTEGER)");
}
void CVideoDatabase::CreateLinkIndex(const char *table)
@@ -279,6 +287,8 @@ void CVideoDatabase::CreateAnalytics()
"actor_link (media_id, media_type(20), actor_id)");
m_pDS->exec("CREATE INDEX ix_actor_link_3 ON actor_link (media_type(20))");
+ m_pDS->exec("CREATE INDEX ix_videoversion ON videoversion (idMedia, mediaType(20))");
+
CreateLinkIndex("tag");
CreateForeignLinkIndex("director", "actor");
CreateForeignLinkIndex("writer", "actor");
@@ -299,6 +309,7 @@ void CVideoDatabase::CreateAnalytics()
"DELETE FROM tag_link WHERE media_id=old.idMovie AND media_type='movie'; "
"DELETE FROM rating WHERE media_id=old.idMovie AND media_type='movie'; "
"DELETE FROM uniqueid WHERE media_id=old.idMovie AND media_type='movie'; "
+ "DELETE FROM videoversion WHERE idMedia=old.idMovie AND mediaType='movie'; "
"END");
m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
"DELETE FROM actor_link WHERE media_id=old.idShow AND media_type='tvshow'; "
@@ -347,6 +358,8 @@ void CVideoDatabase::CreateAnalytics()
"DELETE FROM settings WHERE idFile=old.idFile; "
"DELETE FROM stacktimes WHERE idFile=old.idFile; "
"DELETE FROM streamdetails WHERE idFile=old.idFile; "
+ "DELETE FROM videoversion WHERE idFile=old.idFile; "
+ "DELETE FROM art WHERE media_id=old.idFile AND media_type='videoversion'; "
"END");
CreateViews();
@@ -536,36 +549,43 @@ void CVideoDatabase::CreateViews()
CLog::Log(LOGINFO, "create movie_view");
std::string movieview = PrepareSQL("CREATE VIEW movie_view AS SELECT"
- " movie.*,"
- " sets.strSet AS strSet,"
- " sets.strOverview AS strSetOverview,"
- " files.strFileName AS strFileName,"
- " path.strPath AS strPath,"
- " files.playCount AS playCount,"
- " files.lastPlayed AS lastPlayed, "
- " files.dateAdded AS dateAdded, "
- " bookmark.timeInSeconds AS resumeTimeInSeconds, "
- " bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
- " bookmark.playerState AS playerState, "
- " rating.rating AS rating, "
- " rating.votes AS votes, "
- " rating.rating_type AS rating_type, "
- " uniqueid.value AS uniqueid_value, "
- " uniqueid.type AS uniqueid_type "
- "FROM movie"
- " LEFT JOIN sets ON"
- " sets.idSet = movie.idSet"
- " JOIN files ON"
- " files.idFile=movie.idFile"
- " JOIN path ON"
- " path.idPath=files.idPath"
- " LEFT JOIN bookmark ON"
- " bookmark.idFile=movie.idFile AND bookmark.type=1"
- " LEFT JOIN rating ON"
- " rating.rating_id=movie.c%02d"
- " LEFT JOIN uniqueid ON"
- " uniqueid.uniqueid_id=movie.c%02d",
- VIDEODB_ID_RATING_ID, VIDEODB_ID_IDENT_ID);
+ " movie.*,"
+ " sets.strSet AS strSet,"
+ " sets.strOverview AS strSetOverview,"
+ " files.strFileName AS strFileName,"
+ " path.strPath AS strPath,"
+ " files.playCount AS playCount,"
+ " files.lastPlayed AS lastPlayed, "
+ " files.dateAdded AS dateAdded, "
+ " bookmark.timeInSeconds AS resumeTimeInSeconds, "
+ " bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
+ " bookmark.playerState AS playerState, "
+ " rating.rating AS rating, "
+ " rating.votes AS votes, "
+ " rating.rating_type AS rating_type, "
+ " uniqueid.value AS uniqueid_value, "
+ " uniqueid.type AS uniqueid_type, "
+ " EXISTS( "
+ " SELECT 1 "
+ " FROM videoversion vv "
+ " WHERE vv.idMedia = movie.idMovie "
+ " AND vv.mediaType = 'movie' "
+ " AND vv.idFile <> movie.idFile "
+ " ) AS hasVideoVersions "
+ "FROM movie"
+ " LEFT JOIN sets ON"
+ " sets.idSet = movie.idSet"
+ " JOIN files ON"
+ " files.idFile=movie.idFile"
+ " JOIN path ON"
+ " path.idPath=files.idPath"
+ " LEFT JOIN bookmark ON"
+ " bookmark.idFile=movie.idFile AND bookmark.type=1"
+ " LEFT JOIN rating ON"
+ " rating.rating_id=movie.c%02d"
+ " LEFT JOIN uniqueid ON"
+ " uniqueid.uniqueid_id=movie.c%02d",
+ VIDEODB_ID_RATING_ID, VIDEODB_ID_IDENT_ID);
m_pDS->exec(movieview);
}
@@ -1241,13 +1261,13 @@ int CVideoDatabase::GetMovieId(const std::string& strFilenameAndPath)
if (idFile == -1)
strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
else
- strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
+ strSQL = PrepareSQL("select idMedia from videoversion where idFile = %i", idFile);
CLog::Log(LOGDEBUG, LOGDATABASE, "{} ({}), query = {}", __FUNCTION__,
CURL::GetRedacted(strFilenameAndPath), strSQL);
m_pDS->query(strSQL);
if (m_pDS->num_rows() > 0)
- idMovie = m_pDS->fv("idMovie").get_asInt();
+ idMovie = m_pDS->fv(0).get_asInt();
m_pDS->close();
return idMovie;
@@ -1421,16 +1441,23 @@ int CVideoDatabase::AddNewMovie(CVideoInfoTag& details)
return -1;
}
- std::string strSQL =
- PrepareSQL("INSERT INTO movie (idMovie, idFile) VALUES (NULL, %i)", details.m_iFileId);
- m_pDS->exec(strSQL);
+ BeginTransaction();
+
+ m_pDS->exec(
+ PrepareSQL("INSERT INTO movie (idMovie, idFile) VALUES (NULL, %i)", details.m_iFileId));
details.m_iDbId = static_cast<int>(m_pDS->lastinsertid());
+ m_pDS->exec(PrepareSQL("INSERT INTO videoversion VALUES(%i, %i, 'movie', %i, '%i')",
+ details.m_iFileId, details.m_iDbId, VideoVersionItemType::PRIMARY,
+ VIDEO_VERSION_ID_DEFAULT));
+
+ CommitTransaction();
return details.m_iDbId;
}
catch (...)
{
CLog::Log(LOGERROR, "{} ({}) failed", __FUNCTION__, filePath);
+ RollbackTransaction();
}
return -1;
}
@@ -2129,6 +2156,19 @@ bool CVideoDatabase::GetMovieInfo(const std::string& strFilenameAndPath, CVideoI
return false;
}
+std::string CVideoDatabase::GetMovieTitle(int idMovie)
+{
+ if (!m_pDB || !m_pDS)
+ return "";
+
+ m_pDS->query(PrepareSQL("SELECT c%02d from movie where idMovie = %i", VIDEODB_ID_TITLE, idMovie));
+
+ if (!m_pDS->eof())
+ return m_pDS->fv(0).get_asString();
+ else
+ return "";
+}
+
//********************************************************************************************************************************
bool CVideoDatabase::GetTvShowInfo(const std::string& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */, CFileItem *item /* = NULL */, int getDetails /* = VideoDbDetailsAll */)
{
@@ -3815,6 +3855,156 @@ void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
}
+std::string CVideoDatabase::GetFileBasePathById(int idFile)
+{
+ if (!m_pDB || !m_pDS)
+ return "";
+
+ try
+ {
+ m_pDS->query(PrepareSQL(
+ "SELECT strPath FROM path JOIN files ON path.idPath = files.idPath WHERE idFile = %i",
+ idFile));
+
+ if (!m_pDS->eof())
+ {
+ return m_pDS->fv("strPath").get_asString();
+ }
+ m_pDS->close();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for file {}", __FUNCTION__, idFile);
+ }
+
+ return "";
+}
+
+std::string CVideoDatabase::GetFilenameAndPathById(int idFile)
+{
+ if (!m_pDB || !m_pDS2)
+ return "";
+
+ std::string strFilenameAndPath = "";
+
+ try
+ {
+ m_pDS2->query(PrepareSQL("SELECT strPath, strFilename FROM path JOIN files ON path.idPath = "
+ "files.idPath WHERE idFile = %i",
+ idFile));
+
+ if (!m_pDS2->eof())
+ {
+ ConstructPath(strFilenameAndPath, m_pDS2->fv("strPath").get_asString(),
+ m_pDS2->fv("strFilename").get_asString());
+ }
+ m_pDS2->close();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for file {}", __FUNCTION__, idFile);
+ }
+
+ return strFilenameAndPath;
+}
+
+int CVideoDatabase::GetFileIdByMovie(int idMovie)
+{
+ if (!m_pDB || !m_pDS)
+ return -1;
+
+ int idFile = -1;
+
+ try
+ {
+ m_pDS->query(PrepareSQL("SELECT idFile FROM movie WHERE idMovie = %i", idMovie));
+
+ if (!m_pDS->eof())
+ {
+ idFile = m_pDS->fv("idFile").get_asInt();
+ }
+
+ m_pDS->close();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for movie {}", __FUNCTION__, idMovie);
+ }
+
+ return idFile;
+}
+
+void CVideoDatabase::GetSameVideoItems(CFileItem& item, CFileItemList& items)
+{
+ if (!m_pDB || !m_pDS)
+ return;
+
+ // use two containers to keep the insertion order
+ std::list<int> itemIdList;
+ std::unordered_set<int> itemIdSet;
+
+ auto insertItemId = [&](int id)
+ {
+ if (itemIdSet.find(id) == itemIdSet.end())
+ {
+ itemIdList.push_back(id);
+ itemIdSet.insert(id);
+ }
+ };
+
+ int dbId = item.GetVideoInfoTag()->m_iDbId;
+ MediaType mediaType = item.GetVideoInfoTag()->m_type;
+
+ try
+ {
+ // get items with same unique ids
+ m_pDS->query(
+ PrepareSQL("SELECT media_id "
+ "FROM uniqueid "
+ "WHERE (value, type) IN "
+ " (SELECT value, type FROM uniqueid WHERE media_id = %i AND media_type = '%s')",
+ dbId, mediaType.c_str()));
+
+ while (!m_pDS->eof())
+ {
+ insertItemId(m_pDS->fv("media_id").get_asInt());
+ m_pDS->next();
+ }
+
+ m_pDS->close();
+
+ // get items with same title
+ std::string title = item.GetVideoInfoTag()->GetTitle();
+
+ VideoDbContentType itemType = item.GetVideoContentType();
+ if (itemType == VideoDbContentType::MOVIES)
+ {
+ m_pDS->query(PrepareSQL("SELECT idMovie FROM movie WHERE movie.c%02d = '%s'",
+ VIDEODB_ID_TITLE, title.c_str()));
+
+ while (!m_pDS->eof())
+ {
+ insertItemId(m_pDS->fv("idMovie").get_asInt());
+ m_pDS->next();
+ }
+
+ m_pDS->close();
+ }
+
+ // get video item details
+ for (const auto id : itemIdList)
+ {
+ auto item = std::make_shared<CFileItem>();
+ if (GetDetailsByTypeAndId(*item.get(), itemType, id))
+ items.Add(item);
+ }
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {} {}", __FUNCTION__, mediaType, dbId);
+ }
+}
+
void CVideoDatabase::DeleteTag(int idTag, VideoDbContentType mediaType)
{
try
@@ -4082,6 +4272,7 @@ CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* cons
details.m_iDbId = idMovie;
details.m_type = MediaTypeMovie;
+ details.m_hasVideoVersions = record->at(VIDEODB_DETAILS_MOVIE_HASVERSIONS).get_asBool();
details.m_set.id = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
details.m_set.title = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
details.m_set.overview = record->at(VIDEODB_DETAILS_MOVIE_SET_OVERVIEW).get_asString();
@@ -4108,7 +4299,8 @@ CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* cons
if (getDetails)
{
- GetCast(details.m_iDbId, MediaTypeMovie, details.m_cast);
+ if (getDetails & VideoDbDetailsCast)
+ GetCast(details.m_iDbId, MediaTypeMovie, details.m_cast);
if (getDetails & VideoDbDetailsTag)
GetTags(details.m_iDbId, MediaTypeMovie, details.m_tags);
@@ -5952,11 +6144,27 @@ void CVideoDatabase::UpdateTables(int iVersion)
}
m_pDS->close();
}
+
+ if (iVersion < 123)
+ {
+ // create videoversiontype table
+ m_pDS->exec("CREATE TABLE videoversiontype (id INTEGER PRIMARY KEY, name TEXT, owner INTEGER)");
+ InitializeVideoVersionTypeTable();
+
+ // create videoversion table
+ m_pDS->exec("CREATE TABLE videoversion (idFile INTEGER PRIMARY KEY, idMedia INTEGER, mediaType "
+ "TEXT, itemType INTEGER, idType INTEGER)");
+ m_pDS->exec(PrepareSQL(
+ "INSERT INTO videoversion SELECT idFile, idMovie, 'movie', '%i', '%i' FROM movie",
+ VideoVersionItemType::PRIMARY, VIDEO_VERSION_ID_DEFAULT));
+ }
+
+ // Version 124: add index to videoversion
}
int CVideoDatabase::GetSchemaVersion() const
{
- return 122;
+ return 124;
}
bool CVideoDatabase::LookupByFolders(const std::string &path, bool shows)
@@ -6225,7 +6433,7 @@ CDateTime CVideoDatabase::SetPlayCount(const CFileItem& item, int count, const C
if (item.GetVideoInfoTag()->GetPlayCount() != count)
data["playcount"] = count;
CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::VideoLibrary, "OnUpdate",
- CFileItemPtr(new CFileItem(item)), data);
+ std::make_shared<CFileItem>(item), data);
}
return lastPlayed;
@@ -6819,7 +7027,7 @@ bool CVideoDatabase::GetMusicVideoAlbumsNav(const std::string& strBaseDir, CFile
pItem->GetVideoInfoTag()->m_iDbId = idMVideo;
items.Add(pItem);
idMVideoList.push_back(idMVideo);
- idData.push_back(make_pair(m_pDS->fv(0).get_asString(), m_pDS->fv(5).get_asString()));
+ idData.emplace_back(m_pDS->fv(0).get_asString(), m_pDS->fv(5).get_asString());
}
m_pDS->next();
}
@@ -7646,6 +7854,8 @@ bool CVideoDatabase::GetItems(const std::string& strBaseDir,
return GetCountriesNav(strBaseDir, items, mediaType, filter);
else if (StringUtils::EqualsNoCase(itemType, "tags"))
return GetTagsNav(strBaseDir, items, mediaType, filter);
+ else if (StringUtils::EqualsNoCase(itemType, "videoversions"))
+ return GetVideoVersionsNav(strBaseDir, items, mediaType, filter);
else if (StringUtils::EqualsNoCase(itemType, "artists") &&
mediaType == VideoDbContentType::MUSICVIDEOS)
return GetActorsNav(strBaseDir, items, mediaType, filter);
@@ -7674,6 +7884,8 @@ std::string CVideoDatabase::GetItemById(const std::string &itemType, int id)
return GetCountryById(id);
else if (StringUtils::EqualsNoCase(itemType, "tags"))
return GetTagById(id);
+ else if (StringUtils::EqualsNoCase(itemType, "videoversions"))
+ return GetVideoVersionById(id);
else if (StringUtils::EqualsNoCase(itemType, "albums"))
return GetMusicVideoAlbumById(id);
@@ -7726,6 +7938,9 @@ bool CVideoDatabase::GetMoviesByWhere(const std::string& strBaseDir, const Filte
if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
return false;
+ if (videoUrl.HasOption("videoversionid") && videoUrl.HasOption("mediaid"))
+ return GetVideoVersionsNav(strBaseDir, items, VideoDbContentType::MOVIES, extFilter);
+
int total = -1;
std::string strSQL = "select %s from movie_view ";
@@ -7779,6 +7994,21 @@ bool CVideoDatabase::GetMoviesByWhere(const std::string& strBaseDir, const Filte
std::string path = std::to_string(movie.m_iDbId);
itemUrl.AppendPath(path);
pItem->SetPath(itemUrl.ToString());
+
+ CVariant value;
+ if (itemUrl.GetOption("videoversionid", value))
+ {
+ const int idVideoVersion = value.asInteger();
+ if (idVideoVersion > 0)
+ {
+ pItem->GetVideoInfoTag()->m_hasVideoVersions = false;
+ pItem->GetVideoInfoTag()->m_idVideoVersion = idVideoVersion;
+ pItem->GetVideoInfoTag()->m_typeVideoVersion = GetVideoVersionById(idVideoVersion);
+ pItem->GetVideoInfoTag()->SetFileNameAndPath(GetFilenameAndPathById(
+ GetVideoVersionFile(VideoDbContentType::MOVIES, movie.m_iDbId, idVideoVersion)));
+ }
+ }
+
pItem->SetDynPath(movie.m_strFileNameAndPath);
pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.GetPlayCount() > 0);
@@ -8115,6 +8345,11 @@ std::string CVideoDatabase::GetSetById(int id)
return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
}
+std::string CVideoDatabase::GetSetByNameLike(const std::string& nameLike) const
+{
+ return GetSingleValue("sets", "strSet", PrepareSQL("strSet LIKE '%s'", nameLike.c_str()));
+}
+
std::string CVideoDatabase::GetTagById(int id)
{
return GetSingleValue("tag", "name", PrepareSQL("tag_id = %i", id));
@@ -8925,6 +9160,7 @@ bool CVideoDatabase::GetMusicVideosByWhere(const std::string &baseDir, const Fil
std::string path = std::to_string(record->at(0).get_asInt());
itemUrl.AppendPath(path);
item->SetPath(itemUrl.ToString());
+ item->SetDynPath(musicvideo.m_strFileNameAndPath);
item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.GetPlayCount() > 0);
items.Add(item);
@@ -9605,6 +9841,7 @@ void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle,
std::vector<int> tvshowIDs;
std::vector<int> episodeIDs;
std::vector<int> musicVideoIDs;
+ std::vector<int> videoVersionIDs;
if (!filesToTestForDelete.empty())
{
@@ -9616,6 +9853,8 @@ void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle,
filesToDelete, !showProgress);
musicVideoIDs = CleanMediaType(MediaTypeMusicVideo, filesToTestForDelete,
pathsDeleteDecisions, filesToDelete, !showProgress);
+ videoVersionIDs = CleanMediaType(MediaTypeVideoVersion, filesToTestForDelete,
+ pathsDeleteDecisions, filesToDelete, !showProgress);
}
if (progress != NULL)
@@ -9831,6 +10070,9 @@ void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle,
for (const auto& i : musicVideoIDs)
AnnounceRemove(MediaTypeMusicVideo, i, true);
+
+ for (const auto& i : videoVersionIDs)
+ AnnounceRemove(MediaTypeVideoVersion, i, true);
}
}
catch (...)
@@ -9871,6 +10113,11 @@ std::vector<int> CVideoDatabase::CleanMediaType(const std::string &mediaType, co
idField = "idMVideo";
parentPathIdField = StringUtils::Format("{}.c{:02}", table, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
}
+ else if (mediaType == MediaTypeVideoVersion)
+ {
+ idField = "idMedia";
+ parentPathIdField = "path.idPath";
+ }
else
return cleanedIDs;
@@ -10153,7 +10400,9 @@ void CVideoDatabase::ExportToXML(const std::string &path, bool singleFile /* = t
if(!xmlDoc.SaveFile(nfoFile))
{
CLog::Log(LOGERROR, "{}: Movie nfo export failed! ('{}')", __FUNCTION__, nfoFile);
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
+ g_localizeStrings.Get(20302),
+ CURL::GetRedacted(nfoFile));
iFailCount++;
}
}
@@ -10298,7 +10547,9 @@ void CVideoDatabase::ExportToXML(const std::string &path, bool singleFile /* = t
{
CLog::Log(LOGERROR, "{}: Musicvideo nfo export failed! ('{}')", __FUNCTION__,
nfoFile);
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
+ g_localizeStrings.Get(20302),
+ CURL::GetRedacted(nfoFile));
iFailCount++;
}
}
@@ -10398,7 +10649,9 @@ void CVideoDatabase::ExportToXML(const std::string &path, bool singleFile /* = t
if(!xmlDoc.SaveFile(nfoFile))
{
CLog::Log(LOGERROR, "{}: TVShow nfo export failed! ('{}')", __FUNCTION__, nfoFile);
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
+ g_localizeStrings.Get(20302),
+ CURL::GetRedacted(nfoFile));
iFailCount++;
}
}
@@ -10494,7 +10747,9 @@ void CVideoDatabase::ExportToXML(const std::string &path, bool singleFile /* = t
if(!xmlDoc.SaveFile(nfoFile))
{
CLog::Log(LOGERROR, "{}: Episode nfo export failed! ('{}')", __FUNCTION__, nfoFile);
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(20302), nfoFile);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
+ g_localizeStrings.Get(20302),
+ CURL::GetRedacted(nfoFile));
iFailCount++;
}
}
@@ -11104,6 +11359,16 @@ bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription
if (option != options.end())
filter.AppendWhere(PrepareSQL("movie_view.strSet LIKE '%s'", option->second.asString().c_str()));
+ option = options.find("videoversionid");
+ if (option != options.end())
+ {
+ const int idVideoVersion = option->second.asInteger();
+ if (idVideoVersion > 0)
+ filter.AppendWhere(PrepareSQL("idMovie IN (SELECT idMedia FROM videoversion WHERE "
+ "mediaType = 'movie' AND idType = %i)",
+ idVideoVersion));
+ }
+
AppendIdLinkFilter("tag", "tag", "movie", "movie", "idMovie", options, filter);
AppendLinkFilter("tag", "tag", "movie", "movie", "idMovie", options, filter);
}
@@ -11399,3 +11664,759 @@ void CVideoDatabase::EraseAllForPath(const std::string& path)
CLog::Log(LOGERROR, "{} failed", __FUNCTION__);
}
}
+
+std::string CVideoDatabase::GetVideoItemTitle(VideoDbContentType itemType, int dbId)
+{
+ switch (itemType)
+ {
+ case VideoDbContentType::MOVIES:
+ return GetMovieTitle(dbId);
+ default:
+ return "";
+ }
+}
+
+void CVideoDatabase::InitializeVideoVersionTypeTable()
+{
+ try
+ {
+ BeginTransaction();
+
+ for (int id = VIDEO_VERSION_ID_BEGIN; id <= VIDEO_VERSION_ID_END; ++id)
+ {
+ std::string type = g_localizeStrings.Get(id);
+ m_pDS->exec(PrepareSQL("INSERT INTO videoversiontype VALUES(%i, '%s', %i)", id, type.c_str(),
+ VideoVersionTypeOwner::SYSTEM));
+ }
+
+ CommitTransaction();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed", __FUNCTION__);
+ RollbackTransaction();
+ }
+}
+
+void CVideoDatabase::UpdateVideoVersionTypeTable()
+{
+ try
+ {
+ BeginTransaction();
+
+ for (int id = VIDEO_VERSION_ID_BEGIN; id <= VIDEO_VERSION_ID_END; ++id)
+ {
+ std::string type = g_localizeStrings.Get(id);
+ m_pDS->exec(PrepareSQL("UPDATE videoversiontype SET name = '%s', owner = %i WHERE id = '%i'",
+ type.c_str(), VideoVersionTypeOwner::SYSTEM, id));
+ }
+
+ CommitTransaction();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed", __FUNCTION__);
+ RollbackTransaction();
+ }
+}
+
+int CVideoDatabase::AddVideoVersionType(const std::string& typeVideoVersion,
+ VideoVersionTypeOwner owner)
+{
+ if (typeVideoVersion.empty())
+ return -1;
+
+ int id = -1;
+
+ try
+ {
+ if (!m_pDB || !m_pDS)
+ return -1;
+
+ m_pDS->query(PrepareSQL("SELECT id, owner FROM videoversiontype WHERE name = '%s'",
+ typeVideoVersion.c_str()));
+ if (m_pDS->num_rows() == 0)
+ {
+ m_pDS->exec(
+ PrepareSQL("INSERT INTO videoversiontype (id, name, owner) VALUES(NULL, '%s', %i)",
+ typeVideoVersion.c_str(), owner));
+ id = m_pDS->lastinsertid();
+ }
+ else
+ {
+ id = m_pDS->fv("id").get_asInt();
+
+ // if user is adding an existing version type, overwrite the existing non-system one
+ VideoVersionTypeOwner oldOwner =
+ static_cast<VideoVersionTypeOwner>(m_pDS->fv("owner").get_asInt());
+ if (oldOwner != VideoVersionTypeOwner::SYSTEM && owner == VideoVersionTypeOwner::USER)
+ {
+ m_pDS->exec(PrepareSQL("UPDATE videoversiontype SET owner = %i WHERE id = %i", owner, id));
+ }
+ }
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed to add video version {}", __FUNCTION__, typeVideoVersion);
+ }
+
+ return id;
+}
+
+bool CVideoDatabase::IsVideoExtras(int dbId)
+{
+ if (!m_pDB || !m_pDS)
+ return false;
+
+ try
+ {
+ return static_cast<VideoVersionItemType>(GetSingleValueInt(
+ PrepareSQL("SELECT itemType FROM videoversion WHERE idFile = %i", dbId))) ==
+ VideoVersionItemType::EXTRAS;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {} {}", __FUNCTION__, dbId);
+ }
+
+ return false;
+}
+
+void CVideoDatabase::GetVideoVersions(VideoDbContentType itemType, int dbId, CFileItemList& items)
+{
+ // get main video versions
+ CFileItemList mainList;
+ GetVideoVersions(itemType, dbId, mainList, VideoVersionItemType::PRIMARY);
+ items.Append(mainList);
+
+ // get video extras versions
+ CFileItemList extrasList;
+ GetVideoVersions(itemType, dbId, extrasList, VideoVersionItemType::EXTRAS);
+ items.Append(extrasList);
+}
+
+void CVideoDatabase::GetVideoVersions(VideoDbContentType itemType,
+ int dbId,
+ CFileItemList& items,
+ VideoVersionItemType versionItemType)
+{
+ if (!m_pDB || !m_pDS2)
+ return;
+
+ MediaType mediaType;
+
+ if (itemType == VideoDbContentType::MOVIES)
+ mediaType = MediaTypeMovie;
+ else
+ return;
+
+ try
+ {
+ m_pDS2->query(PrepareSQL("SELECT videoversiontype.name AS name,"
+ " videoversiontype.id AS id,"
+ " videoversion.idFile AS idFile "
+ "FROM videoversiontype"
+ " JOIN videoversion ON"
+ " videoversion.idType = videoversiontype.id "
+ "WHERE videoversion.idMedia = %i AND videoversion.mediaType = '%s' "
+ "AND videoversion.itemType = %i",
+ dbId, mediaType.c_str(), versionItemType));
+
+ std::vector<std::tuple<std::string, int, int>> versions;
+
+ while (!m_pDS2->eof())
+ {
+ versions.push_back(std::make_tuple(m_pDS2->fv("name").get_asString(),
+ m_pDS2->fv("id").get_asInt(),
+ m_pDS2->fv("idFile").get_asInt()));
+ m_pDS2->next();
+ }
+ m_pDS2->close();
+
+ CFileItem videoItem;
+ GetDetailsByTypeAndId(videoItem, itemType, dbId);
+
+ for (auto& version : versions)
+ {
+ std::string name = std::get<0>(version);
+ int id = std::get<1>(version);
+ int idFile = std::get<2>(version);
+
+ CVideoInfoTag infoTag;
+ if (GetFileInfo("", infoTag, idFile))
+ {
+ infoTag.m_type = MediaTypeVideoVersion;
+ infoTag.m_iDbId = idFile;
+ infoTag.m_idVideoVersion = id;
+ infoTag.m_typeVideoVersion = name;
+ infoTag.m_strTitle = name;
+
+ infoTag.m_strPictureURL = videoItem.GetVideoInfoTag()->m_strPictureURL;
+ infoTag.m_fanart = videoItem.GetVideoInfoTag()->m_fanart;
+
+ auto item(std::make_shared<CFileItem>(infoTag));
+ item->m_strTitle = name;
+ item->SetLabel(name);
+
+ CVideoDbUrl itemUrl;
+ if (itemUrl.FromString(StringUtils::Format("videodb://{}/videoversions/{}",
+ CMediaTypes::ToPlural(mediaType), id)))
+ {
+ itemUrl.AddOption("mediaid", dbId);
+ item->SetPath(itemUrl.ToString());
+ }
+
+ item->SetDynPath(infoTag.m_strFileNameAndPath);
+
+ item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, GetPlayCount(idFile) > 0);
+
+ items.Add(item);
+ }
+ }
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {} {}", __FUNCTION__, mediaType, dbId);
+ }
+}
+
+void CVideoDatabase::GetDefaultVideoVersion(VideoDbContentType itemType, int dbId, CFileItem& item)
+{
+ if (!m_pDB || !m_pDS)
+ return;
+
+ MediaType mediaType;
+ std::string strSQL;
+
+ if (itemType == VideoDbContentType::MOVIES)
+ {
+ mediaType = MediaTypeMovie;
+ strSQL = PrepareSQL("SELECT videoversiontype.name AS name,"
+ " videoversiontype.id AS id,"
+ " videoversion.idFile AS idFile "
+ "FROM videoversiontype"
+ " JOIN videoversion ON"
+ " videoversion.idType = videoversiontype.id"
+ " JOIN movie ON"
+ " movie.idFile = videoversion.idFile "
+ "WHERE movie.idMovie = %i",
+ dbId);
+ }
+ else
+ return;
+
+ try
+ {
+ m_pDS->query(strSQL);
+
+ if (!m_pDS->eof())
+ {
+ std::string name = m_pDS->fv("name").get_asString();
+ int id = m_pDS->fv("id").get_asInt();
+ int idFile = m_pDS->fv("idFile").get_asInt();
+
+ CVideoInfoTag infoTag;
+ if (GetFileInfo("", infoTag, idFile))
+ {
+ infoTag.m_type = MediaTypeVideoVersion;
+ infoTag.m_iDbId = idFile;
+ infoTag.m_idVideoVersion = id;
+ infoTag.m_typeVideoVersion = name;
+ infoTag.m_strTitle = name;
+
+ item.SetFromVideoInfoTag(infoTag);
+ item.m_strTitle = name;
+ item.SetLabel(name);
+ }
+ }
+ m_pDS->close();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {} {}", __FUNCTION__, mediaType, dbId);
+ }
+}
+
+void CVideoDatabase::ConvertVideoToVersion(VideoDbContentType itemType,
+ int dbIdSource,
+ int dbIdTarget,
+ int idVideoVersion)
+{
+ int idFile = -1;
+
+ if (itemType == VideoDbContentType::MOVIES)
+ {
+ idFile = GetFileIdByMovie(dbIdSource);
+ }
+ else
+ return;
+
+ if (idFile < 0)
+ return;
+
+ BeginTransaction();
+
+ if (dbIdSource != dbIdTarget)
+ {
+ ExecuteQuery(PrepareSQL("UPDATE videoversion SET idMedia = %i, idType = %i WHERE idFile = %i",
+ dbIdTarget, idVideoVersion, idFile));
+
+ SetVideoVersionDefaultArt(idFile, dbIdSource, itemType);
+
+ DeleteMovie(dbIdSource);
+ }
+ else
+ ExecuteQuery(PrepareSQL("UPDATE videoversion SET idType = %i WHERE idFile = %i", idVideoVersion,
+ idFile));
+
+ CommitTransaction();
+}
+
+void CVideoDatabase::SetDefaultVideoVersion(VideoDbContentType itemType, int dbId, int idFile)
+{
+ if (!m_pDB || !m_pDS)
+ return;
+
+ std::string path = GetFileBasePathById(idFile);
+ if (path.empty())
+ return;
+
+ try
+ {
+ if (itemType == VideoDbContentType::MOVIES)
+ m_pDS->exec(PrepareSQL("UPDATE movie SET idFile = %i, c%02d = '%s' WHERE idMovie = %i",
+ idFile, VIDEODB_ID_BASEPATH, path.c_str(), dbId));
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for video {}", __FUNCTION__, dbId);
+ }
+}
+
+bool CVideoDatabase::IsDefaultVideoVersion(int idFile)
+{
+ if (!m_pDB || !m_pDS)
+ return false;
+
+ try
+ {
+ m_pDS->query(
+ PrepareSQL("SELECT idMedia, mediaType FROM videoversion WHERE idFile = %i", idFile));
+ if (m_pDS->num_rows() > 0)
+ {
+ int idMedia = m_pDS->fv("idMedia").get_asInt();
+ std::string mediaType = m_pDS->fv("mediaType").get_asString();
+
+ if (mediaType == MediaTypeMovie)
+ {
+ m_pDS->query(PrepareSQL("SELECT idFile FROM movie WHERE idMovie = %i", idMedia));
+ if (m_pDS->num_rows() > 0)
+ {
+ if (m_pDS->fv("idFile").get_asInt() == idFile)
+ return true;
+ }
+ }
+ }
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {}", __FUNCTION__, idFile);
+ }
+
+ return false;
+}
+
+void CVideoDatabase::RemoveVideoVersion(int dbId)
+{
+ if (!m_pDB || !m_pDS)
+ return;
+
+ if (IsDefaultVideoVersion(dbId))
+ return;
+
+ try
+ {
+ std::string path = GetSingleValue(PrepareSQL(
+ "SELECT strPath FROM path JOIN files ON files.idPath=path.idPath WHERE files.idFile=%i",
+ dbId));
+ if (!path.empty())
+ InvalidatePathHash(path);
+
+ m_pDS->exec(PrepareSQL("DELETE FROM videoversion WHERE idFile = %i", dbId));
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {}", __FUNCTION__, dbId);
+ }
+}
+
+void CVideoDatabase::SetVideoVersion(int idFile, int idVideoVersion)
+{
+ if (!m_pDB || !m_pDS)
+ return;
+
+ try
+ {
+ m_pDS->exec(PrepareSQL("UPDATE videoversion SET idType = %i WHERE idFile = %i", idVideoVersion,
+ idFile));
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for video {}", __FUNCTION__, idFile);
+ }
+}
+
+void CVideoDatabase::AddPrimaryVideoVersion(VideoDbContentType itemType,
+ int dbId,
+ int idVideoVersion,
+ CFileItem& item)
+{
+ AddVideoVersion(itemType, dbId, idVideoVersion, VideoVersionItemType::PRIMARY, item);
+}
+
+void CVideoDatabase::AddExtrasVideoVersion(VideoDbContentType itemType,
+ int dbId,
+ int idVideoVersion,
+ CFileItem& item)
+{
+ AddVideoVersion(itemType, dbId, idVideoVersion, VideoVersionItemType::EXTRAS, item);
+}
+
+void CVideoDatabase::AddVideoVersion(VideoDbContentType itemType,
+ int dbId,
+ int idVideoVersion,
+ VideoVersionItemType versionItemType,
+ CFileItem& item)
+{
+ if (!m_pDB || !m_pDS)
+ return;
+
+ MediaType mediaType;
+ if (itemType == VideoDbContentType::MOVIES)
+ {
+ mediaType = MediaTypeMovie;
+ }
+ else
+ return;
+
+ int idFile = AddFile(item.GetPath());
+ if (idFile < 0)
+ return;
+
+ try
+ {
+ m_pDS->query(PrepareSQL("SELECT idFile FROM videoversion WHERE idFile = %i", idFile));
+
+ if (m_pDS->num_rows() == 0)
+ m_pDS->exec(PrepareSQL("INSERT INTO videoversion VALUES(%i, %i, '%s', %i, %i)", idFile, dbId,
+ mediaType.c_str(), versionItemType, idVideoVersion));
+ else
+ m_pDS->exec(PrepareSQL("UPDATE videoversion SET idMedia = %i, mediaType = '%s', itemType = "
+ "%i, idType = %i WHERE idFile = %i",
+ dbId, mediaType.c_str(), versionItemType, idVideoVersion, idFile));
+
+ if (item.GetVideoInfoTag()->HasStreamDetails())
+ SetStreamDetailsForFileId(item.GetVideoInfoTag()->m_streamDetails, idFile);
+
+ if (versionItemType == VideoVersionItemType::PRIMARY)
+ SetVideoVersionDefaultArt(idFile, item.GetVideoInfoTag()->m_iDbId, itemType);
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for video {}", __FUNCTION__, dbId);
+ }
+}
+
+int CVideoDatabase::GetVideoVersionFile(VideoDbContentType itemType, int dbId, int idVideoVersion)
+{
+ if (!m_pDB || !m_pDS2)
+ return -1;
+
+ MediaType mediaType;
+
+ if (itemType == VideoDbContentType::MOVIES)
+ mediaType = MediaTypeMovie;
+ else
+ return -1;
+
+ int idFile = -1;
+
+ try
+ {
+ m_pDS2->query(PrepareSQL(
+ "SELECT idFile FROM videoversion WHERE idMedia = %i AND mediaType = '%s' and idType = %i",
+ dbId, mediaType.c_str(), idVideoVersion));
+
+ if (!m_pDS2->eof())
+ {
+ idFile = m_pDS2->fv("idFile").get_asInt();
+ }
+
+ m_pDS2->close();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {} {}", __FUNCTION__, mediaType, dbId);
+ }
+
+ return idFile;
+}
+
+int CVideoDatabase::GetVideoVersionInfo(const std::string& fileNameAndPath,
+ int& idFile,
+ std::string& typeVideoVersion,
+ int& idMedia,
+ MediaType& mediaType,
+ VideoVersionItemType& versionItemType)
+{
+ idFile = GetFileId(fileNameAndPath);
+ if (idFile < 0)
+ return -1;
+
+ return GetVideoVersionInfo(idFile, typeVideoVersion, idMedia, mediaType, versionItemType);
+}
+
+int CVideoDatabase::GetVideoVersionInfo(int idFile,
+ std::string& typeVideoVersion,
+ int& idMedia,
+ MediaType& mediaType,
+ VideoVersionItemType& versionItemType)
+{
+ if (!m_pDB || !m_pDS)
+ return -1;
+
+ int idVideoVersion = -1;
+
+ try
+ {
+ m_pDS->query(PrepareSQL("SELECT videoversiontype.name AS name,"
+ " videoversiontype.id AS id,"
+ " videoversion.idMedia AS idMedia,"
+ " videoversion.mediaType AS mediaType,"
+ " videoversion.itemType AS itemType "
+ "FROM videoversion"
+ " JOIN videoversiontype ON "
+ " videoversiontype.id = videoversion.idType "
+ "WHERE videoversion.idFile = %i",
+ idFile));
+
+ if (m_pDS->num_rows() > 0)
+ {
+ idVideoVersion = m_pDS->fv("id").get_asInt();
+ typeVideoVersion = m_pDS->fv("name").get_asString();
+ idMedia = m_pDS->fv("idMedia").get_asInt();
+ mediaType = m_pDS->fv("mediaType").get_asString();
+ versionItemType = static_cast<VideoVersionItemType>(m_pDS->fv("itemType").get_asInt());
+ }
+
+ m_pDS->close();
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for {}", __FUNCTION__, idFile);
+ }
+
+ return idVideoVersion;
+}
+
+bool CVideoDatabase::GetVideoVersionsNav(const std::string& strBaseDir,
+ CFileItemList& items,
+ VideoDbContentType idContent /* = UNKNOWN */,
+ const Filter& filter /* = Filter() */)
+{
+ if (!m_pDB || !m_pDS)
+ return false;
+
+ MediaType mediaType;
+
+ if (idContent == VideoDbContentType::MOVIES)
+ {
+ mediaType = MediaTypeMovie;
+ }
+ else
+ return false;
+
+ CVideoDbUrl videoUrl;
+ if (!videoUrl.FromString(strBaseDir))
+ return false;
+
+ int idVideoVersion = -1;
+ CVariant value;
+ if (videoUrl.GetOption("videoversionid", value))
+ idVideoVersion = value.asInteger(-1);
+
+ int idMedia = -1;
+ if (videoUrl.GetOption("mediaid", value))
+ idMedia = value.asInteger(-1);
+
+ if (idVideoVersion == VIDEO_VERSION_ID_ALL)
+ {
+ if (idMedia != -1)
+ {
+ CFileItemList list;
+ GetVideoVersions(idContent, idMedia, list);
+
+ items.Clear();
+ items.Append(list);
+
+ return true;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "{} mediaid is missing in {}", __FUNCTION__, strBaseDir);
+ return false;
+ }
+ }
+
+ try
+ {
+ std::string strSQL;
+
+ if (idMedia != -1)
+ strSQL = PrepareSQL("SELECT videoversiontype.name AS name,"
+ " videoversiontype.id AS id "
+ "FROM videoversiontype"
+ " JOIN videoversion ON"
+ " videoversion.idType = videoversiontype.id "
+ "WHERE idMedia = %i AND mediaType = '%s'",
+ idMedia, mediaType.c_str());
+ else
+ strSQL = PrepareSQL("SELECT DISTINCT videoversiontype.name AS name,"
+ " videoversiontype.id AS id "
+ "FROM videoversiontype"
+ " JOIN videoversion ON"
+ " videoversion.idType = videoversiontype.id "
+ "WHERE name != '' AND owner IN (%i, %i)",
+ VideoVersionTypeOwner::SYSTEM, VideoVersionTypeOwner::USER);
+
+ m_pDS->query(strSQL);
+
+ while (!m_pDS->eof())
+ {
+ std::string name = m_pDS->fv("name").get_asString();
+ int id = m_pDS->fv("id").get_asInt();
+
+ auto item(std::make_shared<CFileItem>(name));
+ item->GetVideoInfoTag()->m_type = MediaTypeVideoVersion;
+ item->GetVideoInfoTag()->m_iDbId = id;
+
+ CVideoDbUrl itemUrl = videoUrl;
+ std::string path = StringUtils::Format("{}/", m_pDS->fv("id").get_asInt());
+ itemUrl.AppendPath(path);
+ item->SetPath(itemUrl.ToString());
+
+ item->m_bIsFolder = true;
+
+ items.Add(item);
+ m_pDS->next();
+ }
+ m_pDS->close();
+ return true;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed", __FUNCTION__);
+ }
+ return false;
+}
+
+bool CVideoDatabase::GetVideoVersionTypes(VideoDbContentType idContent, CFileItemList& items)
+{
+ if (!m_pDB || !m_pDS)
+ return false;
+
+ MediaType mediaType;
+
+ if (idContent == VideoDbContentType::MOVIES)
+ {
+ mediaType = MediaTypeMovie;
+ }
+ else
+ return false;
+
+ try
+ {
+ m_pDS->query(
+ PrepareSQL("SELECT name, id FROM videoversiontype WHERE name != '' and owner IN (%i, %i)",
+ VideoVersionTypeOwner::SYSTEM, VideoVersionTypeOwner::USER));
+
+ while (!m_pDS->eof())
+ {
+ std::string name = m_pDS->fv("name").get_asString();
+ int id = m_pDS->fv("id").get_asInt();
+
+ const auto item{std::make_shared<CFileItem>(name)};
+ item->GetVideoInfoTag()->m_type = MediaTypeVideoVersion;
+ item->GetVideoInfoTag()->m_iDbId = id;
+ item->GetVideoInfoTag()->m_idVideoVersion = id;
+ item->GetVideoInfoTag()->m_typeVideoVersion = name;
+ item->GetVideoInfoTag()->m_strTitle = name;
+
+ item->m_strTitle = name;
+ item->SetLabel(name);
+
+ items.Add(item);
+ m_pDS->next();
+ }
+ m_pDS->close();
+ return true;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed", __FUNCTION__);
+ }
+ return false;
+}
+
+std::string CVideoDatabase::GetVideoVersionById(int id)
+{
+ return GetSingleValue(PrepareSQL("SELECT name FROM videoversiontype WHERE id=%i", id), m_pDS2);
+}
+
+bool CVideoDatabase::GetVideoItemByVideoVersion(int dbId, CFileItem& item)
+{
+ if (!m_pDB || !m_pDS)
+ return false;
+
+ try
+ {
+ m_pDS->query(PrepareSQL("SELECT idMedia, mediaType FROM videoversion WHERE idFile = %i", dbId));
+
+ if (m_pDS->num_rows() > 0)
+ {
+ int idMedia = m_pDS->fv("idMedia").get_asInt();
+ std::string mediaType = m_pDS->fv("mediaType").get_asString();
+
+ m_pDS->close();
+
+ if (mediaType == MediaTypeMovie)
+ {
+ CVideoInfoTag videoInfo;
+ if (GetMovieInfo("", videoInfo, idMedia))
+ {
+ item.SetFromVideoInfoTag(videoInfo);
+ return true;
+ }
+ }
+ }
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "{} failed for video version {}", __FUNCTION__, dbId);
+ }
+
+ return false;
+}
+
+void CVideoDatabase::SetVideoVersionDefaultArt(int dbId, int idFrom, VideoDbContentType type)
+{
+ MediaType mediaType;
+ VideoContentTypeToString(type, mediaType);
+
+ std::map<std::string, std::string> art;
+ if (GetArtForItem(idFrom, mediaType, art))
+ {
+ for (const auto& it : art)
+ SetArtForItem(dbId, MediaTypeVideoVersion, it.first, it.second);
+ }
+}
diff --git a/xbmc/video/VideoDatabase.h b/xbmc/video/VideoDatabase.h
index c946edfa5a..a498dc7760 100644
--- a/xbmc/video/VideoDatabase.h
+++ b/xbmc/video/VideoDatabase.h
@@ -93,6 +93,7 @@ enum VideoDbDetails
#define VIDEODB_DETAILS_MOVIE_RATING_TYPE VIDEODB_MAX_COLUMNS + 17
#define VIDEODB_DETAILS_MOVIE_UNIQUEID_VALUE VIDEODB_MAX_COLUMNS + 18
#define VIDEODB_DETAILS_MOVIE_UNIQUEID_TYPE VIDEODB_MAX_COLUMNS + 19
+#define VIDEODB_DETAILS_MOVIE_HASVERSIONS VIDEODB_MAX_COLUMNS + 20
#define VIDEODB_DETAILS_EPISODE_TVSHOW_ID VIDEODB_MAX_COLUMNS + 2
#define VIDEODB_DETAILS_EPISODE_USER_RATING VIDEODB_MAX_COLUMNS + 3
@@ -392,6 +393,27 @@ const struct SDbTableOffsets DbMusicVideoOffsets[] =
#define COMPARE_PERCENTAGE 0.90f // 90%
#define COMPARE_PERCENTAGE_MIN 0.50f // 50%
+// Video Versions
+enum class VideoVersionTypeOwner
+{
+ UNKNOWN = -1,
+ SYSTEM = 0,
+ AUTO = 1,
+ USER = 2
+};
+
+enum class VideoVersionItemType
+{
+ UNKNOWN = -1,
+ PRIMARY = 0,
+ EXTRAS = 1
+};
+
+static constexpr int VIDEO_VERSION_ID_BEGIN = 40400;
+static constexpr int VIDEO_VERSION_ID_END = 40800;
+static constexpr int VIDEO_VERSION_ID_DEFAULT = VIDEO_VERSION_ID_BEGIN;
+static constexpr int VIDEO_VERSION_ID_ALL = 0;
+
class CVideoDatabase : public CDatabase
{
public:
@@ -806,6 +828,7 @@ public:
VideoDbContentType idContent = VideoDbContentType::UNKNOWN,
const Filter& filter = Filter(),
bool countOnly = false);
+
bool GetMusicVideoAlbumsNav(const std::string& strBaseDir, CFileItemList& items, int idArtist, const Filter &filter = Filter(), bool countOnly = false);
bool GetMoviesNav(const std::string& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idStudio=-1, int idCountry=-1, int idSet=-1, int idTag=-1, const SortDescription &sortDescription = SortDescription(), int getDetails = VideoDbDetailsNone);
@@ -985,11 +1008,72 @@ public:
bool SetVideoUserRating(int dbId, int rating, const MediaType& mediaType);
bool GetUseAllExternalAudioForVideo(const std::string& videoPath);
+ std::string GetSetByNameLike(const std::string& nameLike) const;
+
+ std::string GetVideoItemTitle(VideoDbContentType itemType, int dbId);
+ std::string GetVideoVersionById(int id);
+ bool GetVideoItemByVideoVersion(int dbId, CFileItem& item);
+ int GetVideoVersionFile(VideoDbContentType itemType, int dbId, int idVideoVersion);
+ bool IsVideoExtras(int dbId);
+ void GetVideoVersions(VideoDbContentType itemType, int dbId, CFileItemList& items);
+ void GetVideoVersions(VideoDbContentType itemType,
+ int dbId,
+ CFileItemList& items,
+ VideoVersionItemType versionItemType);
+ void GetDefaultVideoVersion(VideoDbContentType itemType, int dbId, CFileItem& item);
+ void ConvertVideoToVersion(VideoDbContentType itemType,
+ int dbIdSource,
+ int dbIdTarget,
+ int idVideoVersion);
+ void SetDefaultVideoVersion(VideoDbContentType itemType, int dbId, int idFile);
+ void SetVideoVersion(int idFile, int idVideoVersion);
+ int AddVideoVersionType(const std::string& typeVideoVersion, VideoVersionTypeOwner owner);
+ void AddVideoVersion(VideoDbContentType itemType,
+ int dbId,
+ int idVideoVersion,
+ VideoVersionItemType versionItemType,
+ CFileItem& item);
+ void AddPrimaryVideoVersion(VideoDbContentType itemType,
+ int dbId,
+ int idVideoVersion,
+ CFileItem& item);
+ void AddExtrasVideoVersion(VideoDbContentType itemType,
+ int dbId,
+ int idVideoVersion,
+ CFileItem& item);
+ void RemoveVideoVersion(int dbId);
+ bool IsDefaultVideoVersion(int idFile);
+ bool GetVideoVersionTypes(VideoDbContentType idContent, CFileItemList& items);
+ void SetVideoVersionDefaultArt(int dbId, int idFrom, VideoDbContentType type);
+ void InitializeVideoVersionTypeTable();
+ void UpdateVideoVersionTypeTable();
+ bool GetVideoVersionsNav(const std::string& strBaseDir,
+ CFileItemList& items,
+ VideoDbContentType idContent = VideoDbContentType::UNKNOWN,
+ const Filter& filter = Filter());
+ int GetVideoVersionInfo(const std::string& strFilenameAndPath,
+ int& idFile,
+ std::string& typeVideoVersion,
+ int& idMedia,
+ MediaType& mediaType,
+ VideoVersionItemType& versionItemType);
+ int GetVideoVersionInfo(int idFile,
+ std::string& typeVideoVersion,
+ int& idMedia,
+ MediaType& mediaType,
+ VideoVersionItemType& versionItemType);
+
+ int GetMovieId(const std::string& strFilenameAndPath);
+ std::string GetMovieTitle(int idMovie);
+ void GetSameVideoItems(CFileItem& item, CFileItemList& items);
+ int GetFileIdByMovie(int idMovie);
+ std::string GetFileBasePathById(int idFile);
+ std::string GetFilenameAndPathById(int idFile);
+
protected:
int AddNewMovie(CVideoInfoTag& details);
int AddNewMusicVideo(CVideoInfoTag& details);
- int GetMovieId(const std::string& strFilenameAndPath);
int GetMusicVideoId(const std::string& strFilenameAndPath);
/*! \brief Get the id of this fileitem
diff --git a/xbmc/video/VideoDbUrl.cpp b/xbmc/video/VideoDbUrl.cpp
index 0bb899c3c2..45b8634da0 100644
--- a/xbmc/video/VideoDbUrl.cpp
+++ b/xbmc/video/VideoDbUrl.cpp
@@ -131,6 +131,10 @@ bool CVideoDbUrl::parse()
m_itemType = "tags";
break;
+ case VIDEODATABASEDIRECTORY::NODE_TYPE_VIDEOVERSIONS:
+ m_itemType = "videoversions";
+ break;
+
case VIDEODATABASEDIRECTORY::NODE_TYPE_ROOT:
case VIDEODATABASEDIRECTORY::NODE_TYPE_OVERVIEW:
default:
@@ -181,6 +185,8 @@ bool CVideoDbUrl::parse()
AddOption("tvshowid", (int)queryParams.GetTvShowId());
if (queryParams.GetYear() != -1)
AddOption("year", (int)queryParams.GetYear());
+ if (queryParams.GetVideoVersionId() != -1)
+ AddOption("videoversionid", (int)queryParams.GetVideoVersionId());
return true;
}
diff --git a/xbmc/video/VideoGeneratedImageFileLoader.cpp b/xbmc/video/VideoGeneratedImageFileLoader.cpp
index 72ec950e16..e39f5960cf 100644
--- a/xbmc/video/VideoGeneratedImageFileLoader.cpp
+++ b/xbmc/video/VideoGeneratedImageFileLoader.cpp
@@ -10,9 +10,12 @@
#include "DVDFileInfo.h"
#include "FileItem.h"
+#include "ServiceBroker.h"
#include "URL.h"
#include "filesystem/DirectoryCache.h"
#include "guilib/Texture.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
#include "utils/URIUtils.h"
#include "video/VideoInfoTag.h"
@@ -48,6 +51,12 @@ void SetupRarOptions(CFileItem& item, const std::string& path)
std::unique_ptr<CTexture> VIDEO::CVideoGeneratedImageFileLoader::Load(
const std::string& specialType, const std::string& filePath, unsigned int, unsigned int) const
{
+ if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_MYVIDEOS_EXTRACTTHUMB))
+ {
+ return {};
+ }
+
CFileItem item{filePath, false};
if (URIUtils::IsInRAR(filePath))
diff --git a/xbmc/video/VideoInfoDownloader.cpp b/xbmc/video/VideoInfoDownloader.cpp
index 5d0fb1f1b7..cd0a1d850d 100644
--- a/xbmc/video/VideoInfoDownloader.cpp
+++ b/xbmc/video/VideoInfoDownloader.cpp
@@ -80,7 +80,7 @@ void CVideoInfoDownloader::Process()
}
else if (m_state == GET_DETAILS)
{
- if (!GetDetails(m_url, m_movieDetails))
+ if (!GetDetails(m_uniqueIDs, m_url, m_movieDetails))
CLog::Log(LOGERROR, "{}: Error getting details from {}", __FUNCTION__,
m_url.GetFirstThumbUrl());
}
@@ -147,12 +147,14 @@ bool CVideoInfoDownloader::GetArtwork(CVideoInfoTag &details)
return m_info->GetArtwork(*m_http, details);
}
-bool CVideoInfoDownloader::GetDetails(const CScraperUrl &url,
- CVideoInfoTag &movieDetails,
- CGUIDialogProgress *pProgress /* = NULL */)
+bool CVideoInfoDownloader::GetDetails(const std::unordered_map<std::string, std::string>& uniqueIDs,
+ const CScraperUrl& url,
+ CVideoInfoTag& movieDetails,
+ CGUIDialogProgress* pProgress /* = NULL */)
{
//CLog::Log(LOGDEBUG,"CVideoInfoDownloader::GetDetails({})", url.m_strURL);
m_url = url;
+ m_uniqueIDs = uniqueIDs;
m_movieDetails = movieDetails;
// fill in the defaults
@@ -179,7 +181,7 @@ bool CVideoInfoDownloader::GetDetails(const CScraperUrl &url,
return true;
}
else // unthreaded
- return m_info->GetVideoDetails(*m_http, url, true/*fMovie*/, movieDetails);
+ return m_info->GetVideoDetails(*m_http, m_uniqueIDs, url, true /*fMovie*/, movieDetails);
}
bool CVideoInfoDownloader::GetEpisodeDetails(const CScraperUrl &url,
@@ -214,7 +216,7 @@ bool CVideoInfoDownloader::GetEpisodeDetails(const CScraperUrl &url,
return true;
}
else // unthreaded
- return m_info->GetVideoDetails(*m_http, url, false/*fMovie*/, movieDetails);
+ return m_info->GetVideoDetails(*m_http, m_uniqueIDs, url, false /*fMovie*/, movieDetails);
}
bool CVideoInfoDownloader::GetEpisodeList(const CScraperUrl& url,
diff --git a/xbmc/video/VideoInfoDownloader.h b/xbmc/video/VideoInfoDownloader.h
index a85603fe06..4479522a33 100644
--- a/xbmc/video/VideoInfoDownloader.h
+++ b/xbmc/video/VideoInfoDownloader.h
@@ -54,7 +54,10 @@ public:
*/
bool GetArtwork(CVideoInfoTag &details);
- bool GetDetails(const CScraperUrl& url, CVideoInfoTag &movieDetails, CGUIDialogProgress *pProgress = NULL);
+ bool GetDetails(const std::unordered_map<std::string, std::string>& uniqueIDs,
+ const CScraperUrl& url,
+ CVideoInfoTag& movieDetails,
+ CGUIDialogProgress* pProgress = NULL);
bool GetEpisodeDetails(const CScraperUrl& url, CVideoInfoTag &movieDetails, CGUIDialogProgress *pProgress = NULL);
bool GetEpisodeList(const CScraperUrl& url, VIDEO::EPISODELIST& details, CGUIDialogProgress *pProgress = NULL);
@@ -70,6 +73,7 @@ protected:
XFILE::CCurlFile* m_http;
std::string m_movieTitle;
int m_movieYear;
+ std::unordered_map<std::string, std::string> m_uniqueIDs;
MOVIELIST m_movieList;
CVideoInfoTag m_movieDetails;
CScraperUrl m_url;
diff --git a/xbmc/video/VideoInfoScanner.cpp b/xbmc/video/VideoInfoScanner.cpp
index 6b7ab281e5..e70b1ee0a1 100644
--- a/xbmc/video/VideoInfoScanner.cpp
+++ b/xbmc/video/VideoInfoScanner.cpp
@@ -11,7 +11,6 @@
#include "FileItem.h"
#include "GUIInfoManager.h"
#include "GUIUserMessages.h"
-#include "NfoFile.h"
#include "ServiceBroker.h"
#include "TextureCache.h"
#include "URL.h"
@@ -23,7 +22,6 @@
#include "events/EventLog.h"
#include "events/MediaLibraryEvent.h"
#include "filesystem/Directory.h"
-#include "filesystem/DirectoryCache.h"
#include "filesystem/File.h"
#include "filesystem/MultiPathDirectory.h"
#include "filesystem/PluginDirectory.h"
@@ -45,8 +43,10 @@
#include "utils/Variant.h"
#include "utils/log.h"
#include "video/VideoThumbLoader.h"
+#include "video/dialogs/GUIDialogVideoVersion.h"
#include <algorithm>
+#include <memory>
#include <utility>
using namespace XFILE;
@@ -74,7 +74,13 @@ namespace VIDEO
try
{
- if (m_showDialog && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE))
+ auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ m_ignoreVideoVersions =
+ settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_IGNOREVIDEOVERSIONS);
+ m_ignoreVideoExtras = settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_IGNOREVIDEOEXTRAS);
+
+ if (m_showDialog && !settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE))
{
CGUIDialogExtendedProgressBar* dialog =
CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogExtendedProgressBar>(WINDOW_DIALOG_EXT_PROGRESS);
@@ -365,10 +371,11 @@ namespace VIDEO
items.SetPath(URIUtils::GetParentPath(item->GetPath()));
}
}
-
+ bool foundSomething = false;
if (!bSkip)
{
- if (RetrieveVideoInfo(items, settings.parent_name_root, content))
+ foundSomething = RetrieveVideoInfo(items, settings.parent_name_root, content);
+ if (foundSomething)
{
if (!m_bStop && (content == CONTENT_MOVIES || content == CONTENT_MUSICVIDEOS))
{
@@ -402,6 +409,19 @@ namespace VIDEO
if (m_bStop)
break;
+ // add video extras to library
+ if (foundSomething && !m_ignoreVideoExtras && pItem->IsVideoExtras())
+ {
+ if (AddVideoExtras(items, content, pItem->GetPath()))
+ {
+ CLog::Log(LOGDEBUG, "VideoInfoScanner: Finished adding video extras from dir {}",
+ CURL::GetRedacted(pItem->GetPath()));
+ }
+
+ // no further processing required
+ continue;
+ }
+
// if we have a directory item (non-playlist) we then recurse into that folder
// do not recurse for tv shows - we have already looked recursively for episodes
if (pItem->m_bIsFolder && !pItem->IsParentFolder() && !pItem->IsPlayList() && settings.recurse > 0 && content != CONTENT_TVSHOWS)
@@ -500,11 +520,16 @@ namespace VIDEO
auto eventLog = CServiceBroker::GetEventLog();
if (eventLog)
+ {
+ const std::string itemlogpath = (info2->Content() == CONTENT_TVSHOWS)
+ ? CURL::GetRedacted(pItem->GetPath())
+ : URIUtils::GetFileName(pItem->GetPath());
+
eventLog->Add(EventPtr(new CMediaLibraryEvent(
mediaType, pItem->GetPath(), 24145,
- StringUtils::Format(g_localizeStrings.Get(24147), mediaType,
- URIUtils::GetFileName(pItem->GetPath())),
- pItem->GetArt("thumb"), CURL::GetRedacted(pItem->GetPath()), EventLevel::Warning)));
+ StringUtils::Format(g_localizeStrings.Get(24147), mediaType, itemlogpath),
+ EventLevel::Warning)));
+ }
}
pURL = NULL;
@@ -629,6 +654,35 @@ namespace VIDEO
movieTitle = tag->GetTitle();
movieYear = tag->GetYear(); // movieYear is expected to be >= 0
}
+
+ std::string identifierType;
+ std::string identifier;
+ long lResult = -1;
+ if (info2->IsPython() && CUtil::GetFilenameIdentifier(movieTitle, identifierType, identifier))
+ {
+ const std::unordered_map<std::string, std::string> uniqueIDs{{identifierType, identifier}};
+ if (GetDetails(pItem, uniqueIDs, url, info2,
+ (result == CInfoScanner::COMBINED_NFO || result == CInfoScanner::OVERRIDE_NFO)
+ ? loader.get()
+ : nullptr,
+ pDlgProgress))
+ {
+ if ((lResult = AddVideo(pItem, info2->Content(), false, useLocal)) < 0)
+ return INFO_ERROR;
+
+ if (fetchEpisodes)
+ {
+ INFO_RET ret = RetrieveInfoForEpisodes(pItem, lResult, info2, useLocal, pDlgProgress);
+ if (ret == INFO_ADDED)
+ {
+ m_database.SetPathHash(pItem->GetPath(), pItem->GetProperty("hash").asString());
+ return INFO_ADDED;
+ }
+ }
+ return INFO_ADDED;
+ }
+ }
+
if (pURL && pURL->HasUrls())
url = *pURL;
else if ((retVal = FindVideo(movieTitle, movieYear, info2, url, pDlgProgress)) <= 0)
@@ -636,11 +690,12 @@ namespace VIDEO
CLog::Log(LOGDEBUG, "VideoInfoScanner: Fetching url '{}' using {} scraper (content: '{}')",
url.GetFirstThumbUrl(), info2->Name(), TranslateContent(info2->Content()));
+ const std::unordered_map<std::string, std::string> uniqueIDs{{identifierType, identifier}};
- long lResult = -1;
- if (GetDetails(pItem, url, info2,
- (result == CInfoScanner::COMBINED_NFO ||
- result == CInfoScanner::OVERRIDE_NFO) ? loader.get() : nullptr,
+ if (GetDetails(pItem, {}, url, info2,
+ (result == CInfoScanner::COMBINED_NFO || result == CInfoScanner::OVERRIDE_NFO)
+ ? loader.get()
+ : nullptr,
pDlgProgress))
{
if ((lResult = AddVideo(pItem, info2->Content(), false, useLocal)) < 0)
@@ -711,6 +766,24 @@ namespace VIDEO
movieTitle = tag->GetTitle();
movieYear = tag->GetYear(); // movieYear is expected to be >= 0
}
+
+ std::string identifierType;
+ std::string identifier;
+ if (info2->IsPython() && CUtil::GetFilenameIdentifier(movieTitle, identifierType, identifier))
+ {
+ const std::unordered_map<std::string, std::string> uniqueIDs{{identifierType, identifier}};
+ if (GetDetails(pItem, uniqueIDs, url, info2,
+ (result == CInfoScanner::COMBINED_NFO || result == CInfoScanner::OVERRIDE_NFO)
+ ? loader.get()
+ : nullptr,
+ pDlgProgress))
+ {
+ if (AddVideo(pItem, info2->Content(), bDirNames, useLocal) < 0)
+ return INFO_ERROR;
+ return INFO_ADDED;
+ }
+ }
+
if (pURL && pURL->HasUrls())
url = *pURL;
else if ((retVal = FindVideo(movieTitle, movieYear, info2, url, pDlgProgress)) <= 0)
@@ -719,13 +792,18 @@ namespace VIDEO
CLog::Log(LOGDEBUG, "VideoInfoScanner: Fetching url '{}' using {} scraper (content: '{}')",
url.GetFirstThumbUrl(), info2->Name(), TranslateContent(info2->Content()));
- if (GetDetails(pItem, url, info2,
- (result == CInfoScanner::COMBINED_NFO ||
- result == CInfoScanner::OVERRIDE_NFO) ? loader.get() : nullptr,
+ if (GetDetails(pItem, {}, url, info2,
+ (result == CInfoScanner::COMBINED_NFO || result == CInfoScanner::OVERRIDE_NFO)
+ ? loader.get()
+ : nullptr,
pDlgProgress))
{
- if (AddVideo(pItem, info2->Content(), bDirNames, useLocal) < 0)
+ int dbId = AddVideo(pItem, info2->Content(), bDirNames, useLocal);
+ if (dbId < 0)
return INFO_ERROR;
+ if (info2->Content() == CONTENT_MOVIES && !m_ignoreVideoVersions &&
+ ProcessVideoVersion(VideoDbContentType::MOVIES, dbId))
+ return INFO_HAVE_ALREADY;
return INFO_ADDED;
}
//! @todo This is not strictly correct as we could fail to download information here or error, or be cancelled
@@ -788,6 +866,24 @@ namespace VIDEO
movieTitle = tag->GetTitle();
movieYear = tag->GetYear(); // movieYear is expected to be >= 0
}
+
+ std::string identifierType;
+ std::string identifier;
+ if (info2->IsPython() && CUtil::GetFilenameIdentifier(movieTitle, identifierType, identifier))
+ {
+ const std::unordered_map<std::string, std::string> uniqueIDs{{identifierType, identifier}};
+ if (GetDetails(pItem, uniqueIDs, url, info2,
+ (result == CInfoScanner::COMBINED_NFO || result == CInfoScanner::OVERRIDE_NFO)
+ ? loader.get()
+ : nullptr,
+ pDlgProgress))
+ {
+ if (AddVideo(pItem, info2->Content(), bDirNames, useLocal) < 0)
+ return INFO_ERROR;
+ return INFO_ADDED;
+ }
+ }
+
if (pURL && pURL->HasUrls())
url = *pURL;
else if ((retVal = FindVideo(movieTitle, movieYear, info2, url, pDlgProgress)) <= 0)
@@ -796,9 +892,10 @@ namespace VIDEO
CLog::Log(LOGDEBUG, "VideoInfoScanner: Fetching url '{}' using {} scraper (content: '{}')",
url.GetFirstThumbUrl(), info2->Name(), TranslateContent(info2->Content()));
- if (GetDetails(pItem, url, info2,
- (result == CInfoScanner::COMBINED_NFO ||
- result == CInfoScanner::OVERRIDE_NFO) ? loader.get() : nullptr,
+ if (GetDetails(pItem, {}, url, info2,
+ (result == CInfoScanner::COMBINED_NFO || result == CInfoScanner::OVERRIDE_NFO)
+ ? loader.get()
+ : nullptr,
pDlgProgress))
{
if (AddVideo(pItem, info2->Content(), bDirNames, useLocal) < 0)
@@ -1398,6 +1495,33 @@ namespace VIDEO
if (!strTrailer.empty())
movieDetails.m_strTrailer = strTrailer;
+ // Deal with 'Disc n' subdirectories
+ const std::string discNum{
+ CUtil::GetDiscNumberFromPath(URIUtils::GetParentPath(movieDetails.m_strFileNameAndPath))};
+ if (!discNum.empty())
+ {
+ if (movieDetails.m_set.title.empty())
+ {
+ const std::string setName{m_database.GetSetByNameLike(movieDetails.m_strTitle)};
+ if (!setName.empty())
+ {
+ // Add movie to existing set
+ movieDetails.SetSet(setName);
+ }
+ else
+ {
+ // Create set, then add movie to the set
+ const int idSet{m_database.AddSet(movieDetails.m_strTitle)};
+ m_database.SetArtForItem(idSet, MediaTypeVideoCollection, art);
+ movieDetails.SetSet(movieDetails.m_strTitle);
+ }
+ }
+
+ // Add '(Disc n)' to title (in local language)
+ movieDetails.m_strTitle =
+ StringUtils::Format(g_localizeStrings.Get(29995), movieDetails.m_strTitle, discNum);
+ }
+
lResult = m_database.SetDetailsForMovie(movieDetails, art);
movieDetails.m_iDbId = lResult;
movieDetails.m_type = MediaTypeMovie;
@@ -1478,7 +1602,7 @@ namespace VIDEO
m_database.Close();
- CFileItemPtr itemCopy = CFileItemPtr(new CFileItem(*pItem));
+ CFileItemPtr itemCopy = std::make_shared<CFileItem>(*pItem);
CVariant data;
data["added"] = true;
if (m_bRunning)
@@ -1503,6 +1627,21 @@ namespace VIDEO
}
}
+ VideoDbContentType ContentToVideoDbType(CONTENT_TYPE content)
+ {
+ switch (content)
+ {
+ case CONTENT_MOVIES:
+ return VideoDbContentType::MOVIES;
+ case CONTENT_MUSICVIDEOS:
+ return VideoDbContentType::MUSICVIDEOS;
+ case CONTENT_TVSHOWS:
+ return VideoDbContentType::EPISODES;
+ default:
+ return VideoDbContentType::UNKNOWN;
+ }
+ }
+
std::string CVideoInfoScanner::GetArtTypeFromSize(unsigned int width, unsigned int height)
{
std::string type = "thumb";
@@ -1960,7 +2099,9 @@ namespace VIDEO
return INFO_ADDED;
}
- bool CVideoInfoScanner::GetDetails(CFileItem *pItem, CScraperUrl &url,
+ bool CVideoInfoScanner::GetDetails(CFileItem* pItem,
+ const std::unordered_map<std::string, std::string>& uniqueIDs,
+ CScraperUrl& url,
const ScraperPtr& scraper,
IVideoInfoTagLoader* loader,
CGUIDialogProgress* pDialog /* = NULL */)
@@ -1971,7 +2112,7 @@ namespace VIDEO
m_handle->SetText(url.GetTitle());
CVideoInfoDownloader imdb(scraper);
- bool ret = imdb.GetDetails(url, movieDetails, pDialog);
+ bool ret = imdb.GetDetails(uniqueIDs, url, movieDetails, pDialog);
if (ret)
{
@@ -2079,7 +2220,7 @@ namespace VIDEO
const std::vector<std::string> &excludes) const
{
CFileItemList items;
- items.Add(CFileItemPtr(new CFileItem(directory, true)));
+ items.Add(std::make_shared<CFileItem>(directory, true));
CUtil::GetRecursiveDirsListing(directory, items, DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_NO_FILE_INFO);
CDigest digest{CDigest::Type::MD5};
@@ -2267,4 +2408,63 @@ namespace VIDEO
return 0; // didn't find anything
}
+ bool CVideoInfoScanner::AddVideoExtras(CFileItemList& items,
+ const CONTENT_TYPE& content,
+ const std::string& path)
+ {
+ int dbId = -1;
+
+ // get the library item which was added previously with the specified conent type
+ for (const auto& item : items)
+ {
+ if (content == CONTENT_MOVIES)
+ {
+ dbId = m_database.GetMovieId(item->GetPath());
+ if (dbId != -1)
+ {
+ break;
+ }
+ }
+ }
+
+ if (dbId == -1)
+ {
+ CLog::Log(LOGERROR, "VideoInfoScanner: Failed to find the library item for video extras {}",
+ CURL::GetRedacted(path));
+ return false;
+ }
+
+ // Add video extras to library
+ CDirectory::EnumerateDirectory(
+ path,
+ [this, content, dbId, path](const std::shared_ptr<CFileItem>& item)
+ {
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_MYVIDEOS_EXTRACTFLAGS))
+ {
+ CDVDFileInfo::GetFileStreamDetails(item.get());
+ CLog::Log(LOGDEBUG, "VideoInfoScanner: Extracted filestream details from video file {}",
+ CURL::GetRedacted(item->GetPath()));
+ }
+
+ const std::string typeVideoVersion =
+ CGUIDialogVideoVersion::GenerateExtrasVideoVersion(path, item->GetPath());
+
+ const int idVideoVersion =
+ m_database.AddVideoVersionType(typeVideoVersion, VideoVersionTypeOwner::AUTO);
+
+ m_database.AddExtrasVideoVersion(ContentToVideoDbType(content), dbId, idVideoVersion,
+ *item.get());
+ CLog::Log(LOGDEBUG, "VideoInfoScanner: Added video extras {}",
+ CURL::GetRedacted(item->GetPath()));
+ },
+ true, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), DIR_FLAG_DEFAULTS);
+
+ return true;
+ }
+
+ bool CVideoInfoScanner::ProcessVideoVersion(VideoDbContentType itemType, int dbId)
+ {
+ return CGUIDialogVideoVersion::ProcessVideoVersion(itemType, dbId);
+ }
}
diff --git a/xbmc/video/VideoInfoScanner.h b/xbmc/video/VideoInfoScanner.h
index 3bd23144c5..9f49103be7 100644
--- a/xbmc/video/VideoInfoScanner.h
+++ b/xbmc/video/VideoInfoScanner.h
@@ -144,14 +144,17 @@ namespace VIDEO
/*! \brief Retrieve detailed information for an item from an online source, optionally supplemented with local data
@todo sort out some better return codes.
\param pItem item to retrieve online details for.
+ \param uniqueIDs Unique IDs for additional information for scrapers.
\param url URL to use to retrieve online details.
\param scraper Scraper that handles parsing the online data.
\param nfoFile if set, we override the online data with the locally supplied data. Defaults to NULL.
\param pDialog progress dialog to update and check for cancellation during processing. Defaults to NULL.
\return true if information is found, false if an error occurred, the lookup was cancelled, or no information was found.
*/
- bool GetDetails(CFileItem *pItem, CScraperUrl &url,
- const ADDON::ScraperPtr &scraper,
+ bool GetDetails(CFileItem* pItem,
+ const std::unordered_map<std::string, std::string>& uniqueIDs,
+ CScraperUrl& url,
+ const ADDON::ScraperPtr& scraper,
VIDEO::IVideoInfoTagLoader* nfoFile = nullptr,
CGUIDialogProgress* pDialog = nullptr);
@@ -236,8 +239,13 @@ namespace VIDEO
bool EnumerateSeriesFolder(CFileItem* item, EPISODELIST& episodeList);
bool ProcessItemByVideoInfoTag(const CFileItem *item, EPISODELIST &episodeList);
+ bool AddVideoExtras(CFileItemList& items, const CONTENT_TYPE& content, const std::string& path);
+ bool ProcessVideoVersion(VideoDbContentType itemType, int dbId);
+
bool m_bStop;
bool m_scanAll;
+ bool m_ignoreVideoVersions{false};
+ bool m_ignoreVideoExtras{false};
std::string m_strStartDir;
CVideoDatabase m_database;
std::set<std::string> m_pathsToCount;
diff --git a/xbmc/video/VideoInfoTag.cpp b/xbmc/video/VideoInfoTag.cpp
index 811dd06127..b896c777ad 100644
--- a/xbmc/video/VideoInfoTag.cpp
+++ b/xbmc/video/VideoInfoTag.cpp
@@ -18,6 +18,7 @@
#include "utils/Variant.h"
#include "utils/XMLUtils.h"
#include "utils/log.h"
+#include "video/VideoDatabase.h"
#include <algorithm>
#include <sstream>
@@ -43,6 +44,7 @@ void CVideoInfoTag::Reset()
m_set.id = -1;
m_set.overview.clear();
m_tags.clear();
+ m_typeVideoVersion.clear();
m_strFile.clear();
m_strPath.clear();
m_strMPAARating.clear();
@@ -222,6 +224,8 @@ bool CVideoInfoTag::Save(TiXmlNode *node, const std::string &tag, bool savePathI
movie->InsertEndChild(set);
}
XMLUtils::SetStringArray(movie, "tag", m_tags);
+ XMLUtils::SetString(movie, "videoversion", m_typeVideoVersion);
+ XMLUtils::SetInt(movie, "videoversionid", m_idVideoVersion);
XMLUtils::SetStringArray(movie, "credits", m_writingCredits);
XMLUtils::SetStringArray(movie, "director", m_director);
if (HasPremiered())
@@ -505,6 +509,9 @@ void CVideoInfoTag::Archive(CArchive& ar)
ar << m_set.id;
ar << m_set.overview;
ar << m_tags;
+ ar << m_typeVideoVersion;
+ ar << m_idVideoVersion;
+ ar << m_hasVideoVersions;
ar << m_duration;
ar << m_strFile;
ar << m_strPath;
@@ -607,6 +614,9 @@ void CVideoInfoTag::Archive(CArchive& ar)
ar >> m_set.id;
ar >> m_set.overview;
ar >> m_tags;
+ ar >> m_typeVideoVersion;
+ ar >> m_idVideoVersion;
+ ar >> m_hasVideoVersions;
ar >> m_duration;
ar >> m_strFile;
ar >> m_strPath;
@@ -726,6 +736,8 @@ void CVideoInfoTag::Serialize(CVariant& value) const
value["setid"] = m_set.id;
value["setoverview"] = m_set.overview;
value["tag"] = m_tags;
+ value["videoversion"] = m_typeVideoVersion;
+ value["videoversionid"] = m_idVideoVersion;
value["runtime"] = GetDuration();
value["file"] = m_strFile;
value["path"] = m_strPath;
@@ -855,6 +867,9 @@ void CVideoInfoTag::ToSortable(SortItem& sortable, Field field) const
case FieldId: sortable[FieldId] = m_iDbId; break;
case FieldTrackNumber: sortable[FieldTrackNumber] = m_iTrack; break;
case FieldTag: sortable[FieldTag] = m_tags; break;
+ case FieldVideoVersion:
+ sortable[FieldVideoVersion] = m_typeVideoVersion;
+ break;
case FieldVideoResolution: sortable[FieldVideoResolution] = m_streamDetails.GetVideoHeight(); break;
case FieldVideoAspectRatio: sortable[FieldVideoAspectRatio] = m_streamDetails.GetVideoAspect(); break;
@@ -1253,6 +1268,11 @@ void CVideoInfoTag::ParseNative(const TiXmlElement* movie, bool prioritise)
if (XMLUtils::GetStringArray(movie, "tag", tags, prioritise, itemSeparator))
SetTags(tags);
+ if (XMLUtils::GetString(movie, "videoversion", value))
+ SetVideoVersion(value);
+
+ XMLUtils::GetInt(movie, "videoversionid", m_idVideoVersion);
+
std::vector<std::string> studio(m_studio);
if (XMLUtils::GetStringArray(movie, "studio", studio, prioritise, itemSeparator))
SetStudio(studio);
@@ -1625,6 +1645,16 @@ void CVideoInfoTag::SetTags(std::vector<std::string> tags)
m_tags = Trim(std::move(tags));
}
+void CVideoInfoTag::SetVideoVersion(std::string typeVideoVersion)
+{
+ m_typeVideoVersion = Trim(std::move(typeVideoVersion));
+}
+
+bool CVideoInfoTag::HasVideoVersions() const
+{
+ return m_hasVideoVersions;
+}
+
void CVideoInfoTag::SetFile(std::string file)
{
m_strFile = Trim(std::move(file));
@@ -1789,3 +1819,19 @@ bool CVideoInfoTag::SetResumePoint(double timeInSeconds, double totalTimeInSecon
m_resumePoint = resumePoint;
return true;
}
+
+bool CVideoInfoTag::IsVideoExtras() const
+{
+ if (m_type == MediaTypeVideoVersion)
+ {
+ CVideoDatabase videodb;
+ if (videodb.Open())
+ {
+ return videodb.IsVideoExtras(m_iDbId);
+ }
+ else
+ CLog::Log(LOGERROR, "{}: Failed to open database", __FUNCTION__);
+ }
+
+ return false;
+}
diff --git a/xbmc/video/VideoInfoTag.h b/xbmc/video/VideoInfoTag.h
index ffbdb15d3c..32000be840 100644
--- a/xbmc/video/VideoInfoTag.h
+++ b/xbmc/video/VideoInfoTag.h
@@ -89,6 +89,7 @@ public:
const std::string GetCast(bool bIncludeRole = false) const;
bool HasStreamDetails() const;
bool IsEmpty() const;
+ bool HasVideoVersions() const;
const std::string& GetPath() const
{
@@ -145,6 +146,7 @@ public:
void SetSet(std::string set);
void SetSetOverview(std::string setOverview);
void SetTags(std::vector<std::string> tags);
+ void SetVideoVersion(std::string typeVideoVersion);
void SetFile(std::string file);
void SetPath(std::string path);
void SetMPAARating(std::string mpaaRating);
@@ -205,6 +207,8 @@ public:
*/
virtual bool SetResumePoint(const CBookmark &resumePoint);
+ bool IsVideoExtras() const;
+
/*!
* @brief Set this videos's resume point.
* @param timeInSeconds the time of the resume point
@@ -238,6 +242,9 @@ public:
};
SetInfo m_set; //!< Assigned movie set
std::vector<std::string> m_tags;
+ std::string m_typeVideoVersion;
+ int m_idVideoVersion{-1};
+ bool m_hasVideoVersions{false};
std::string m_strFile;
std::string m_strPath;
std::string m_strMPAARating;
diff --git a/xbmc/video/VideoThumbLoader.cpp b/xbmc/video/VideoThumbLoader.cpp
index 91d1e7a436..541892b90f 100644
--- a/xbmc/video/VideoThumbLoader.cpp
+++ b/xbmc/video/VideoThumbLoader.cpp
@@ -15,6 +15,7 @@
#include "cores/VideoPlayer/DVDFileInfo.h"
#include "cores/VideoSettings.h"
#include "filesystem/Directory.h"
+#include "filesystem/File.h"
#include "filesystem/StackDirectory.h"
#include "guilib/GUIComponent.h"
#include "guilib/StereoscopicsManager.h"
@@ -75,13 +76,14 @@ std::vector<std::string> GetSettingListAsString(const std::string& settingID)
}
const std::map<std::string, std::vector<std::string>> artTypeDefaults = {
- {MediaTypeEpisode, {"thumb"}},
- {MediaTypeTvShow, {"poster", "fanart", "banner"}},
- {MediaTypeSeason, {"poster", "fanart", "banner"}},
- {MediaTypeMovie, {"poster", "fanart"}},
- {MediaTypeVideoCollection, {"poster", "fanart"}},
- {MediaTypeMusicVideo, {"poster", "fanart"}},
- {MediaTypeNone, { "poster", "fanart", "banner", "thumb" }},
+ {MediaTypeEpisode, {"thumb"}},
+ {MediaTypeTvShow, {"poster", "fanart", "banner"}},
+ {MediaTypeSeason, {"poster", "fanart", "banner"}},
+ {MediaTypeMovie, {"poster", "fanart"}},
+ {MediaTypeVideoCollection, {"poster", "fanart"}},
+ {MediaTypeMusicVideo, {"poster", "fanart"}},
+ {MediaTypeVideoVersion, {"poster", "fanart", "banner", "thumb"}},
+ {MediaTypeNone, {"poster", "fanart", "banner", "thumb"}},
};
const std::vector<std::string> artTypeDefaultsFallback = {};
@@ -95,12 +97,13 @@ const std::vector<std::string>& GetArtTypeDefault(const std::string& mediaType)
}
const std::map<std::string, std::string> artTypeSettings = {
- {MediaTypeEpisode, CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST},
- {MediaTypeTvShow, CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST},
- {MediaTypeSeason, CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST},
- {MediaTypeMovie, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST},
- {MediaTypeVideoCollection, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST},
- {MediaTypeMusicVideo, CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST},
+ {MediaTypeEpisode, CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST},
+ {MediaTypeTvShow, CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST},
+ {MediaTypeSeason, CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST},
+ {MediaTypeMovie, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST},
+ {MediaTypeVideoCollection, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST},
+ {MediaTypeMusicVideo, CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST},
+ {MediaTypeVideoVersion, CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST},
};
} // namespace
@@ -188,11 +191,12 @@ bool CVideoThumbLoader::LoadItemCached(CFileItem* pItem)
{
FillLibraryArt(*pItem);
- if (!pItem->GetVideoInfoTag()->m_type.empty() &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeMovie &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeTvShow &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeEpisode &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeMusicVideo)
+ if (!pItem->GetVideoInfoTag()->m_type.empty() &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeMovie &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeTvShow &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeEpisode &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeMusicVideo &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeVideoVersion)
{
m_videoDatabase->Close();
return true; // nothing else to be done
@@ -226,12 +230,12 @@ bool CVideoThumbLoader::LoadItemLookup(CFileItem* pItem)
if (pItem->m_bIsShareOrDrive || pItem->IsParentFolder() || pItem->GetPath() == "add")
return false;
- if (pItem->HasVideoInfoTag() &&
- !pItem->GetVideoInfoTag()->m_type.empty() &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeMovie &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeTvShow &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeEpisode &&
- pItem->GetVideoInfoTag()->m_type != MediaTypeMusicVideo)
+ if (pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->m_type.empty() &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeMovie &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeTvShow &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeEpisode &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeMusicVideo &&
+ pItem->GetVideoInfoTag()->m_type != MediaTypeVideoVersion)
return false; // Nothing to do here
m_videoDatabase->Open();
@@ -483,10 +487,14 @@ std::string CVideoThumbLoader::GetLocalArt(const CFileItem &item, const std::str
thumbloader thread accesses the streamed filesystem at the same time as the
app thread and the latter has to wait for it.
*/
- if (item.m_bIsFolder &&
- (item.IsStreamedFilesystem() ||
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cacheBufferMode ==
- CACHE_BUFFER_MODE_ALL))
+
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ const bool cacheAll =
+ settings ? settings->GetInt(CSettings::SETTING_FILECACHE_BUFFERMODE) == CACHE_BUFFER_MODE_ALL
+ : false;
+
+ if (item.m_bIsFolder && (item.IsStreamedFilesystem() || cacheAll))
{
CFileItemList items; // Dummy list
CDirectory::GetDirectory(item.GetPath(), items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
diff --git a/xbmc/video/VideoUtils.cpp b/xbmc/video/VideoUtils.cpp
index b10c0f6a25..27f0bad224 100644
--- a/xbmc/video/VideoUtils.cpp
+++ b/xbmc/video/VideoUtils.cpp
@@ -25,11 +25,13 @@
#include "guilib/LocalizeStrings.h"
#include "playlists/PlayList.h"
#include "playlists/PlayListFactory.h"
+#include "profiles/ProfileManager.h"
#include "settings/MediaSettings.h"
#include "settings/SettingUtils.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "threads/IRunnable.h"
+#include "utils/FileUtils.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/log.h"
@@ -44,7 +46,8 @@ class CAsyncGetItemsForPlaylist : public IRunnable
public:
CAsyncGetItemsForPlaylist(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
: m_item(item),
- m_resume(item->GetStartOffset() == STARTOFFSET_RESUME),
+ m_resume((item->GetStartOffset() == STARTOFFSET_RESUME) &&
+ VIDEO_UTILS::GetItemResumeInformation(*item).isResumable),
m_queuedItems(queuedItems)
{
}
@@ -283,23 +286,8 @@ void CAsyncGetItemsForPlaylist::GetItemsForPlaylist(const std::shared_ptr<CFileI
}
else if (item->IsPlayList())
{
- const std::unique_ptr<PLAYLIST::CPlayList> playList(PLAYLIST::CPlayListFactory::Create(*item));
- if (!playList)
- {
- CLog::LogF(LOGERROR, "Failed to create playlist {}", item->GetPath());
- return;
- }
-
- if (!playList->Load(item->GetPath()))
- {
- CLog::LogF(LOGERROR, "Failed to load playlist {}", item->GetPath());
- return;
- }
-
- for (int i = 0; i < playList->size(); ++i)
- {
- GetItemsForPlaylist((*playList)[i]);
- }
+ // just queue the playlist, it will be expanded on play
+ m_queuedItems.Add(item);
}
else if (item->IsInternetStream())
{
@@ -338,7 +326,8 @@ std::string GetVideoDbItemPath(const CFileItem& item)
}
void AddItemToPlayListAndPlay(const std::shared_ptr<CFileItem>& itemToQueue,
- const std::shared_ptr<CFileItem>& itemToPlay)
+ const std::shared_ptr<CFileItem>& itemToPlay,
+ const std::string& player)
{
// recursively add items to list
CFileItemList queuedItems;
@@ -370,7 +359,7 @@ void AddItemToPlayListAndPlay(const std::shared_ptr<CFileItem>& itemToQueue,
}
playlistPlayer.SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
- playlistPlayer.Play(pos, "");
+ playlistPlayer.Play(pos, player);
}
} // unnamed namespace
@@ -388,13 +377,15 @@ bool IsAutoPlayNextItem(const CFileItem& item)
bool IsAutoPlayNextItem(const std::string& content)
{
int settingValue = CSettings::SETTING_AUTOPLAYNEXT_UNCATEGORIZED;
- if (content == MediaTypeMovie)
+ if (content == MediaTypeMovie || content == MediaTypeMovies ||
+ content == MediaTypeVideoCollections)
settingValue = CSettings::SETTING_AUTOPLAYNEXT_MOVIES;
- else if (content == MediaTypeEpisode)
+ else if (content == MediaTypeEpisode || content == MediaTypeSeasons ||
+ content == MediaTypeEpisodes)
settingValue = CSettings::SETTING_AUTOPLAYNEXT_EPISODES;
- else if (content == MediaTypeMusicVideo)
+ else if (content == MediaTypeMusicVideo || content == MediaTypeMusicVideos)
settingValue = CSettings::SETTING_AUTOPLAYNEXT_MUSICVIDEOS;
- else if (content == MediaTypeTvShow)
+ else if (content == MediaTypeTvShow || content == MediaTypeTvShows)
settingValue = CSettings::SETTING_AUTOPLAYNEXT_TVSHOWS;
const auto setting = std::dynamic_pointer_cast<CSettingList>(
@@ -406,6 +397,7 @@ bool IsAutoPlayNextItem(const std::string& content)
void PlayItem(
const std::shared_ptr<CFileItem>& itemIn,
+ const std::string& player,
ContentUtils::PlayMode mode /* = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_VIDEO */)
{
auto item = itemIn;
@@ -421,7 +413,7 @@ void PlayItem(
if (item->m_bIsFolder && !item->IsPlugin())
{
- AddItemToPlayListAndPlay(item, nullptr);
+ AddItemToPlayListAndPlay(item, nullptr, player);
}
else if (item->HasVideoInfoTag())
{
@@ -450,7 +442,7 @@ void PlayItem(
if (item->GetStartOffset() == STARTOFFSET_RESUME)
parentItem->SetStartOffset(STARTOFFSET_RESUME);
- AddItemToPlayListAndPlay(parentItem, item);
+ AddItemToPlayListAndPlay(parentItem, item, player);
}
else // mode == PlayMode::PLAY_ONLY_THIS
{
@@ -458,7 +450,7 @@ void PlayItem(
auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
playlistPlayer.Reset();
playlistPlayer.SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
- playlistPlayer.Play(item, "");
+ playlistPlayer.Play(item, player);
}
}
}
@@ -516,6 +508,20 @@ bool GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList&
true); // can be cancelled
}
+namespace
+{
+bool IsNonExistingUserPartyModePlaylist(const CFileItem& item)
+{
+ if (!item.IsSmartPlayList())
+ return false;
+
+ const std::string& path{item.GetPath()};
+ const auto profileManager{CServiceBroker::GetSettingsComponent()->GetProfileManager()};
+ return ((profileManager->GetUserDataItem("PartyMode-Video.xsp") == path) &&
+ !CFileUtils::Exists(path));
+}
+} // unnamed namespace
+
bool IsItemPlayable(const CFileItem& item)
{
if (item.IsParentFolder())
@@ -540,14 +546,11 @@ bool IsItemPlayable(const CFileItem& item)
if (item.IsPlugin() || item.IsScript() || item.IsAddonsPath())
return false;
- // Exclude unwanted windows
- if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
- return false;
-
// Exclude special items
if (StringUtils::StartsWithNoCase(item.GetPath(), "newsmartplaylist://") ||
StringUtils::StartsWithNoCase(item.GetPath(), "newplaylist://") ||
- StringUtils::StartsWithNoCase(item.GetPath(), "newtag://"))
+ StringUtils::StartsWithNoCase(item.GetPath(), "newtag://") ||
+ StringUtils::StartsWithNoCase(item.GetPath(), "newvideoversion://"))
return false;
// Include playlists located at one of the possible video/mixed playlist locations
@@ -569,13 +572,16 @@ bool IsItemPlayable(const CFileItem& item)
StringUtils::StartsWith(item.GetPath(), StringUtils::Format("{}/mixed/", path)))
return true;
- if (!item.m_bIsFolder)
+ if (!item.m_bIsFolder && !item.HasVideoInfoTag())
{
// Unknown location. Type cannot be determined for non-folder items.
return false;
}
}
+ if (IsNonExistingUserPartyModePlaylist(item))
+ return false;
+
if (item.m_bIsFolder &&
(item.IsVideoDb() || StringUtils::StartsWithNoCase(item.GetPath(), "library://video/")))
{
@@ -803,7 +809,7 @@ ResumeInformation GetStackPartResumeInformation(const CFileItem& item, unsigned
if (item.IsStack())
{
- const std::string path = item.GetDynPath();
+ const std::string& path = item.GetDynPath();
if (URIUtils::IsDiscImageStack(path))
{
// disc image stack
diff --git a/xbmc/video/VideoUtils.h b/xbmc/video/VideoUtils.h
index a55de50446..aebb637d9d 100644
--- a/xbmc/video/VideoUtils.h
+++ b/xbmc/video/VideoUtils.h
@@ -33,9 +33,11 @@ bool IsAutoPlayNextItem(const std::string& content);
all items contained in the folder and start playback of the playlist. If item is a single video
item, start playback directly, without adding it to the video playlist first.
\param item [in] the item to play
+ \param player [in] the player to use, empty for default player
\param mode [in] queue all successors and play them after item
*/
void PlayItem(const std::shared_ptr<CFileItem>& item,
+ const std::string& player,
ContentUtils::PlayMode mode = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM);
enum class QueuePosition
diff --git a/xbmc/video/dialogs/CMakeLists.txt b/xbmc/video/dialogs/CMakeLists.txt
index d29f35cd79..4af28bee01 100644
--- a/xbmc/video/dialogs/CMakeLists.txt
+++ b/xbmc/video/dialogs/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SOURCES GUIDialogAudioSettings.cpp
GUIDialogTeletext.cpp
GUIDialogVideoBookmarks.cpp
GUIDialogVideoInfo.cpp
+ GUIDialogVideoVersion.cpp
GUIDialogVideoOSD.cpp
GUIDialogVideoSettings.cpp)
@@ -15,6 +16,7 @@ set(HEADERS GUIDialogAudioSettings.h
GUIDialogTeletext.h
GUIDialogVideoBookmarks.h
GUIDialogVideoInfo.h
+ GUIDialogVideoVersion.h
GUIDialogVideoOSD.h
GUIDialogVideoSettings.h)
diff --git a/xbmc/video/dialogs/GUIDialogAudioSettings.cpp b/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
index 83b888b90d..9c8b1b10ff 100644
--- a/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
@@ -33,6 +33,7 @@
#include "utils/log.h"
#include "video/VideoDatabase.h"
+#include <memory>
#include <string>
#include <vector>
@@ -241,8 +242,11 @@ void CGUIDialogAudioSettings::InitializeSettings()
CSettingDependency dependencyAudioOutputPassthroughDisabled(SettingDependencyType::Enable, GetSettingsManager());
dependencyAudioOutputPassthroughDisabled.Or()
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_AUDIO_PASSTHROUGH, "false", SettingDependencyOperator::Equals, false, GetSettingsManager())))
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition("IsPlayingPassthrough", "", "", true, GetSettingsManager())));
+ ->Add(std::make_shared<CSettingDependencyCondition>(SETTING_AUDIO_PASSTHROUGH, "false",
+ SettingDependencyOperator::Equals, false,
+ GetSettingsManager()))
+ ->Add(std::make_shared<CSettingDependencyCondition>("IsPlayingPassthrough", "", "", true,
+ GetSettingsManager()));
SettingDependencies depsAudioOutputPassthroughDisabled;
depsAudioOutputPassthroughDisabled.push_back(dependencyAudioOutputPassthroughDisabled);
diff --git a/xbmc/video/dialogs/GUIDialogCMSSettings.cpp b/xbmc/video/dialogs/GUIDialogCMSSettings.cpp
index ac1a7addfa..6299cacbc6 100644
--- a/xbmc/video/dialogs/GUIDialogCMSSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogCMSSettings.cpp
@@ -27,6 +27,7 @@
#include "utils/log.h"
#include "video/VideoDatabase.h"
+#include <memory>
#include <vector>
#define SETTING_VIDEO_CMSENABLE "videoscreen.cmsenabled"
@@ -79,31 +80,35 @@ void CGUIDialogCMSSettings::InitializeSettings()
// create "depsCmsEnabled" for settings depending on CMS being enabled
CSettingDependency dependencyCmsEnabled(SettingDependencyType::Enable, GetSettingsManager());
- dependencyCmsEnabled.Or()
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSENABLE, "true", SettingDependencyOperator::Equals, false, GetSettingsManager())));
+ dependencyCmsEnabled.Or()->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_VIDEO_CMSENABLE, "true", SettingDependencyOperator::Equals, false,
+ GetSettingsManager()));
SettingDependencies depsCmsEnabled;
depsCmsEnabled.push_back(dependencyCmsEnabled);
// create "depsCms3dlut" for 3dlut settings
CSettingDependency dependencyCms3dlut(SettingDependencyType::Visible, GetSettingsManager());
- dependencyCms3dlut.And()
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSMODE, std::to_string(CMS_MODE_3DLUT), SettingDependencyOperator::Equals, false, GetSettingsManager())));
+ dependencyCms3dlut.And()->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_VIDEO_CMSMODE, std::to_string(CMS_MODE_3DLUT), SettingDependencyOperator::Equals,
+ false, GetSettingsManager()));
SettingDependencies depsCms3dlut;
depsCms3dlut.push_back(dependencyCmsEnabled);
depsCms3dlut.push_back(dependencyCms3dlut);
// create "depsCmsIcc" for display settings with icc profile
CSettingDependency dependencyCmsIcc(SettingDependencyType::Visible, GetSettingsManager());
- dependencyCmsIcc.And()
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSMODE, std::to_string(CMS_MODE_PROFILE), SettingDependencyOperator::Equals, false, GetSettingsManager())));
+ dependencyCmsIcc.And()->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_VIDEO_CMSMODE, std::to_string(CMS_MODE_PROFILE), SettingDependencyOperator::Equals,
+ false, GetSettingsManager()));
SettingDependencies depsCmsIcc;
depsCmsIcc.push_back(dependencyCmsEnabled);
depsCmsIcc.push_back(dependencyCmsIcc);
// create "depsCmsGamma" for effective gamma adjustment (not available with bt.1886)
CSettingDependency dependencyCmsGamma(SettingDependencyType::Visible, GetSettingsManager());
- dependencyCmsGamma.And()
- ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_VIDEO_CMSGAMMAMODE, std::to_string(CMS_TRC_BT1886), SettingDependencyOperator::Equals, true, GetSettingsManager())));
+ dependencyCmsGamma.And()->Add(std::make_shared<CSettingDependencyCondition>(
+ SETTING_VIDEO_CMSGAMMAMODE, std::to_string(CMS_TRC_BT1886), SettingDependencyOperator::Equals,
+ true, GetSettingsManager()));
SettingDependencies depsCmsGamma;
depsCmsGamma.push_back(dependencyCmsEnabled);
depsCmsGamma.push_back(dependencyCmsIcc);
@@ -117,9 +122,9 @@ void CGUIDialogCMSSettings::InitializeSettings()
int currentMode = settings->GetInt(SETTING_VIDEO_CMSMODE);
entries.clear();
// entries.push_back(TranslatableIntegerSettingOption(16039, CMS_MODE_OFF)); // FIXME: get from CMS class
- entries.push_back(TranslatableIntegerSettingOption(36580, CMS_MODE_3DLUT));
+ entries.emplace_back(36580, CMS_MODE_3DLUT);
#ifdef HAVE_LCMS2
- entries.push_back(TranslatableIntegerSettingOption(36581, CMS_MODE_PROFILE));
+ entries.emplace_back(36581, CMS_MODE_PROFILE);
#endif
std::shared_ptr<CSettingInt> settingCmsMode = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSMODE, 36562, SettingLevel::Basic, currentMode, entries);
settingCmsMode->SetDependencies(depsCmsEnabled);
@@ -131,29 +136,29 @@ void CGUIDialogCMSSettings::InitializeSettings()
// display settings
int currentWhitepoint = settings->GetInt(SETTING_VIDEO_CMSWHITEPOINT);
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(36586, CMS_WHITEPOINT_D65));
- entries.push_back(TranslatableIntegerSettingOption(36587, CMS_WHITEPOINT_D93));
+ entries.emplace_back(36586, CMS_WHITEPOINT_D65);
+ entries.emplace_back(36587, CMS_WHITEPOINT_D93);
std::shared_ptr<CSettingInt> settingCmsWhitepoint = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSWHITEPOINT, 36568, SettingLevel::Basic, currentWhitepoint, entries);
settingCmsWhitepoint->SetDependencies(depsCmsIcc);
int currentPrimaries = settings->GetInt(SETTING_VIDEO_CMSPRIMARIES);
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(36588, CMS_PRIMARIES_AUTO));
- entries.push_back(TranslatableIntegerSettingOption(36589, CMS_PRIMARIES_BT709));
- entries.push_back(TranslatableIntegerSettingOption(36579, CMS_PRIMARIES_BT2020));
- entries.push_back(TranslatableIntegerSettingOption(36590, CMS_PRIMARIES_170M));
- entries.push_back(TranslatableIntegerSettingOption(36591, CMS_PRIMARIES_BT470M));
- entries.push_back(TranslatableIntegerSettingOption(36592, CMS_PRIMARIES_BT470BG));
- entries.push_back(TranslatableIntegerSettingOption(36593, CMS_PRIMARIES_240M));
+ entries.emplace_back(36588, CMS_PRIMARIES_AUTO);
+ entries.emplace_back(36589, CMS_PRIMARIES_BT709);
+ entries.emplace_back(36579, CMS_PRIMARIES_BT2020);
+ entries.emplace_back(36590, CMS_PRIMARIES_170M);
+ entries.emplace_back(36591, CMS_PRIMARIES_BT470M);
+ entries.emplace_back(36592, CMS_PRIMARIES_BT470BG);
+ entries.emplace_back(36593, CMS_PRIMARIES_240M);
std::shared_ptr<CSettingInt> settingCmsPrimaries = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSPRIMARIES, 36570, SettingLevel::Basic, currentPrimaries, entries);
settingCmsPrimaries->SetDependencies(depsCmsIcc);
int currentGammaMode = settings->GetInt(SETTING_VIDEO_CMSGAMMAMODE);
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(36582, CMS_TRC_BT1886));
- entries.push_back(TranslatableIntegerSettingOption(36583, CMS_TRC_INPUT_OFFSET));
- entries.push_back(TranslatableIntegerSettingOption(36584, CMS_TRC_OUTPUT_OFFSET));
- entries.push_back(TranslatableIntegerSettingOption(36585, CMS_TRC_ABSOLUTE));
+ entries.emplace_back(36582, CMS_TRC_BT1886);
+ entries.emplace_back(36583, CMS_TRC_INPUT_OFFSET);
+ entries.emplace_back(36584, CMS_TRC_OUTPUT_OFFSET);
+ entries.emplace_back(36585, CMS_TRC_ABSOLUTE);
std::shared_ptr<CSettingInt> settingCmsGammaMode = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSGAMMAMODE, 36572, SettingLevel::Basic, currentGammaMode, entries);
settingCmsGammaMode->SetDependencies(depsCmsIcc);
@@ -165,9 +170,9 @@ void CGUIDialogCMSSettings::InitializeSettings()
int currentLutSize = settings->GetInt(SETTING_VIDEO_CMSLUTSIZE);
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(36594, 4));
- entries.push_back(TranslatableIntegerSettingOption(36595, 6));
- entries.push_back(TranslatableIntegerSettingOption(36596, 8));
+ entries.emplace_back(36594, 4);
+ entries.emplace_back(36595, 6);
+ entries.emplace_back(36596, 8);
std::shared_ptr<CSettingInt> settingCmsLutSize = AddSpinner(groupColorManagement, SETTING_VIDEO_CMSLUTSIZE, 36576, SettingLevel::Basic, currentLutSize, entries);
settingCmsLutSize->SetDependencies(depsCmsIcc);
}
diff --git a/xbmc/video/dialogs/GUIDialogTeletext.cpp b/xbmc/video/dialogs/GUIDialogTeletext.cpp
index 01e86cb57d..13c706b12f 100644
--- a/xbmc/video/dialogs/GUIDialogTeletext.cpp
+++ b/xbmc/video/dialogs/GUIDialogTeletext.cpp
@@ -75,6 +75,10 @@ bool CGUIDialogTeletext::OnMessage(CGUIMessage& message)
void CGUIDialogTeletext::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
{
+ if (m_TextDecoder.Changed())
+ {
+ MarkDirtyRegion();
+ }
CGUIDialog::Process(currentTime, dirtyregions);
m_renderRegion = m_vertCoords;
}
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
index cc0b4965e2..df4582d229 100644
--- a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
@@ -10,6 +10,7 @@
#include "ContextMenuManager.h"
#include "FileItem.h"
+#include "GUIDialogVideoVersion.h"
#include "GUIPassword.h"
#include "GUIUserMessages.h"
#include "ServiceBroker.h"
@@ -55,7 +56,7 @@
#include "video/VideoLibraryQueue.h"
#include "video/VideoThumbLoader.h"
#include "video/VideoUtils.h"
-#include "video/guilib/VideoSelectActionProcessor.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
#include "video/windows/GUIWindowVideoNav.h"
#include <algorithm>
@@ -66,7 +67,6 @@
using namespace XFILE::VIDEODATABASEDIRECTORY;
using namespace XFILE;
using namespace KODI::MESSAGING;
-using namespace VIDEO::GUILIB;
#define CONTROL_IMAGE 3
#define CONTROL_TEXTAREA 4
@@ -79,6 +79,7 @@ using namespace VIDEO::GUILIB;
#define CONTROL_BTN_PLAY_TRAILER 11
#define CONTROL_BTN_GET_FANART 12
#define CONTROL_BTN_DIRECTOR 13
+#define CONTROL_BTN_VIDEOVERSION 14
#define CONTROL_LIST 50
@@ -155,6 +156,10 @@ bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
m_bViewReview = !m_bViewReview;
Update();
}
+ else if (iControl == CONTROL_BTN_VIDEOVERSION)
+ {
+ OnVideoVersion();
+ }
else if (iControl == CONTROL_BTN_PLAY)
{
Play();
@@ -506,7 +511,7 @@ void CGUIDialogVideoInfo::Update()
}
// Check for resumability
- if (m_movieItem->GetVideoInfoTag()->GetResumePoint().timeInSeconds > 0.0)
+ if (m_movieItem->GetVideoInfoTag()->GetResumePoint().IsPartWay())
CONTROL_ENABLE(CONTROL_BTN_RESUME);
else
CONTROL_DISABLE(CONTROL_BTN_RESUME);
@@ -704,6 +709,42 @@ void CGUIDialogVideoInfo::ClearCastList()
m_castList->Clear();
}
+namespace
+{
+class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
+{
+public:
+ explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item)
+ : CVideoPlayActionProcessorBase(item)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ Play();
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ Play();
+ return true;
+ }
+
+private:
+ void Play()
+ {
+ m_item->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
+ const ContentUtils::PlayMode mode{m_item->GetProperty("CheckAutoPlayNextItem").asBoolean()
+ ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
+ : ContentUtils::PlayMode::PLAY_ONLY_THIS};
+ VIDEO_UTILS::PlayItem(m_item, "", mode);
+ }
+};
+} // unnamed namespace
+
void CGUIDialogVideoInfo::Play(bool resume)
{
std::string strPath;
@@ -748,30 +789,30 @@ void CGUIDialogVideoInfo::Play(bool resume)
if (resume)
{
- m_movieItem->SetStartOffset(STARTOFFSET_RESUME);
+ CVideoPlayActionProcessor proc{m_movieItem};
+ proc.ProcessAction(VIDEO::GUILIB::ACTION_RESUME);
}
else
{
- const SelectAction action = CVideoSelectActionProcessorBase::ChoosePlayOrResume(*m_movieItem);
- if (action == SELECT_ACTION_RESUME)
+ if (GetControl(CONTROL_BTN_RESUME))
{
- m_movieItem->SetStartOffset(STARTOFFSET_RESUME);
+ // if dialog has a resume button, play button has always the purpose to start from beginning
+ CVideoPlayActionProcessor proc{m_movieItem};
+ proc.ProcessAction(VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING);
}
- else if (action != SELECT_ACTION_PLAY)
+ else
{
- // The Resume dialog was closed without any choice
- SetMovie(m_movieItem.get()); // restore cast list, which was cleared on dialog close
- Open();
- return;
+ // play button acts according to default play action setting
+ CVideoPlayActionProcessor proc{m_movieItem};
+ proc.ProcessDefaultAction();
+ if (proc.UserCancelled())
+ {
+ // The Resume dialog was closed without any choice
+ SetMovie(m_movieItem.get()); // restore cast list, which was cleared on dialog close
+ Open();
+ }
}
}
-
- m_movieItem->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
-
- const ContentUtils::PlayMode mode = m_movieItem->GetProperty("CheckAutoPlayNextItem").asBoolean()
- ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
- : ContentUtils::PlayMode::PLAY_ONLY_THIS;
- VIDEO_UTILS::PlayItem(m_movieItem, mode);
}
namespace
@@ -1022,6 +1063,19 @@ int CGUIDialogVideoInfo::ManageVideoItem(const std::shared_ptr<CFileItem>& item)
// set or change movie set the movie belongs to
buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
+
+ if (!item->GetVideoInfoTag()->m_hasVideoVersions)
+ {
+ // set video version
+ buttons.Add(
+ CONTEXT_BUTTON_CONVERT_VIDEOVERSION,
+ StringUtils::Format(g_localizeStrings.Get(40021), CMediaTypes::GetLocalization(type)));
+ }
+
+ // manage video version
+ buttons.Add(
+ CONTEXT_BUTTON_MANAGE_VIDEOVERSION,
+ StringUtils::Format(g_localizeStrings.Get(40001), CMediaTypes::GetLocalization(type)));
}
if (type == MediaTypeEpisode &&
@@ -1093,6 +1147,15 @@ int CGUIDialogVideoInfo::ManageVideoItem(const std::shared_ptr<CFileItem>& item)
break;
}
+ case CONTEXT_BUTTON_CONVERT_VIDEOVERSION:
+ result = ConvertVideoVersion(item);
+ break;
+
+ case CONTEXT_BUTTON_MANAGE_VIDEOVERSION:
+ ManageVideoVersion(item);
+ result = true;
+ break;
+
case CONTEXT_BUTTON_UNLINK_BOOKMARK:
database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
result = true;
@@ -1217,10 +1280,8 @@ bool CGUIDialogVideoInfo::CanDeleteVideoItem(const std::shared_ptr<CFileItem>& i
CQueryParams params;
CVideoDatabaseDirectory::GetQueryParams(item->GetPath(), params);
- return params.GetMovieId() != -1 ||
- params.GetEpisodeId() != -1 ||
- params.GetMVideoId() != -1 ||
- params.GetSetId() != -1 ||
+ return params.GetMovieId() != -1 || params.GetEpisodeId() != -1 || params.GetMVideoId() != -1 ||
+ params.GetSetId() != -1 || params.GetVideoVersionId() != -1 ||
(params.GetTvShowId() != -1 && params.GetSeason() <= -1 &&
!CVideoDatabaseDirectory::IsAllItem(item->GetPath()));
}
@@ -2023,3 +2084,18 @@ void CGUIDialogVideoInfo::ShowFor(const CFileItem& item)
if (window)
window->OnItemInfo(item);
}
+
+void CGUIDialogVideoInfo::OnVideoVersion()
+{
+ CGUIDialogVideoVersion::ManageVideoVersion(m_movieItem);
+}
+
+bool CGUIDialogVideoInfo::ConvertVideoVersion(const std::shared_ptr<CFileItem>& item)
+{
+ return CGUIDialogVideoVersion::ConvertVideoVersion(item);
+}
+
+void CGUIDialogVideoInfo::ManageVideoVersion(const std::shared_ptr<CFileItem>& item)
+{
+ CGUIDialogVideoVersion::ManageVideoVersion(item);
+}
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.h b/xbmc/video/dialogs/GUIDialogVideoInfo.h
index c726003bea..e4b3d93a0e 100644
--- a/xbmc/video/dialogs/GUIDialogVideoInfo.h
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.h
@@ -51,6 +51,9 @@ public:
static bool GetSetForMovie(const CFileItem* movieItem, std::shared_ptr<CFileItem>& selectedSet);
static bool SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet);
+ static bool ConvertVideoVersion(const std::shared_ptr<CFileItem>& item);
+ static void ManageVideoVersion(const std::shared_ptr<CFileItem>& item);
+
static bool GetItemsForTag(const std::string &strHeading, const std::string &type, CFileItemList &items, int idTag = -1, bool showAll = true);
static bool AddItemsToTag(const std::shared_ptr<CFileItem>& tagItem);
static bool RemoveItemsFromTag(const std::shared_ptr<CFileItem>& tagItem);
@@ -86,6 +89,7 @@ protected:
* \param pItem Search result item
*/
void OnSearchItemFound(const CFileItem* pItem);
+ void OnVideoVersion();
void Play(bool resume = false);
void OnGetArt();
void OnGetFanart();
@@ -110,4 +114,5 @@ private:
static bool ManageVideoItemArtwork(const std::shared_ptr<CFileItem>& item,
const MediaType& mediaType,
const std::string& artType);
+ bool ChooseVideoVersion();
};
diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
index 31dc9708cb..168dee89c1 100644
--- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp
@@ -332,25 +332,22 @@ void CGUIDialogVideoSettings::InitializeSettings()
TranslatableIntegerSettingOptions entries;
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(16039, VS_INTERLACEMETHOD_NONE));
- entries.push_back(TranslatableIntegerSettingOption(16019, VS_INTERLACEMETHOD_AUTO));
- entries.push_back(TranslatableIntegerSettingOption(20131, VS_INTERLACEMETHOD_RENDER_BLEND));
- entries.push_back(TranslatableIntegerSettingOption(20129, VS_INTERLACEMETHOD_RENDER_WEAVE));
- entries.push_back(TranslatableIntegerSettingOption(16021, VS_INTERLACEMETHOD_RENDER_BOB));
- entries.push_back(TranslatableIntegerSettingOption(16020, VS_INTERLACEMETHOD_DEINTERLACE));
- entries.push_back(TranslatableIntegerSettingOption(16036, VS_INTERLACEMETHOD_DEINTERLACE_HALF));
- entries.push_back(
- TranslatableIntegerSettingOption(16311, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL));
- entries.push_back(TranslatableIntegerSettingOption(16310, VS_INTERLACEMETHOD_VDPAU_TEMPORAL));
- entries.push_back(TranslatableIntegerSettingOption(16325, VS_INTERLACEMETHOD_VDPAU_BOB));
- entries.push_back(
- TranslatableIntegerSettingOption(16318, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF));
- entries.push_back(
- TranslatableIntegerSettingOption(16317, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF));
- entries.push_back(TranslatableIntegerSettingOption(16327, VS_INTERLACEMETHOD_VAAPI_BOB));
- entries.push_back(TranslatableIntegerSettingOption(16328, VS_INTERLACEMETHOD_VAAPI_MADI));
- entries.push_back(TranslatableIntegerSettingOption(16329, VS_INTERLACEMETHOD_VAAPI_MACI));
- entries.push_back(TranslatableIntegerSettingOption(16320, VS_INTERLACEMETHOD_DXVA_AUTO));
+ entries.emplace_back(16039, VS_INTERLACEMETHOD_NONE);
+ entries.emplace_back(16019, VS_INTERLACEMETHOD_AUTO);
+ entries.emplace_back(20131, VS_INTERLACEMETHOD_RENDER_BLEND);
+ entries.emplace_back(20129, VS_INTERLACEMETHOD_RENDER_WEAVE);
+ entries.emplace_back(16021, VS_INTERLACEMETHOD_RENDER_BOB);
+ entries.emplace_back(16020, VS_INTERLACEMETHOD_DEINTERLACE);
+ entries.emplace_back(16036, VS_INTERLACEMETHOD_DEINTERLACE_HALF);
+ entries.emplace_back(16311, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL);
+ entries.emplace_back(16310, VS_INTERLACEMETHOD_VDPAU_TEMPORAL);
+ entries.emplace_back(16325, VS_INTERLACEMETHOD_VDPAU_BOB);
+ entries.emplace_back(16318, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF);
+ entries.emplace_back(16317, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF);
+ entries.emplace_back(16327, VS_INTERLACEMETHOD_VAAPI_BOB);
+ entries.emplace_back(16328, VS_INTERLACEMETHOD_VAAPI_MADI);
+ entries.emplace_back(16329, VS_INTERLACEMETHOD_VAAPI_MACI);
+ entries.emplace_back(16320, VS_INTERLACEMETHOD_DXVA_AUTO);
/* remove unsupported methods */
for (TranslatableIntegerSettingOptions::iterator it = entries.begin(); it != entries.end(); )
@@ -372,25 +369,25 @@ void CGUIDialogVideoSettings::InitializeSettings()
}
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(16301, VS_SCALINGMETHOD_NEAREST));
- entries.push_back(TranslatableIntegerSettingOption(16302, VS_SCALINGMETHOD_LINEAR));
- entries.push_back(TranslatableIntegerSettingOption(16303, VS_SCALINGMETHOD_CUBIC_B_SPLINE));
- entries.push_back(TranslatableIntegerSettingOption(16314, VS_SCALINGMETHOD_CUBIC_MITCHELL));
- entries.push_back(TranslatableIntegerSettingOption(16321, VS_SCALINGMETHOD_CUBIC_CATMULL));
- entries.push_back(TranslatableIntegerSettingOption(16326, VS_SCALINGMETHOD_CUBIC_0_075));
- entries.push_back(TranslatableIntegerSettingOption(16330, VS_SCALINGMETHOD_CUBIC_0_1));
- entries.push_back(TranslatableIntegerSettingOption(16304, VS_SCALINGMETHOD_LANCZOS2));
- entries.push_back(TranslatableIntegerSettingOption(16323, VS_SCALINGMETHOD_SPLINE36_FAST));
- entries.push_back(TranslatableIntegerSettingOption(16315, VS_SCALINGMETHOD_LANCZOS3_FAST));
- entries.push_back(TranslatableIntegerSettingOption(16322, VS_SCALINGMETHOD_SPLINE36));
- entries.push_back(TranslatableIntegerSettingOption(16305, VS_SCALINGMETHOD_LANCZOS3));
- entries.push_back(TranslatableIntegerSettingOption(16306, VS_SCALINGMETHOD_SINC8));
- entries.push_back(TranslatableIntegerSettingOption(16307, VS_SCALINGMETHOD_BICUBIC_SOFTWARE));
- entries.push_back(TranslatableIntegerSettingOption(16308, VS_SCALINGMETHOD_LANCZOS_SOFTWARE));
- entries.push_back(TranslatableIntegerSettingOption(16309, VS_SCALINGMETHOD_SINC_SOFTWARE));
- entries.push_back(TranslatableIntegerSettingOption(13120, VS_SCALINGMETHOD_VDPAU_HARDWARE));
- entries.push_back(TranslatableIntegerSettingOption(16319, VS_SCALINGMETHOD_DXVA_HARDWARE));
- entries.push_back(TranslatableIntegerSettingOption(16316, VS_SCALINGMETHOD_AUTO));
+ entries.emplace_back(16301, VS_SCALINGMETHOD_NEAREST);
+ entries.emplace_back(16302, VS_SCALINGMETHOD_LINEAR);
+ entries.emplace_back(16303, VS_SCALINGMETHOD_CUBIC_B_SPLINE);
+ entries.emplace_back(16314, VS_SCALINGMETHOD_CUBIC_MITCHELL);
+ entries.emplace_back(16321, VS_SCALINGMETHOD_CUBIC_CATMULL);
+ entries.emplace_back(16326, VS_SCALINGMETHOD_CUBIC_0_075);
+ entries.emplace_back(16330, VS_SCALINGMETHOD_CUBIC_0_1);
+ entries.emplace_back(16304, VS_SCALINGMETHOD_LANCZOS2);
+ entries.emplace_back(16323, VS_SCALINGMETHOD_SPLINE36_FAST);
+ entries.emplace_back(16315, VS_SCALINGMETHOD_LANCZOS3_FAST);
+ entries.emplace_back(16322, VS_SCALINGMETHOD_SPLINE36);
+ entries.emplace_back(16305, VS_SCALINGMETHOD_LANCZOS3);
+ entries.emplace_back(16306, VS_SCALINGMETHOD_SINC8);
+ entries.emplace_back(16307, VS_SCALINGMETHOD_BICUBIC_SOFTWARE);
+ entries.emplace_back(16308, VS_SCALINGMETHOD_LANCZOS_SOFTWARE);
+ entries.emplace_back(16309, VS_SCALINGMETHOD_SINC_SOFTWARE);
+ entries.emplace_back(13120, VS_SCALINGMETHOD_VDPAU_HARDWARE);
+ entries.emplace_back(16319, VS_SCALINGMETHOD_DXVA_HARDWARE);
+ entries.emplace_back(16316, VS_SCALINGMETHOD_AUTO);
/* remove unsupported methods */
for(TranslatableIntegerSettingOptions::iterator it = entries.begin(); it != entries.end(); )
@@ -443,10 +440,10 @@ void CGUIDialogVideoSettings::InitializeSettings()
{
const bool visible = !CServiceBroker::GetWinSystem()->IsHDRDisplaySettingEnabled();
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(36554, VS_TONEMAPMETHOD_OFF));
- entries.push_back(TranslatableIntegerSettingOption(36555, VS_TONEMAPMETHOD_REINHARD));
- entries.push_back(TranslatableIntegerSettingOption(36557, VS_TONEMAPMETHOD_ACES));
- entries.push_back(TranslatableIntegerSettingOption(36558, VS_TONEMAPMETHOD_HABLE));
+ entries.emplace_back(36554, VS_TONEMAPMETHOD_OFF);
+ entries.emplace_back(36555, VS_TONEMAPMETHOD_REINHARD);
+ entries.emplace_back(36557, VS_TONEMAPMETHOD_ACES);
+ entries.emplace_back(36558, VS_TONEMAPMETHOD_HABLE);
AddSpinner(groupVideo, SETTING_VIDEO_TONEMAP_METHOD, 36553, SettingLevel::Basic,
videoSettings.m_ToneMapMethod, entries, false, visible);
@@ -457,9 +454,9 @@ void CGUIDialogVideoSettings::InitializeSettings()
// stereoscopic settings
entries.clear();
- entries.push_back(TranslatableIntegerSettingOption(16316, RENDER_STEREO_MODE_OFF));
- entries.push_back(TranslatableIntegerSettingOption(36503, RENDER_STEREO_MODE_SPLIT_HORIZONTAL));
- entries.push_back(TranslatableIntegerSettingOption(36504, RENDER_STEREO_MODE_SPLIT_VERTICAL));
+ entries.emplace_back(16316, RENDER_STEREO_MODE_OFF);
+ entries.emplace_back(36503, RENDER_STEREO_MODE_SPLIT_HORIZONTAL);
+ entries.emplace_back(36504, RENDER_STEREO_MODE_SPLIT_VERTICAL);
AddSpinner(groupStereoscopic, SETTING_VIDEO_STEREOSCOPICMODE, 36535, SettingLevel::Basic, videoSettings.m_StereoMode, entries);
AddToggle(groupStereoscopic, SETTING_VIDEO_STEREOSCOPICINVERT, 36536, SettingLevel::Basic, videoSettings.m_StereoInvert);
diff --git a/xbmc/video/dialogs/GUIDialogVideoVersion.cpp b/xbmc/video/dialogs/GUIDialogVideoVersion.cpp
new file mode 100644
index 0000000000..800d4ff311
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoVersion.cpp
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (C) 2005-2023 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 "GUIDialogVideoVersion.h"
+
+#include "FileItem.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "cores/VideoPlayer/DVDFileInfo.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogOK.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindow.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "playlists/PlayListTypes.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/FileExtensionProvider.h"
+#include "utils/SortUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "video/VideoInfoTag.h"
+#include "video/VideoThumbLoader.h"
+#include "video/VideoUtils.h"
+#include "video/dialogs/GUIDialogVideoInfo.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
+
+#include <algorithm>
+#include <string>
+
+static constexpr unsigned int CONTROL_LABEL_MODE = 1;
+static constexpr unsigned int CONTROL_LABEL_TITLE = 2;
+static constexpr unsigned int CONTROL_IMAGE_THUMB = 3;
+
+static constexpr unsigned int CONTROL_BUTTON_PLAY = 21;
+static constexpr unsigned int CONTROL_BUTTON_ADD_VERSION = 22;
+static constexpr unsigned int CONTROL_BUTTON_ADD_EXTRAS = 23;
+static constexpr unsigned int CONTROL_BUTTON_RENAME = 24;
+static constexpr unsigned int CONTROL_BUTTON_SET_DEFAULT = 25;
+static constexpr unsigned int CONTROL_BUTTON_REMOVE = 26;
+static constexpr unsigned int CONTROL_BUTTON_CHOOSE_ART = 27;
+
+static constexpr unsigned int CONTROL_LIST_PRIMARY_VERSION = 50;
+static constexpr unsigned int CONTROL_LIST_EXTRAS_VERSION = 51;
+
+CGUIDialogVideoVersion::CGUIDialogVideoVersion(int id)
+ : CGUIDialog(id, "DialogVideoVersion.xml"),
+ m_videoItem(std::make_shared<CFileItem>()),
+ m_primaryVideoVersionList(std::make_unique<CFileItemList>()),
+ m_extrasVideoVersionList(std::make_unique<CFileItemList>()),
+ m_defaultVideoVersion(std::make_unique<CFileItem>()),
+ m_selectedVideoVersion(std::make_unique<CFileItem>())
+{
+ m_loadType = KEEP_IN_MEMORY;
+
+ if (!m_database.Open())
+ CLog::Log(LOGERROR, "{}: Failed to open database", __FUNCTION__);
+}
+
+CGUIDialogVideoVersion::~CGUIDialogVideoVersion()
+{
+}
+
+bool CGUIDialogVideoVersion::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_INIT:
+ {
+ m_cancelled = false;
+ break;
+ }
+
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ ClearVideoVersionList();
+ break;
+ }
+
+ case GUI_MSG_CLICKED:
+ {
+ const int control = message.GetSenderId();
+ if (control == CONTROL_LIST_PRIMARY_VERSION)
+ {
+ const int action = message.GetParam1();
+ if (action == ACTION_SELECT_ITEM || action == ACTION_MOUSE_LEFT_CLICK)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), control);
+ OnMessage(msg);
+
+ const int item = msg.GetParam1();
+ if (item < 0 || item >= m_primaryVideoVersionList->Size())
+ break;
+
+ *m_selectedVideoVersion = *m_primaryVideoVersionList->Get(item);
+
+ if (m_selectedVideoVersion->GetVideoInfoTag()->m_iDbId ==
+ m_defaultVideoVersion->GetVideoInfoTag()->m_iDbId)
+ {
+ CONTROL_DISABLE(CONTROL_BUTTON_REMOVE);
+ CONTROL_DISABLE(CONTROL_BUTTON_SET_DEFAULT);
+ }
+ else
+ {
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_REMOVE, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_RENAME, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_SET_DEFAULT, m_mode == Mode::MANAGE);
+ }
+
+ if (m_mode == Mode::MANAGE)
+ SET_CONTROL_FOCUS(CONTROL_BUTTON_PLAY, 0);
+ else
+ CloseAll();
+ }
+ }
+ else if (control == CONTROL_LIST_EXTRAS_VERSION)
+ {
+ const int action = message.GetParam1();
+ if (action == ACTION_SELECT_ITEM || action == ACTION_MOUSE_LEFT_CLICK)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), control);
+ OnMessage(msg);
+
+ const int item = msg.GetParam1();
+ if (item < 0 || item >= m_extrasVideoVersionList->Size())
+ break;
+
+ *m_selectedVideoVersion = *m_extrasVideoVersionList->Get(item).get();
+
+ CONTROL_DISABLE(CONTROL_BUTTON_SET_DEFAULT);
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_REMOVE, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_RENAME, m_mode == Mode::MANAGE);
+
+ if (m_mode == Mode::MANAGE)
+ SET_CONTROL_FOCUS(CONTROL_BUTTON_PLAY, 0);
+ else
+ CloseAll();
+ }
+ }
+ else if (control == CONTROL_BUTTON_PLAY)
+ {
+ Play();
+ }
+ else if (control == CONTROL_BUTTON_ADD_VERSION)
+ {
+ AddVersion();
+ }
+ else if (control == CONTROL_BUTTON_ADD_EXTRAS)
+ {
+ AddExtras();
+ }
+ else if (control == CONTROL_BUTTON_RENAME)
+ {
+ Rename();
+ }
+ else if (control == CONTROL_BUTTON_SET_DEFAULT)
+ {
+ SetDefault();
+ }
+ else if (control == CONTROL_BUTTON_REMOVE)
+ {
+ Remove();
+ }
+ else if (control == CONTROL_BUTTON_CHOOSE_ART)
+ {
+ ChooseArt();
+ }
+
+ break;
+ }
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogVideoVersion::OnBack(int actionID)
+{
+ m_cancelled = true;
+ return CGUIDialog::OnBack(actionID);
+}
+
+void CGUIDialogVideoVersion::OnInitWindow()
+{
+ // set working mode
+ SET_CONTROL_LABEL(CONTROL_LABEL_MODE, m_mode == Mode::MANAGE ? "manage" : "choose");
+
+ // set window title
+ std::string title = m_videoItem->GetVideoInfoTag()->GetTitle();
+
+ const int year = m_videoItem->GetVideoInfoTag()->GetYear();
+ if (year != 0)
+ title = StringUtils::Format("{} ({})", title, year);
+
+ SET_CONTROL_LABEL(
+ CONTROL_LABEL_TITLE,
+ StringUtils::Format(g_localizeStrings.Get(m_mode == Mode::MANAGE ? 40022 : 40023), title));
+
+ // bind primary and extras version lists
+ CGUIMessage msg1(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_PRIMARY_VERSION, 0, 0,
+ m_primaryVideoVersionList.get());
+ OnMessage(msg1);
+
+ CGUIMessage msg2(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_EXTRAS_VERSION, 0, 0,
+ m_extrasVideoVersionList.get());
+ OnMessage(msg2);
+
+ // disable buttons that need a selected video version
+ CONTROL_DISABLE(CONTROL_BUTTON_REMOVE);
+ CONTROL_DISABLE(CONTROL_BUTTON_SET_DEFAULT);
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_ADD_VERSION, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_ADD_EXTRAS, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_CHOOSE_ART, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_RENAME, m_mode == Mode::MANAGE);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BUTTON_PLAY, m_mode == Mode::MANAGE);
+
+ CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogVideoVersion::ClearVideoVersionList()
+{
+ CGUIMessage msg1(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST_PRIMARY_VERSION);
+ OnMessage(msg1);
+ m_primaryVideoVersionList->Clear();
+
+ CGUIMessage msg2(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST_EXTRAS_VERSION);
+ OnMessage(msg2);
+ m_extrasVideoVersionList->Clear();
+}
+
+void CGUIDialogVideoVersion::RefreshVideoVersionList()
+{
+ // clear current version list
+ ClearVideoVersionList();
+
+ const int dbId = m_videoItem->GetVideoInfoTag()->m_iDbId;
+ MediaType mediaType = m_videoItem->GetVideoInfoTag()->m_type;
+ VideoDbContentType itemType = m_videoItem->GetVideoContentType();
+
+ // get primary version list
+ m_database.GetVideoVersions(itemType, dbId, *m_primaryVideoVersionList,
+ VideoVersionItemType::PRIMARY);
+ m_primaryVideoVersionList->SetContent(CMediaTypes::ToPlural(mediaType));
+
+ // refresh primary version list
+ CGUIMessage msg1(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_PRIMARY_VERSION, 0, 0,
+ m_primaryVideoVersionList.get());
+ OnMessage(msg1);
+
+ // get extras version list
+ m_database.GetVideoVersions(itemType, dbId, *m_extrasVideoVersionList,
+ VideoVersionItemType::EXTRAS);
+ m_extrasVideoVersionList->SetContent(CMediaTypes::ToPlural(mediaType));
+
+ // refresh extras version list
+ CGUIMessage msg2(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_EXTRAS_VERSION, 0, 0,
+ m_extrasVideoVersionList.get());
+ OnMessage(msg2);
+
+ // update default video version
+ m_database.GetDefaultVideoVersion(itemType, dbId, *m_selectedVideoVersion.get());
+}
+
+void CGUIDialogVideoVersion::SetVideoItem(const std::shared_ptr<CFileItem>& item)
+{
+ if (item == nullptr || !item->HasVideoInfoTag() ||
+ item->GetVideoInfoTag()->m_type != MediaTypeMovie)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogVideoVersion: Unexpected video item");
+ return;
+ }
+
+ m_videoItem = item;
+
+ ClearVideoVersionList();
+
+ const int dbId = item->GetVideoInfoTag()->m_iDbId;
+ MediaType mediaType = item->GetVideoInfoTag()->m_type;
+ VideoDbContentType itemType = item->GetVideoContentType();
+
+ m_database.GetVideoVersions(itemType, dbId, *m_primaryVideoVersionList,
+ VideoVersionItemType::PRIMARY);
+ m_primaryVideoVersionList->SetContent(CMediaTypes::ToPlural(mediaType));
+
+ m_database.GetVideoVersions(itemType, dbId, *m_extrasVideoVersionList,
+ VideoVersionItemType::EXTRAS);
+ m_extrasVideoVersionList->SetContent(CMediaTypes::ToPlural(mediaType));
+
+ m_database.GetDefaultVideoVersion(itemType, dbId, *m_defaultVideoVersion);
+ m_database.GetDefaultVideoVersion(itemType, dbId, *m_selectedVideoVersion);
+
+ CVideoThumbLoader loader;
+
+ for (auto& item : *m_primaryVideoVersionList)
+ loader.LoadItem(item.get());
+
+ for (auto& item : *m_extrasVideoVersionList)
+ loader.LoadItem(item.get());
+}
+
+void CGUIDialogVideoVersion::CloseAll()
+{
+ // close our dialog
+ Close(true);
+
+ // close the video info dialog if exists
+ CGUIDialogVideoInfo* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVideoInfo>(
+ WINDOW_DIALOG_VIDEO_INFO);
+ if (dialog)
+ dialog->Close(true);
+}
+
+namespace
+{
+class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
+{
+public:
+ explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item,
+ const std::shared_ptr<CFileItem>& videoVersion)
+ : CVideoPlayActionProcessorBase(item, videoVersion)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ Play();
+ return true;
+ }
+
+ bool OnPlaySelected() override
+ {
+ Play();
+ return true;
+ }
+
+private:
+ void Play()
+ {
+ m_item->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
+ const ContentUtils::PlayMode mode{m_item->GetProperty("CheckAutoPlayNextItem").asBoolean()
+ ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
+ : ContentUtils::PlayMode::PLAY_ONLY_THIS};
+ VIDEO_UTILS::PlayItem(m_item, "", mode);
+ }
+};
+} // unnamed namespace
+
+void CGUIDialogVideoVersion::Play()
+{
+ CloseAll();
+
+ CVideoPlayActionProcessor proc{m_videoItem, m_selectedVideoVersion};
+ proc.ProcessDefaultAction();
+}
+
+void CGUIDialogVideoVersion::Remove()
+{
+ MediaType mediaType = m_videoItem->GetVideoInfoTag()->m_type;
+
+ // default video version is not allowed
+ if (m_database.IsDefaultVideoVersion(m_selectedVideoVersion->GetVideoInfoTag()->m_iDbId))
+ {
+ CGUIDialogOK::ShowAndGetInput(
+ CVariant(40018),
+ StringUtils::Format(g_localizeStrings.Get(40019),
+ m_selectedVideoVersion->GetVideoInfoTag()->m_typeVideoVersion,
+ CMediaTypes::GetLocalization(mediaType),
+ m_videoItem->GetVideoInfoTag()->GetTitle()));
+ return;
+ }
+
+ // confirm to remove
+ if (!CGUIDialogYesNo::ShowAndGetInput(
+ CVariant(40018),
+ StringUtils::Format(g_localizeStrings.Get(40020),
+ m_selectedVideoVersion->GetVideoInfoTag()->m_typeVideoVersion)))
+ {
+ return;
+ }
+
+ // remove video version
+ m_database.RemoveVideoVersion(m_selectedVideoVersion->GetVideoInfoTag()->m_iDbId);
+
+ // refresh the video version list
+ RefreshVideoVersionList();
+
+ // select the default video version
+ const int dbId = m_videoItem->GetVideoInfoTag()->m_iDbId;
+ VideoDbContentType itemType = m_videoItem->GetVideoContentType();
+
+ m_database.GetDefaultVideoVersion(itemType, dbId, *m_selectedVideoVersion.get());
+
+ CONTROL_DISABLE(CONTROL_BUTTON_REMOVE);
+ CONTROL_DISABLE(CONTROL_BUTTON_SET_DEFAULT);
+}
+
+void CGUIDialogVideoVersion::Rename()
+{
+ const int idVideoVersion = SelectVideoVersion(m_videoItem);
+ if (idVideoVersion != -1)
+ {
+ m_database.SetVideoVersion(m_selectedVideoVersion->GetVideoInfoTag()->m_iDbId, idVideoVersion);
+ }
+
+ // refresh the video version list
+ RefreshVideoVersionList();
+}
+
+void CGUIDialogVideoVersion::SetDefault()
+{
+ // set the selected video version as default
+ SetDefaultVideoVersion(*m_selectedVideoVersion.get());
+
+ const int dbId = m_videoItem->GetVideoInfoTag()->m_iDbId;
+ VideoDbContentType itemType = m_videoItem->GetVideoContentType();
+
+ // update our default video version
+ m_database.GetDefaultVideoVersion(itemType, dbId, *m_defaultVideoVersion.get());
+
+ CONTROL_DISABLE(CONTROL_BUTTON_SET_DEFAULT);
+}
+
+void CGUIDialogVideoVersion::SetDefaultVideoVersion(CFileItem& version)
+{
+ const int dbId = m_videoItem->GetVideoInfoTag()->m_iDbId;
+ VideoDbContentType itemType = m_videoItem->GetVideoContentType();
+
+ // set the specified video version as default
+ m_database.SetDefaultVideoVersion(itemType, dbId, version.GetVideoInfoTag()->m_iDbId);
+
+ // update the video item
+ m_videoItem->SetPath(version.GetPath());
+ m_videoItem->SetDynPath(version.GetPath());
+
+ // update video details since we changed the video file for the item
+ m_database.GetDetailsByTypeAndId(*m_videoItem, itemType, dbId);
+
+ // notify all windows to update the file item
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, GUI_MSG_FLAG_FORCE_UPDATE,
+ m_videoItem);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+}
+
+void CGUIDialogVideoVersion::ChooseArt()
+{
+ if (!CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(
+ std::make_shared<CFileItem>(*m_selectedVideoVersion)))
+ return;
+
+ // update the thumbnail
+ CGUIControl* control = GetControl(CONTROL_IMAGE_THUMB);
+ if (control)
+ {
+ CGUIImage* imageControl = static_cast<CGUIImage*>(control);
+ imageControl->FreeResources();
+ imageControl->SetFileName(m_selectedVideoVersion->GetArt("thumb"));
+ }
+
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+}
+
+void CGUIDialogVideoVersion::AddVersion()
+{
+ AddVideoVersion(true);
+}
+
+void CGUIDialogVideoVersion::AddExtras()
+{
+ AddVideoVersion(false);
+}
+
+void CGUIDialogVideoVersion::AddVideoVersion(bool primary)
+{
+ const int dbId = m_videoItem->GetVideoInfoTag()->m_iDbId;
+ MediaType mediaType = m_videoItem->GetVideoInfoTag()->m_type;
+ VideoDbContentType itemType = m_videoItem->GetVideoContentType();
+
+ std::string title = primary ? StringUtils::Format(g_localizeStrings.Get(40014),
+ CMediaTypes::GetLocalization(mediaType))
+ : StringUtils::Format(g_localizeStrings.Get(40015),
+ CMediaTypes::GetLocalization(mediaType));
+
+ // prompt to choose a video file
+ VECSOURCES sources = *CMediaSourceSettings::GetInstance().GetSources("files");
+
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(sources);
+
+ std::string path;
+ if (CGUIDialogFileBrowser::ShowAndGetFile(
+ sources, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), title, path))
+ {
+ std::string typeVideoVersion;
+ std::string videoTitle;
+ int idFile{-1};
+ int idMedia{-1};
+ MediaType itemMediaType;
+ VideoVersionItemType versionItemType{VideoVersionItemType::UNKNOWN};
+
+ const int idVideoVersion = m_database.GetVideoVersionInfo(
+ path, idFile, typeVideoVersion, idMedia, itemMediaType, versionItemType);
+
+ if (idVideoVersion != -1)
+ {
+ CFileItemList versions;
+ m_database.GetVideoVersions(itemType, dbId, versions);
+ if (std::any_of(versions.begin(), versions.end(),
+ [idFile](const std::shared_ptr<CFileItem>& version)
+ { return version->GetVideoInfoTag()->m_iDbId == idFile; }))
+ {
+ CGUIDialogOK::ShowAndGetInput(
+ title, StringUtils::Format(g_localizeStrings.Get(40016), typeVideoVersion,
+ CMediaTypes::GetLocalization(mediaType)));
+ return;
+ }
+
+ if (itemMediaType == MediaTypeMovie)
+ {
+ videoTitle = m_database.GetMovieTitle(idMedia);
+ }
+ else
+ return;
+
+ if (!CGUIDialogYesNo::ShowAndGetInput(
+ title, StringUtils::Format(g_localizeStrings.Get(40017), typeVideoVersion,
+ CMediaTypes::GetLocalization(mediaType), videoTitle,
+ CMediaTypes::GetLocalization(mediaType))))
+ {
+ return;
+ }
+
+ if (m_database.IsDefaultVideoVersion(idFile))
+ {
+ CFileItemList list;
+ m_database.GetVideoVersions(itemType, idMedia, list);
+
+ if (list.Size() > 1)
+ {
+ CGUIDialogOK::ShowAndGetInput(
+ title, StringUtils::Format(g_localizeStrings.Get(40019), typeVideoVersion,
+ CMediaTypes::GetLocalization(mediaType), videoTitle));
+ return;
+ }
+ else
+ {
+ if (itemMediaType == MediaTypeMovie)
+ {
+ m_database.DeleteMovie(idMedia);
+ }
+ else
+ return;
+ }
+ }
+ else
+ m_database.RemoveVideoVersion(idFile);
+ }
+
+ CFileItem item(path, false);
+
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_MYVIDEOS_EXTRACTFLAGS))
+ {
+ CDVDFileInfo::GetFileStreamDetails(&item);
+ CLog::Log(LOGDEBUG, "VideoVersion: Extracted filestream details from video file {}",
+ CURL::GetRedacted(item.GetPath()));
+ }
+
+ if (primary)
+ {
+ const int idVideoVersion = SelectVideoVersion(m_videoItem);
+ if (idVideoVersion != -1)
+ {
+ m_database.AddPrimaryVideoVersion(itemType, dbId, idVideoVersion, item);
+ }
+ }
+ else
+ {
+ std::string typeVideoVersion =
+ CGUIDialogVideoVersion::GenerateExtrasVideoVersion(URIUtils::GetFileName(path));
+
+ const int idVideoVersion =
+ m_database.AddVideoVersionType(typeVideoVersion, VideoVersionTypeOwner::AUTO);
+
+ m_database.AddExtrasVideoVersion(itemType, dbId, idVideoVersion, item);
+ }
+
+ // refresh the video version list
+ RefreshVideoVersionList();
+ }
+}
+
+std::tuple<int, std::string> CGUIDialogVideoVersion::NewVideoVersion()
+{
+ std::string typeVideoVersion;
+
+ // prompt for the new video version
+ if (!CGUIKeyboardFactory::ShowAndGetInput(typeVideoVersion,
+ CVariant{g_localizeStrings.Get(40004)}, false))
+ return std::make_tuple(-1, "");
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ {
+ CLog::Log(LOGERROR, "{}: Failed to open database", __FUNCTION__);
+ return std::make_tuple(-1, "");
+ }
+
+ typeVideoVersion = StringUtils::Trim(typeVideoVersion);
+ const int idVideoVersion =
+ videodb.AddVideoVersionType(typeVideoVersion, VideoVersionTypeOwner::USER);
+
+ return std::make_tuple(idVideoVersion, typeVideoVersion);
+}
+
+void CGUIDialogVideoVersion::SetSelectedVideoVersion(const std::shared_ptr<CFileItem>& version)
+{
+ m_selectedVideoVersion = std::make_unique<CFileItem>(*version);
+}
+
+void CGUIDialogVideoVersion::ManageVideoVersion(const std::shared_ptr<CFileItem>& item)
+{
+ CGUIDialogVideoVersion* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVideoVersion>(
+ WINDOW_DIALOG_VIDEO_VERSION);
+ if (!dialog)
+ {
+ CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_VIDEO_VERSION instance!");
+ return;
+ }
+
+ dialog->SetVideoItem(item);
+ dialog->SetMode(Mode::MANAGE);
+ dialog->Open();
+}
+
+CGUIDialogVideoVersion::VersionSelectResult CGUIDialogVideoVersion::ChooseVideoVersion(
+ const std::shared_ptr<CFileItem>& item)
+{
+ if (!item->HasVideoInfoTag())
+ {
+ CLog::LogF(LOGWARNING, "Item is not a video. path={}", item->GetPath());
+ return {true, {}};
+ }
+
+ if (!item->HasVideoVersions())
+ {
+ CLog::LogF(LOGWARNING, "Item has no video versions. path={}", item->GetPath());
+ return {true, {}};
+ }
+
+ // prompt to select a video version
+ CGUIDialogVideoVersion* dialog{
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVideoVersion>(
+ WINDOW_DIALOG_VIDEO_VERSION_SELECT)};
+ if (!dialog)
+ {
+ CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_VIDEO_VERSION_SELECT instance!");
+ return {true, {}};
+ }
+
+ dialog->SetVideoItem(item);
+ dialog->SetMode(Mode::CHOOSE);
+ dialog->Open();
+
+ // get the selected video version from dialog if not cancelled
+ return {dialog->m_cancelled, dialog->m_selectedVideoVersion};
+}
+
+int CGUIDialogVideoVersion::ManageVideoVersionContextMenu(const std::shared_ptr<CFileItem>& version)
+{
+ CContextButtons buttons;
+
+ buttons.Add(CONTEXT_BUTTON_RENAME, 118);
+ buttons.Add(CONTEXT_BUTTON_SET_DEFAULT, 31614);
+ buttons.Add(CONTEXT_BUTTON_DELETE, 15015);
+ buttons.Add(CONTEXT_BUTTON_SET_ART, 13511);
+
+ int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
+ if (button > 0)
+ {
+ CGUIDialogVideoVersion* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVideoVersion>(
+ WINDOW_DIALOG_VIDEO_VERSION);
+ if (!dialog)
+ return -1;
+
+ CFileItem videoItem;
+ if (!dialog->m_database.GetVideoItemByVideoVersion(version->GetVideoInfoTag()->m_iDbId,
+ videoItem))
+ return -1;
+
+ dialog->SetVideoItem(std::make_shared<CFileItem>(videoItem));
+ dialog->SetMode(Mode::MANAGE);
+ dialog->SetSelectedVideoVersion(version);
+
+ switch (static_cast<CONTEXT_BUTTON>(button))
+ {
+ case CONTEXT_BUTTON_RENAME:
+ {
+ dialog->Rename();
+ break;
+ }
+ case CONTEXT_BUTTON_SET_DEFAULT:
+ {
+ dialog->SetDefault();
+ break;
+ }
+ case CONTEXT_BUTTON_DELETE:
+ {
+ dialog->Remove();
+ break;
+ }
+ case CONTEXT_BUTTON_SET_ART:
+ {
+ dialog->ChooseArt();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return button;
+}
+
+int CGUIDialogVideoVersion::SelectVideoVersion(const std::shared_ptr<CFileItem>& item)
+{
+ if (!item || !item->HasVideoInfoTag())
+ return -1;
+
+ VideoDbContentType itemType = item->GetVideoContentType();
+ if (itemType != VideoDbContentType::MOVIES)
+ return -1;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ {
+ CLog::Log(LOGERROR, "{}: Failed to open database", __FUNCTION__);
+ return -1;
+ }
+
+ int idVideoVersion = -1;
+ std::string typeVideoVersion;
+
+ CFileItemList list;
+ videodb.GetVideoVersionTypes(itemType, list);
+
+ const std::string mediaType = item->GetVideoInfoTag()->m_type;
+ const std::string title =
+ StringUtils::Format(g_localizeStrings.Get(40003), CMediaTypes::GetLocalization(mediaType));
+
+ while (true)
+ {
+ CGUIDialogSelect* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ if (!dialog)
+ return -1;
+
+ dialog->Reset();
+ dialog->SetItems(list);
+ dialog->SetHeading(title);
+ dialog->EnableButton(true, 40004);
+ dialog->Open();
+
+ if (dialog->IsButtonPressed())
+ {
+ // create a new video version
+ if (CGUIKeyboardFactory::ShowAndGetInput(typeVideoVersion, g_localizeStrings.Get(40004),
+ false))
+ {
+ typeVideoVersion = StringUtils::Trim(typeVideoVersion);
+ idVideoVersion = videodb.AddVideoVersionType(typeVideoVersion, VideoVersionTypeOwner::USER);
+ }
+ }
+ else if (dialog->IsConfirmed())
+ {
+ std::shared_ptr<CFileItem> selectedItem = dialog->GetSelectedFileItem();
+ idVideoVersion = selectedItem->GetVideoInfoTag()->m_idVideoVersion;
+ typeVideoVersion = selectedItem->GetVideoInfoTag()->m_typeVideoVersion;
+ }
+ else
+ return -1;
+
+ if (idVideoVersion < 0)
+ return -1;
+
+ const int dbId = item->GetVideoInfoTag()->m_iDbId;
+
+ CFileItemList versions;
+ videodb.GetVideoVersions(itemType, dbId, versions);
+
+ // the selected video version already exists
+ if (std::any_of(versions.begin(), versions.end(),
+ [idVideoVersion](const std::shared_ptr<CFileItem>& version)
+ { return version->GetVideoInfoTag()->m_iDbId == idVideoVersion; }))
+ {
+ CGUIDialogOK::ShowAndGetInput(
+ CVariant{40005}, StringUtils::Format(g_localizeStrings.Get(40007), typeVideoVersion));
+ }
+ else
+ break;
+ }
+
+ return idVideoVersion;
+}
+
+bool CGUIDialogVideoVersion::ConvertVideoVersion(const std::shared_ptr<CFileItem>& item)
+{
+ if (item == nullptr || !item->HasVideoInfoTag())
+ return false;
+
+ VideoDbContentType itemType = item->GetVideoContentType();
+ if (itemType != VideoDbContentType::MOVIES)
+ return false;
+
+ std::string mediaType = item->GetVideoInfoTag()->m_type;
+
+ // invalid operation warning
+ if (item->GetVideoInfoTag()->HasVideoVersions())
+ {
+ CGUIDialogOK::ShowAndGetInput(
+ CVariant{40005},
+ StringUtils::Format(g_localizeStrings.Get(40006), CMediaTypes::GetLocalization(mediaType)));
+ return false;
+ }
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ {
+ CLog::Log(LOGERROR, "{}: Failed to open database", __FUNCTION__);
+ return false;
+ }
+
+ // get video list
+ std::string videoTitlesDir =
+ StringUtils::Format("videodb://{}/titles", CMediaTypes::ToPlural(mediaType));
+
+ CFileItemList list;
+ if (itemType == VideoDbContentType::MOVIES)
+ videodb.GetMoviesNav(videoTitlesDir, list);
+ else
+ return false;
+
+ if (list.Size() < 2)
+ return false;
+
+ list.Sort(SortByLabel, SortOrderAscending,
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING)
+ ? SortAttributeIgnoreArticle
+ : SortAttributeNone);
+
+ const int dbId = item->GetVideoInfoTag()->m_iDbId;
+
+ for (int i = 0; i < list.Size(); ++i)
+ {
+ if (list[i]->GetVideoInfoTag()->m_iDbId == dbId)
+ {
+ list.Remove(i);
+ break;
+ }
+ }
+
+ // decorate the items
+ CVideoThumbLoader loader;
+ for (auto& item : list)
+ {
+ loader.LoadItem(item.get());
+ item->SetLabel2(item->GetVideoInfoTag()->m_strFileNameAndPath);
+ }
+
+ // choose the target video
+ CGUIDialogSelect* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ if (!dialog)
+ return false;
+
+ dialog->Reset();
+ dialog->SetItems(list);
+ dialog->SetHeading(
+ StringUtils::Format(g_localizeStrings.Get(40002), CMediaTypes::GetLocalization(mediaType)));
+ dialog->SetUseDetails(true);
+ dialog->Open();
+
+ if (!dialog->IsConfirmed())
+ return false;
+
+ std::shared_ptr<CFileItem> selectedItem = dialog->GetSelectedFileItem();
+
+ // choose a video version
+ const int idVideoVersion = SelectVideoVersion(selectedItem);
+ if (idVideoVersion < 0)
+ return false;
+
+ videodb.ConvertVideoToVersion(itemType, dbId, selectedItem->GetVideoInfoTag()->m_iDbId,
+ idVideoVersion);
+ return true;
+}
+
+bool CGUIDialogVideoVersion::ProcessVideoVersion(VideoDbContentType itemType, int dbId)
+{
+ if (itemType != VideoDbContentType::MOVIES)
+ return false;
+
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ {
+ CLog::Log(LOGERROR, "{}: Failed to open database", __FUNCTION__);
+ return false;
+ }
+
+ CFileItem item;
+ if (!videodb.GetDetailsByTypeAndId(item, itemType, dbId))
+ return false;
+
+ CFileItemList list;
+ videodb.GetSameVideoItems(item, list);
+
+ if (list.Size() < 2)
+ return false;
+
+ MediaType mediaType = item.GetVideoInfoTag()->m_type;
+
+ std::string path;
+ videodb.GetFilePathById(dbId, path, itemType);
+
+ if (!CGUIDialogYesNo::ShowAndGetInput(
+ StringUtils::Format(g_localizeStrings.Get(40008),
+ CMediaTypes::GetLocalization(mediaType)),
+ StringUtils::Format(g_localizeStrings.Get(40009), CMediaTypes::GetLocalization(mediaType),
+ item.GetVideoInfoTag()->GetTitle(), path)))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < list.Size(); ++i)
+ {
+ if (dbId == list[i]->GetVideoInfoTag()->m_iDbId)
+ {
+ list.Remove(i);
+ break;
+ }
+ }
+
+ // decorate the items
+ CVideoThumbLoader loader;
+ for (auto& item : list)
+ {
+ loader.LoadItem(item.get());
+ item->SetLabel2(item->GetVideoInfoTag()->m_strFileNameAndPath);
+ }
+
+ CGUIDialogSelect* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ if (!dialog)
+ return false;
+
+ dialog->Reset();
+ dialog->SetItems(list);
+ dialog->SetHeading(
+ StringUtils::Format(g_localizeStrings.Get(40002), CMediaTypes::GetLocalization(mediaType)));
+ dialog->SetUseDetails(true);
+ dialog->Open();
+
+ if (!dialog->IsConfirmed())
+ return false;
+
+ std::shared_ptr<CFileItem> selectedItem = dialog->GetSelectedFileItem();
+
+ // choose a video version
+ const int idVideoVersion = SelectVideoVersion(selectedItem);
+ if (idVideoVersion < 0)
+ return false;
+
+ videodb.ConvertVideoToVersion(itemType, dbId, selectedItem->GetVideoInfoTag()->m_iDbId,
+ idVideoVersion);
+ return true;
+}
+
+std::string CGUIDialogVideoVersion::GenerateExtrasVideoVersion(const std::string& extrasRoot,
+ const std::string& extrasPath)
+{
+ // generate a video extra version string from its file path
+
+ // remove the root path from its path
+ std::string extrasVersion = extrasPath.substr(extrasRoot.size());
+
+ return GenerateExtrasVideoVersion(extrasVersion);
+}
+
+std::string CGUIDialogVideoVersion::GenerateExtrasVideoVersion(const std::string& extrasPath)
+{
+ // generate a video extra version string from its file path
+
+ std::string extrasVersion = extrasPath;
+
+ // remove file extension
+ URIUtils::RemoveExtension(extrasVersion);
+
+ // remove special characters
+ extrasVersion = StringUtils::ReplaceSpecialCharactersWithSpace(extrasVersion);
+
+ // trim the string
+ return StringUtils::Trim(extrasVersion);
+}
diff --git a/xbmc/video/dialogs/GUIDialogVideoVersion.h b/xbmc/video/dialogs/GUIDialogVideoVersion.h
new file mode 100644
index 0000000000..b408f3c782
--- /dev/null
+++ b/xbmc/video/dialogs/GUIDialogVideoVersion.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2005-2023 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 "MediaSource.h"
+#include "guilib/GUIDialog.h"
+#include "media/MediaType.h"
+#include "video/VideoDatabase.h"
+
+#include <memory>
+
+class CFileItem;
+class CFileItemList;
+class CVideoDatabase;
+
+class CGUIDialogVideoVersion : public CGUIDialog
+{
+public:
+ CGUIDialogVideoVersion(int id);
+ ~CGUIDialogVideoVersion(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnBack(int actionID) override;
+
+ enum class Mode
+ {
+ MANAGE,
+ CHOOSE,
+ };
+ void SetMode(Mode mode) { m_mode = mode; }
+ void SetVideoItem(const std::shared_ptr<CFileItem>& item);
+
+ static std::tuple<int, std::string> NewVideoVersion();
+ static bool ConvertVideoVersion(const std::shared_ptr<CFileItem>& item);
+ static bool ProcessVideoVersion(VideoDbContentType itemType, int dbId);
+ static int SelectVideoVersion(const std::shared_ptr<CFileItem>& item);
+ static void ManageVideoVersion(const std::shared_ptr<CFileItem>& item);
+ static std::string GenerateExtrasVideoVersion(const std::string& extrasRoot,
+ const std::string& extrasPath);
+ static std::string GenerateExtrasVideoVersion(const std::string& extrasPath);
+ static int ManageVideoVersionContextMenu(const std::shared_ptr<CFileItem>& version);
+
+ struct VersionSelectResult
+ {
+ bool cancelled{false};
+ std::shared_ptr<CFileItem> selected;
+ };
+ static VersionSelectResult ChooseVideoVersion(const std::shared_ptr<CFileItem>& item);
+
+protected:
+ void OnInitWindow() override;
+
+private:
+ void SetDefaultVideoVersion(CFileItem& version);
+ void SetSelectedVideoVersion(const std::shared_ptr<CFileItem>& version);
+ void ClearVideoVersionList();
+ void RefreshVideoVersionList();
+ void AddVideoVersion(bool primary);
+ void Play();
+ void AddVersion();
+ void AddExtras();
+ void Rename();
+ void SetDefault();
+ void Remove();
+ void ChooseArt();
+ void CloseAll();
+
+ std::shared_ptr<CFileItem> m_videoItem;
+ Mode m_mode{Mode::MANAGE};
+ bool m_cancelled{false};
+ CVideoDatabase m_database;
+ std::unique_ptr<CFileItemList> m_primaryVideoVersionList;
+ std::unique_ptr<CFileItemList> m_extrasVideoVersionList;
+ std::unique_ptr<CFileItem> m_defaultVideoVersion;
+ std::shared_ptr<CFileItem> m_selectedVideoVersion;
+};
diff --git a/xbmc/video/guilib/CMakeLists.txt b/xbmc/video/guilib/CMakeLists.txt
index fd16dee4cb..a953270624 100644
--- a/xbmc/video/guilib/CMakeLists.txt
+++ b/xbmc/video/guilib/CMakeLists.txt
@@ -1,6 +1,10 @@
-set(SOURCES VideoSelectActionProcessor.cpp)
+set(SOURCES VideoPlayActionProcessor.cpp
+ VideoSelectActionProcessor.cpp
+ VideoActionProcessorHelper.cpp)
-set(HEADERS VideoSelectAction.h
- VideoSelectActionProcessor.h)
+set(HEADERS VideoAction.h
+ VideoPlayActionProcessor.h
+ VideoSelectActionProcessor.h
+ VideoActionProcessorHelper.h)
core_add_library(video_guilib)
diff --git a/xbmc/video/guilib/VideoAction.h b/xbmc/video/guilib/VideoAction.h
new file mode 100644
index 0000000000..d2efd86d3c
--- /dev/null
+++ b/xbmc/video/guilib/VideoAction.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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
+
+namespace VIDEO
+{
+namespace GUILIB
+{
+// Note: Do not change the numerical values of the elements. Some of them are used as values for
+// the integer settings SETTING_MYVIDEOS_SELECTACTION and SETTING_MYVIDEOS_PLAYACTION.
+enum Action
+{
+ ACTION_CHOOSE = 0,
+ ACTION_PLAY_OR_RESUME = 1, // if resume is possible, ask user. play from beginning otherwise
+ ACTION_RESUME = 2, // resume if possibly, play from beginning otherwise
+ ACTION_INFO = 3,
+ ACTION_MORE = 4,
+ ACTION_PLAY_FROM_BEGINNING = 5, // play from beginning, also if resume would be possible
+ ACTION_PLAYPART = 6,
+ ACTION_QUEUE = 7,
+};
+} // namespace GUILIB
+} // namespace VIDEO
diff --git a/xbmc/video/guilib/VideoActionProcessorHelper.cpp b/xbmc/video/guilib/VideoActionProcessorHelper.cpp
new file mode 100644
index 0000000000..04f3132cbb
--- /dev/null
+++ b/xbmc/video/guilib/VideoActionProcessorHelper.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 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 "VideoActionProcessorHelper.h"
+
+#include "FileItem.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/dialogs/GUIDialogVideoVersion.h"
+
+using namespace VIDEO::GUILIB;
+
+CVideoActionProcessorHelper::~CVideoActionProcessorHelper()
+{
+ RestoreDefaultVideoVersion();
+}
+
+void CVideoActionProcessorHelper::SetDefaultVideoVersion()
+{
+ RestoreDefaultVideoVersion();
+
+ //! @todo this hack must go away! Playback currently only works if we persist the
+ //! movie version to play in the video database temporarily, until playback was started.
+ CVideoDatabase db;
+ if (!db.Open())
+ {
+ CLog::LogF(LOGERROR, "Unable to open video database!");
+ return;
+ }
+
+ const VideoDbContentType itemType{m_item->GetVideoContentType()};
+ const int dbId{m_item->GetVideoInfoTag()->m_iDbId};
+
+ CFileItem defaultVideoVersion;
+ db.GetDefaultVideoVersion(itemType, dbId, defaultVideoVersion);
+ m_defaultVideoVersionFileId = defaultVideoVersion.GetVideoInfoTag()->m_iDbId;
+ m_defaultVideoVersionDynPath = defaultVideoVersion.GetDynPath();
+
+ db.SetDefaultVideoVersion(itemType, dbId, m_videoVersion->GetVideoInfoTag()->m_iDbId);
+
+ m_item->SetDynPath(m_videoVersion->GetDynPath());
+ db.GetDetailsByTypeAndId(*m_item, itemType, dbId);
+
+ // notify all windows to update the file item
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, GUI_MSG_FLAG_FORCE_UPDATE, m_item);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+}
+
+void CVideoActionProcessorHelper::RestoreDefaultVideoVersion()
+{
+ //! @todo this hack must go away!
+ if (m_restoreFolderFlag)
+ {
+ m_restoreFolderFlag = false;
+ m_item->m_bIsFolder = true;
+ }
+
+ if (m_defaultVideoVersionFileId == -1)
+ return;
+
+ //! @todo this hack must go away! Playback currently only works if we persist the
+ //! movie version to play in the video database temporarily, until playback was started.
+ CVideoDatabase db;
+ if (!db.Open())
+ {
+ CLog::LogF(LOGERROR, "Unable to open video database!");
+ return;
+ }
+
+ const VideoDbContentType itemType{m_item->GetVideoContentType()};
+ const int dbId{m_item->GetVideoInfoTag()->m_iDbId};
+
+ db.SetDefaultVideoVersion(itemType, dbId, m_defaultVideoVersionFileId);
+
+ m_item->SetDynPath(m_defaultVideoVersionDynPath);
+ db.GetDetailsByTypeAndId(*m_item, itemType, dbId);
+
+ m_defaultVideoVersionFileId = -1;
+ m_defaultVideoVersionDynPath.clear();
+
+ // notify all windows to update the file item
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, GUI_MSG_FLAG_FORCE_UPDATE, m_item);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+}
+
+std::shared_ptr<CFileItem> CVideoActionProcessorHelper::ChooseVideoVersion()
+{
+ if (!m_videoVersion && m_item->HasVideoVersions())
+ {
+ if (!m_item->GetProperty("force_choose_video_version").asBoolean(false))
+ {
+ // select the specified video version
+ if (m_item->GetVideoInfoTag()->m_idVideoVersion > 0)
+ m_videoVersion = m_item;
+
+ const auto settings{CServiceBroker::GetSettingsComponent()->GetSettings()};
+
+ if (!m_videoVersion)
+ {
+ //! @todo get rid of this hack to patch away item's folder flag if it is video version
+ //! folder
+ if (m_item->GetVideoInfoTag()->m_idVideoVersion == VIDEO_VERSION_ID_ALL &&
+ settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER))
+ {
+ m_item->m_bIsFolder = false;
+ m_restoreFolderFlag = true;
+ }
+ }
+
+ if (!m_videoVersion)
+ {
+ // select the default video version
+ if (settings->GetBool(CSettings::SETTING_MYVIDEOS_SELECTDEFAULTVERSION))
+ {
+ CVideoDatabase db;
+ if (!db.Open())
+ {
+ CLog::LogF(LOGERROR, "Unable to open video database!");
+ }
+ else
+ {
+ CFileItem defaultVersion;
+ db.GetDefaultVideoVersion(m_item->GetVideoContentType(),
+ m_item->GetVideoInfoTag()->m_iDbId, defaultVersion);
+ if (!defaultVersion.HasVideoInfoTag() || defaultVersion.GetVideoInfoTag()->IsEmpty())
+ CLog::LogF(LOGERROR, "Unable to get default video version from video database!");
+ else
+ m_videoVersion = std::make_shared<const CFileItem>(defaultVersion);
+ }
+ }
+ }
+ }
+
+ if (!m_videoVersion && (m_item->GetProperty("force_choose_video_version").asBoolean(false) ||
+ !m_item->GetProperty("prohibit_choose_video_version").asBoolean(false)))
+ {
+ const auto result{CGUIDialogVideoVersion::ChooseVideoVersion(m_item)};
+ if (result.cancelled)
+ return {};
+ else
+ m_videoVersion = result.selected;
+ }
+ }
+
+ if (m_videoVersion)
+ SetDefaultVideoVersion();
+
+ return m_item;
+}
diff --git a/xbmc/video/guilib/VideoActionProcessorHelper.h b/xbmc/video/guilib/VideoActionProcessorHelper.h
new file mode 100644
index 0000000000..0238d70953
--- /dev/null
+++ b/xbmc/video/guilib/VideoActionProcessorHelper.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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 <memory>
+#include <string>
+
+class CFileItem;
+
+namespace VIDEO
+{
+namespace GUILIB
+{
+class CVideoActionProcessorHelper
+{
+public:
+ CVideoActionProcessorHelper(const std::shared_ptr<CFileItem>& item,
+ const std::shared_ptr<const CFileItem>& videoVersion)
+ : m_item{item}, m_videoVersion{videoVersion}
+ {
+ }
+ virtual ~CVideoActionProcessorHelper();
+
+ std::shared_ptr<CFileItem> ChooseVideoVersion();
+
+private:
+ CVideoActionProcessorHelper() = delete;
+ void SetDefaultVideoVersion();
+ void RestoreDefaultVideoVersion();
+
+ std::shared_ptr<CFileItem> m_item;
+ std::shared_ptr<const CFileItem> m_videoVersion;
+ int m_defaultVideoVersionFileId{-1};
+ std::string m_defaultVideoVersionDynPath;
+ bool m_restoreFolderFlag{false};
+};
+} // namespace GUILIB
+} // namespace VIDEO
diff --git a/xbmc/video/guilib/VideoPlayActionProcessor.cpp b/xbmc/video/guilib/VideoPlayActionProcessor.cpp
new file mode 100644
index 0000000000..bea41524d8
--- /dev/null
+++ b/xbmc/video/guilib/VideoPlayActionProcessor.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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 "VideoPlayActionProcessor.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/Variant.h"
+#include "video/VideoUtils.h"
+#include "video/guilib/VideoActionProcessorHelper.h"
+
+using namespace VIDEO::GUILIB;
+
+Action CVideoPlayActionProcessorBase::GetDefaultAction()
+{
+ return static_cast<Action>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ CSettings::SETTING_MYVIDEOS_PLAYACTION));
+}
+
+bool CVideoPlayActionProcessorBase::ProcessDefaultAction()
+{
+ return ProcessAction(GetDefaultAction());
+}
+
+bool CVideoPlayActionProcessorBase::ProcessAction(Action action)
+{
+ m_userCancelled = false;
+
+ CVideoActionProcessorHelper procHelper{m_item, m_videoVersion};
+ const auto videoVersion{procHelper.ChooseVideoVersion()};
+ if (videoVersion)
+ m_item = videoVersion;
+ else
+ {
+ m_userCancelled = true;
+ return true; // User cancelled the select menu. We're done.
+ }
+
+ return Process(action);
+}
+
+bool CVideoPlayActionProcessorBase::Process(Action action)
+{
+ switch (action)
+ {
+ case ACTION_PLAY_OR_RESUME:
+ {
+ const Action selectedAction = ChoosePlayOrResume(*m_item);
+ if (selectedAction < 0)
+ {
+ m_userCancelled = true;
+ return true; // User cancelled the select menu. We're done.
+ }
+
+ return Process(selectedAction);
+ }
+
+ case ACTION_RESUME:
+ return OnResumeSelected();
+
+ case ACTION_PLAY_FROM_BEGINNING:
+ return OnPlaySelected();
+
+ default:
+ break;
+ }
+ return false; // We did not handle the action.
+}
+
+Action CVideoPlayActionProcessorBase::ChoosePlayOrResume(const CFileItem& item)
+{
+ Action action = ACTION_PLAY_FROM_BEGINNING;
+
+ const std::string resumeString = VIDEO_UTILS::GetResumeString(item);
+ if (!resumeString.empty())
+ {
+ CContextButtons choices;
+
+ choices.Add(ACTION_RESUME, resumeString);
+ choices.Add(ACTION_PLAY_FROM_BEGINNING, 12021); // Play from beginning
+
+ action = static_cast<Action>(CGUIDialogContextMenu::ShowAndGetChoice(choices));
+ }
+
+ return action;
+}
diff --git a/xbmc/video/guilib/VideoPlayActionProcessor.h b/xbmc/video/guilib/VideoPlayActionProcessor.h
new file mode 100644
index 0000000000..33105be8ad
--- /dev/null
+++ b/xbmc/video/guilib/VideoPlayActionProcessor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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 "video/guilib/VideoAction.h"
+
+#include <memory>
+
+class CFileItem;
+
+namespace VIDEO
+{
+namespace GUILIB
+{
+class CVideoPlayActionProcessorBase
+{
+public:
+ explicit CVideoPlayActionProcessorBase(const std::shared_ptr<CFileItem>& item) : m_item(item) {}
+ CVideoPlayActionProcessorBase(const std::shared_ptr<CFileItem>& item,
+ const std::shared_ptr<const CFileItem>& videoVersion)
+ : m_item{item}, m_videoVersion{videoVersion}
+ {
+ }
+ virtual ~CVideoPlayActionProcessorBase() = default;
+
+ bool ProcessDefaultAction();
+ bool ProcessAction(Action action);
+
+ bool UserCancelled() const { return m_userCancelled; }
+
+ static Action ChoosePlayOrResume(const CFileItem& item);
+
+protected:
+ virtual Action GetDefaultAction();
+ virtual bool Process(Action action);
+
+ virtual bool OnResumeSelected() = 0;
+ virtual bool OnPlaySelected() = 0;
+
+ std::shared_ptr<CFileItem> m_item;
+ bool m_userCancelled{false};
+
+private:
+ CVideoPlayActionProcessorBase() = delete;
+
+ const std::shared_ptr<const CFileItem> m_videoVersion;
+};
+} // namespace GUILIB
+} // namespace VIDEO
diff --git a/xbmc/video/guilib/VideoSelectAction.h b/xbmc/video/guilib/VideoSelectAction.h
deleted file mode 100644
index 731e0cf35b..0000000000
--- a/xbmc/video/guilib/VideoSelectAction.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2023 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
-
-namespace VIDEO
-{
-namespace GUILIB
-{
-// Note: Do not change the numerical values of the elements. Some of them are used as values for
-// the integer setting SETTING_MYVIDEOS_SELECTACTION.
-enum SelectAction
-{
- SELECT_ACTION_CHOOSE = 0,
- SELECT_ACTION_PLAY_OR_RESUME = 1,
- SELECT_ACTION_RESUME = 2,
- SELECT_ACTION_INFO = 3,
- SELECT_ACTION_MORE = 4,
- SELECT_ACTION_PLAY = 5,
- SELECT_ACTION_PLAYPART = 6,
- SELECT_ACTION_QUEUE = 7,
-};
-} // namespace GUILIB
-} // namespace VIDEO
diff --git a/xbmc/video/guilib/VideoSelectActionProcessor.cpp b/xbmc/video/guilib/VideoSelectActionProcessor.cpp
index 1a8d274a4f..4458115bbf 100644
--- a/xbmc/video/guilib/VideoSelectActionProcessor.cpp
+++ b/xbmc/video/guilib/VideoSelectActionProcessor.cpp
@@ -19,45 +19,43 @@
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "utils/StringUtils.h"
+#include "utils/Variant.h"
#include "video/VideoInfoTag.h"
#include "video/VideoUtils.h"
using namespace VIDEO::GUILIB;
-SelectAction CVideoSelectActionProcessorBase::GetDefaultSelectAction()
+Action CVideoSelectActionProcessorBase::GetDefaultSelectAction()
{
- return static_cast<SelectAction>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ return static_cast<Action>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
CSettings::SETTING_MYVIDEOS_SELECTACTION));
}
-bool CVideoSelectActionProcessorBase::Process()
+Action CVideoSelectActionProcessorBase::GetDefaultAction()
{
- return Process(GetDefaultSelectAction());
+ return GetDefaultSelectAction();
}
-bool CVideoSelectActionProcessorBase::Process(SelectAction selectAction)
+bool CVideoSelectActionProcessorBase::Process(Action action)
{
- switch (selectAction)
- {
- case SELECT_ACTION_CHOOSE:
- {
- const SelectAction action = ChooseVideoItemSelectAction();
- if (action < 0)
- return true; // User cancelled the context menu. We're done.
+ if (CVideoPlayActionProcessorBase::Process(action))
+ return true;
- return Process(action);
- }
-
- case SELECT_ACTION_PLAY_OR_RESUME:
+ switch (action)
+ {
+ case ACTION_CHOOSE:
{
- const SelectAction action = ChoosePlayOrResume(m_item);
- if (action < 0)
+ const Action selectedAction = ChooseVideoItemSelectAction();
+ if (selectedAction < 0)
+ {
+ m_userCancelled = true;
return true; // User cancelled the select menu. We're done.
+ }
- return Process(action);
+ return Process(selectedAction);
}
- case SELECT_ACTION_PLAYPART:
+ case ACTION_PLAYPART:
{
const unsigned int part = ChooseStackItemPartNumber();
if (part < 1) // part numbers are 1-based
@@ -66,19 +64,13 @@ bool CVideoSelectActionProcessorBase::Process(SelectAction selectAction)
return OnPlayPartSelected(part);
}
- case SELECT_ACTION_RESUME:
- return OnResumeSelected();
-
- case SELECT_ACTION_PLAY:
- return OnPlaySelected();
-
- case SELECT_ACTION_QUEUE:
+ case ACTION_QUEUE:
return OnQueueSelected();
- case SELECT_ACTION_INFO:
+ case ACTION_INFO:
return OnInfoSelected();
- case SELECT_ACTION_MORE:
+ case ACTION_MORE:
return OnMoreSelected();
default:
@@ -90,7 +82,7 @@ bool CVideoSelectActionProcessorBase::Process(SelectAction selectAction)
unsigned int CVideoSelectActionProcessorBase::ChooseStackItemPartNumber() const
{
CFileItemList parts;
- XFILE::CDirectory::GetDirectory(m_item.GetDynPath(), parts, "", XFILE::DIR_FLAG_DEFAULTS);
+ XFILE::CDirectory::GetDirectory(m_item->GetDynPath(), parts, "", XFILE::DIR_FLAG_DEFAULTS);
for (int i = 0; i < parts.Size(); ++i)
parts[i]->SetLabel(StringUtils::Format(g_localizeStrings.Get(23051), i + 1)); // Part #
@@ -110,42 +102,24 @@ unsigned int CVideoSelectActionProcessorBase::ChooseStackItemPartNumber() const
return dialog->GetSelectedItem() + 1; // part numbers are 1-based
}
-SelectAction CVideoSelectActionProcessorBase::ChoosePlayOrResume(const CFileItem& item)
-{
- SelectAction action = SELECT_ACTION_PLAY;
-
- const std::string resumeString = VIDEO_UTILS::GetResumeString(item);
- if (!resumeString.empty())
- {
- CContextButtons choices;
-
- choices.Add(SELECT_ACTION_RESUME, resumeString);
- choices.Add(SELECT_ACTION_PLAY, 12021); // Play from beginning
-
- action = static_cast<SelectAction>(CGUIDialogContextMenu::ShowAndGetChoice(choices));
- }
-
- return action;
-}
-
-SelectAction CVideoSelectActionProcessorBase::ChooseVideoItemSelectAction() const
+Action CVideoSelectActionProcessorBase::ChooseVideoItemSelectAction() const
{
CContextButtons choices;
- const std::string resumeString = VIDEO_UTILS::GetResumeString(m_item);
+ const std::string resumeString = VIDEO_UTILS::GetResumeString(*m_item);
if (!resumeString.empty())
{
- choices.Add(SELECT_ACTION_RESUME, resumeString);
- choices.Add(SELECT_ACTION_PLAY, 12021); // Play from beginning
+ choices.Add(ACTION_RESUME, resumeString);
+ choices.Add(ACTION_PLAY_FROM_BEGINNING, 12021); // Play from beginning
}
else
{
- choices.Add(SELECT_ACTION_PLAY, 208); // Play
+ choices.Add(ACTION_PLAY_FROM_BEGINNING, 208); // Play
}
- choices.Add(SELECT_ACTION_INFO, 22081); // Show information
- choices.Add(SELECT_ACTION_QUEUE, 13347); // Queue item
- choices.Add(SELECT_ACTION_MORE, 22082); // More
+ choices.Add(ACTION_INFO, 22081); // Show information
+ choices.Add(ACTION_QUEUE, 13347); // Queue item
+ choices.Add(ACTION_MORE, 22082); // More
- return static_cast<SelectAction>(CGUIDialogContextMenu::ShowAndGetChoice(choices));
+ return static_cast<Action>(CGUIDialogContextMenu::ShowAndGetChoice(choices));
}
diff --git a/xbmc/video/guilib/VideoSelectActionProcessor.h b/xbmc/video/guilib/VideoSelectActionProcessor.h
index c896a386da..11217df5a6 100644
--- a/xbmc/video/guilib/VideoSelectActionProcessor.h
+++ b/xbmc/video/guilib/VideoSelectActionProcessor.h
@@ -8,7 +8,9 @@
#pragma once
-#include "video/guilib/VideoSelectAction.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
+
+#include <memory>
class CFileItem;
@@ -16,32 +18,36 @@ namespace VIDEO
{
namespace GUILIB
{
-class CVideoSelectActionProcessorBase
+class CVideoSelectActionProcessorBase : public CVideoPlayActionProcessorBase
{
public:
- explicit CVideoSelectActionProcessorBase(CFileItem& item) : m_item(item) {}
- virtual ~CVideoSelectActionProcessorBase() = default;
+ explicit CVideoSelectActionProcessorBase(const std::shared_ptr<CFileItem>& item)
+ : CVideoPlayActionProcessorBase(item)
+ {
+ }
- static SelectAction GetDefaultSelectAction();
+ CVideoSelectActionProcessorBase(const std::shared_ptr<CFileItem>& item,
+ const std::shared_ptr<const CFileItem>& videoVersion)
+ : CVideoPlayActionProcessorBase(item, videoVersion)
+ {
+ }
- bool Process();
- bool Process(SelectAction selectAction);
+ ~CVideoSelectActionProcessorBase() override = default;
- static SelectAction ChoosePlayOrResume(const CFileItem& item);
+ static Action GetDefaultSelectAction();
protected:
+ Action GetDefaultAction() override;
+ bool Process(Action action) override;
+
virtual bool OnPlayPartSelected(unsigned int part) = 0;
- virtual bool OnResumeSelected() = 0;
- virtual bool OnPlaySelected() = 0;
virtual bool OnQueueSelected() = 0;
virtual bool OnInfoSelected() = 0;
virtual bool OnMoreSelected() = 0;
- CFileItem& m_item;
-
private:
CVideoSelectActionProcessorBase() = delete;
- SelectAction ChooseVideoItemSelectAction() const;
+ Action ChooseVideoItemSelectAction() const;
unsigned int ChooseStackItemPartNumber() const;
};
} // namespace GUILIB
diff --git a/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp b/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp
index 25381bbe82..486bd7a727 100644
--- a/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp
+++ b/xbmc/video/jobs/VideoLibraryMarkWatchedJob.cpp
@@ -6,23 +6,25 @@
* See LICENSES/README.md for more information.
*/
-#include <vector>
-
#include "VideoLibraryMarkWatchedJob.h"
+
#include "FileItem.h"
+#include "ServiceBroker.h"
#include "Util.h"
#include "filesystem/Directory.h"
#ifdef HAS_UPNP
#include "network/upnp/UPnP.h"
#endif
+#include "profiles/ProfileManager.h"
#include "pvr/PVRManager.h"
#include "pvr/recordings/PVRRecordings.h"
-#include "profiles/ProfileManager.h"
#include "settings/SettingsComponent.h"
-#include "ServiceBroker.h"
#include "utils/URIUtils.h"
#include "video/VideoDatabase.h"
+#include <memory>
+#include <vector>
+
CVideoLibraryMarkWatchedJob::CVideoLibraryMarkWatchedJob(const std::shared_ptr<CFileItem>& item,
bool mark)
: m_item(item), m_mark(mark)
@@ -50,7 +52,7 @@ bool CVideoLibraryMarkWatchedJob::Work(CVideoDatabase &db)
return false;
CFileItemList items;
- items.Add(CFileItemPtr(new CFileItem(*m_item)));
+ items.Add(std::make_shared<CFileItem>(*m_item));
if (m_item->m_bIsFolder)
CUtil::GetRecursiveListing(m_item->GetPath(), items, "", XFILE::DIR_FLAG_NO_FILE_INFO);
diff --git a/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp b/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp
index 9f300720b2..4e10720a3c 100644
--- a/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp
+++ b/xbmc/video/jobs/VideoLibraryRefreshingJob.cpp
@@ -32,6 +32,7 @@
#include "video/tags/VideoInfoTagLoaderFactory.h"
#include "video/tags/VideoTagLoaderPlugin.h"
+#include <memory>
#include <utility>
using namespace KODI::MESSAGING;
@@ -281,13 +282,13 @@ bool CVideoLibraryRefreshingJob::Work(CVideoDatabase &db)
}
// otherwise just add a copy of the item
else
- items.Add(CFileItemPtr(new CFileItem(*m_item->GetVideoInfoTag())));
+ items.Add(std::make_shared<CFileItem>(*m_item->GetVideoInfoTag()));
// update the path to the real path (instead of a videodb:// one)
path = m_item->GetVideoInfoTag()->m_strPath;
}
else
- items.Add(CFileItemPtr(new CFileItem(*m_item)));
+ items.Add(std::make_shared<CFileItem>(*m_item));
// set the proper path of the list of items to lookup
items.SetPath(m_item->m_bIsFolder ? URIUtils::GetParentPath(path) : URIUtils::GetDirectory(path));
diff --git a/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp b/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp
index 9fa3687b5e..e17c99ceb7 100644
--- a/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp
+++ b/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp
@@ -163,10 +163,9 @@ CInfoScanner::INFO_TYPE CVideoTagLoaderFFmpeg::LoadMKV(CVideoInfoTag& tag,
continue;
size_t size = m_fctx->streams[i]->attached_pic.size;
if (art)
- art->emplace_back(EmbeddedArt(m_fctx->streams[i]->attached_pic.data,
- size, avtag->value, type));
+ art->emplace_back(m_fctx->streams[i]->attached_pic.data, size, avtag->value, type);
else
- tag.m_coverArt.emplace_back(EmbeddedArtInfo(size, avtag->value, type));
+ tag.m_coverArt.emplace_back(size, avtag->value, type);
}
}
@@ -258,10 +257,9 @@ CInfoScanner::INFO_TYPE CVideoTagLoaderFFmpeg::LoadMP4(CVideoInfoTag& tag,
size_t size = m_fctx->streams[i]->attached_pic.size;
const std::string type = "poster";
if (art)
- art->emplace_back(EmbeddedArt(m_fctx->streams[i]->attached_pic.data,
- size, "image/png", type));
+ art->emplace_back(m_fctx->streams[i]->attached_pic.data, size, "image/png", type);
else
- tag.m_coverArt.emplace_back(EmbeddedArtInfo(size, "image/png", type));
+ tag.m_coverArt.emplace_back(size, "image/png", type);
}
return hasfull ? CInfoScanner::FULL_NFO : CInfoScanner::TITLE_NFO;
diff --git a/xbmc/video/tags/VideoTagLoaderPlugin.cpp b/xbmc/video/tags/VideoTagLoaderPlugin.cpp
index 407e0ee4f0..f3b97ee608 100644
--- a/xbmc/video/tags/VideoTagLoaderPlugin.cpp
+++ b/xbmc/video/tags/VideoTagLoaderPlugin.cpp
@@ -12,6 +12,8 @@
#include "URL.h"
#include "filesystem/PluginDirectory.h"
+#include <memory>
+
using namespace XFILE;
CVideoTagLoaderPlugin::CVideoTagLoaderPlugin(const CFileItem& item, bool forceRefresh)
@@ -21,10 +23,10 @@ CVideoTagLoaderPlugin::CVideoTagLoaderPlugin(const CFileItem& item, bool forceRe
return;
// Preserve CFileItem video info and art to avoid info loss between creating VideoInfoTagLoaderFactory and calling Load()
if (m_item.HasVideoInfoTag())
- m_tag.reset(new CVideoInfoTag(*m_item.GetVideoInfoTag()));
+ m_tag = std::make_unique<CVideoInfoTag>(*m_item.GetVideoInfoTag());
auto& art = item.GetArt();
if (!art.empty())
- m_art.reset(new CGUIListItem::ArtMap(art));
+ m_art = std::make_unique<CGUIListItem::ArtMap>(art);
}
bool CVideoTagLoaderPlugin::HasInfo() const
@@ -48,7 +50,7 @@ CInfoScanner::INFO_TYPE CVideoTagLoaderPlugin::Load(CVideoInfoTag& tag, bool, st
if (!items.IsEmpty())
{
const CFileItemPtr &item = items[0];
- m_art.reset(new CGUIListItem::ArtMap(item->GetArt()));
+ m_art = std::make_unique<CGUIListItem::ArtMap>(item->GetArt());
if (item->HasVideoInfoTag())
{
tag = *item->GetVideoInfoTag();
diff --git a/xbmc/video/test/TestVideoInfoScanner.cpp b/xbmc/video/test/TestVideoInfoScanner.cpp
index 981d7498b0..f44ead0ae6 100644
--- a/xbmc/video/test/TestVideoInfoScanner.cpp
+++ b/xbmc/video/test/TestVideoInfoScanner.cpp
@@ -52,7 +52,7 @@ TEST_P(TestVideoInfoScanner, EnumerateEpisodeItem)
CFileItem item(entry.path, false);
EPISODELIST expected;
for (int i = 0; i < 3 && entry.episode[i]; i++)
- expected.push_back(EPISODE(entry.season, entry.episode[i], 0, false));
+ expected.emplace_back(entry.season, entry.episode[i], 0, false);
EPISODELIST result;
ASSERT_TRUE(scanner.EnumerateEpisodeItem(&item, result));
diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp
index 2d92bb2086..99721ed0b8 100644
--- a/xbmc/video/windows/GUIWindowVideoBase.cpp
+++ b/xbmc/video/windows/GUIWindowVideoBase.cpp
@@ -20,7 +20,6 @@
#include "application/Application.h"
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
-#include "cores/playercorefactory/PlayerCoreFactory.h"
#include "dialogs/GUIDialogProgress.h"
#include "dialogs/GUIDialogSelect.h"
#include "dialogs/GUIDialogSmartPlaylistEditor.h"
@@ -52,13 +51,18 @@
#include "utils/URIUtils.h"
#include "utils/Variant.h"
#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/VideoDbUrl.h"
#include "video/VideoInfoScanner.h"
#include "video/VideoLibraryQueue.h"
#include "video/VideoUtils.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
+#include "video/guilib/VideoPlayActionProcessor.h"
#include "video/guilib/VideoSelectActionProcessor.h"
#include "view/GUIViewState.h"
+#include <memory>
+
using namespace XFILE;
using namespace VIDEODATABASEDIRECTORY;
using namespace VIDEO;
@@ -211,6 +215,10 @@ bool CGUIWindowVideoBase::OnItemInfo(const CFileItem& fileItem)
if (fileItem.HasAddonInfo())
return CGUIDialogAddonInfo::ShowForItem(std::make_shared<CFileItem>(fileItem));
+ // Video version
+ if (fileItem.HasVideoInfoTag() && fileItem.GetVideoInfoTag()->m_type == MediaTypeVideoVersion)
+ return false;
+
// Movie set
if (fileItem.m_bIsFolder && fileItem.IsVideoDb() &&
fileItem.GetPath() != "videodb://movies/sets/" &&
@@ -238,11 +246,12 @@ bool CGUIWindowVideoBase::OnItemInfo(const CFileItem& fileItem)
bool foundDirectly = false;
const ADDON::ScraperPtr scraper = m_database.GetScraperForPath(strDir, settings, foundDirectly);
- if (!scraper && !(fileItem.IsPlugin() || fileItem.IsScript()) &&
+ if (!fileItem.HasVideoInfoTag() && !scraper && !(fileItem.IsPlugin() || fileItem.IsScript()) &&
!(m_database.HasMovieInfo(fileItem.GetDynPath()) || m_database.HasTvShowInfo(strDir) ||
m_database.HasEpisodeInfo(fileItem.GetDynPath()) ||
m_database.HasMusicVideoInfo(fileItem.GetDynPath())))
{
+ // We have no chance to fill a video info tag, neither scraper nor db data available.
HELPERS::ShowOKDialogText(CVariant{20176}, // Show video information
CVariant{19055}); // no information available
m_database.Close();
@@ -520,6 +529,7 @@ bool CGUIWindowVideoBase::OnSelect(int iItem)
!StringUtils::StartsWith(path, "newsmartplaylist://") &&
!StringUtils::StartsWith(path, "newplaylist://") &&
!StringUtils::StartsWith(path, "newtag://") &&
+ !StringUtils::StartsWith(path, "newvideoversion://") &&
!StringUtils::StartsWith(path, "script://"))
return OnFileAction(iItem, CVideoSelectActionProcessorBase::GetDefaultSelectAction(), "");
@@ -532,7 +542,7 @@ class CVideoSelectActionProcessor : public CVideoSelectActionProcessorBase
{
public:
CVideoSelectActionProcessor(CGUIWindowVideoBase& window,
- CFileItem& item,
+ const std::shared_ptr<CFileItem>& item,
int itemIndex,
const std::string& player)
: CVideoSelectActionProcessorBase(item),
@@ -542,7 +552,7 @@ public:
{
// Reset the current start offset. The actual resume
// option is set by the processor, based on the action passed.
- m_item.SetStartOffset(0);
+ m_item->SetStartOffset(0);
}
protected:
@@ -553,27 +563,27 @@ protected:
bool OnResumeSelected() override
{
- m_item.SetStartOffset(STARTOFFSET_RESUME);
- if (m_item.m_bIsFolder)
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ if (m_item->m_bIsFolder)
{
// resume playback of the folder
m_window.PlayItem(m_itemIndex, m_player);
return true;
}
// resume playback of the video
- return m_window.OnClick(m_itemIndex);
+ return m_window.OnClick(m_itemIndex, m_player);
}
bool OnPlaySelected() override
{
- if (m_item.m_bIsFolder)
+ if (m_item->m_bIsFolder)
{
// play the folder
m_window.PlayItem(m_itemIndex, m_player);
return true;
}
// play the video
- return m_window.OnClick(m_itemIndex);
+ return m_window.OnClick(m_itemIndex, m_player);
}
bool OnQueueSelected() override
@@ -597,14 +607,14 @@ private:
};
} // namespace
-bool CGUIWindowVideoBase::OnFileAction(int iItem, SelectAction action, const std::string& player)
+bool CGUIWindowVideoBase::OnFileAction(int iItem, Action action, const std::string& player)
{
const std::shared_ptr<CFileItem> item = m_vecItems->Get(iItem);
if (!item)
return false;
- CVideoSelectActionProcessor proc(*this, *item, iItem, player);
- return proc.Process(action);
+ CVideoSelectActionProcessor proc(*this, item, iItem, player);
+ return proc.ProcessAction(action);
}
bool CGUIWindowVideoBase::OnItemInfo(int iItem)
@@ -724,9 +734,49 @@ void CGUIWindowVideoBase::LoadVideoInfo(CFileItemList& items,
}
}
+namespace
+{
+class CVideoPlayActionProcessor : public CVideoPlayActionProcessorBase
+{
+public:
+ CVideoPlayActionProcessor(CGUIWindowVideoBase& window,
+ const std::shared_ptr<CFileItem>& item,
+ int itemIndex,
+ const std::string& player)
+ : CVideoPlayActionProcessorBase(item),
+ m_window(window),
+ m_itemIndex(itemIndex),
+ m_player(player)
+ {
+ }
+
+protected:
+ bool OnResumeSelected() override
+ {
+ m_item->SetStartOffset(STARTOFFSET_RESUME);
+ return m_window.OnFileAction(m_itemIndex, VIDEO::GUILIB::ACTION_RESUME, m_player);
+ }
+
+ bool OnPlaySelected() override
+ {
+ m_item->SetStartOffset(0);
+ return m_window.OnFileAction(m_itemIndex, VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING, m_player);
+ }
+
+private:
+ CGUIWindowVideoBase& m_window;
+ const int m_itemIndex{-1};
+ const std::string m_player;
+};
+} // namespace
+
bool CGUIWindowVideoBase::OnPlayOrResumeItem(int iItem, const std::string& player)
{
- return OnFileAction(iItem, SELECT_ACTION_PLAY_OR_RESUME, player);
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return false;
+
+ CVideoPlayActionProcessor proc{*this, m_vecItems->Get(iItem), iItem, player};
+ return proc.ProcessDefaultAction();
}
void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
@@ -755,23 +805,6 @@ void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &but
}
}
- if (!item->m_bIsFolder && !(item->IsPlayList() && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders))
- {
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- // get players
- std::vector<std::string> players;
- if (item->IsVideoDb())
- {
- CFileItem item2(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
- playerCoreFactory.GetPlayers(item2, players);
- }
- else
- playerCoreFactory.GetPlayers(*item, players);
-
- if (players.size() > 1)
- buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213);
- }
if (item->IsSmartPlayList())
{
buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
@@ -818,13 +851,13 @@ bool CGUIWindowVideoBase::OnPlayStackPart(int itemIndex, unsigned int partNumber
CDirectory::GetDirectory(path, parts, "", DIR_FLAG_DEFAULTS);
const int value = CVideoSelectActionProcessor::ChoosePlayOrResume(*parts[partNumber - 1]);
- if (value == SELECT_ACTION_RESUME)
+ if (value == VIDEO::GUILIB::ACTION_RESUME)
{
const VIDEO_UTILS::ResumeInformation resumeInfo =
VIDEO_UTILS::GetItemResumeInformation(*parts[partNumber - 1]);
item->SetStartOffset(resumeInfo.startOffset);
}
- else if (value != SELECT_ACTION_PLAY)
+ else if (value != VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING)
return false; // if not selected PLAY, then we changed our mind so return
item->m_lStartPartNumber = partNumber;
@@ -861,36 +894,7 @@ bool CGUIWindowVideoBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
}
case CONTEXT_BUTTON_PLAY_PART:
{
- return OnFileAction(itemNumber, SELECT_ACTION_PLAYPART, "");
- }
- case CONTEXT_BUTTON_PLAY_WITH:
- {
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- std::vector<std::string> players;
- if (item->IsVideoDb())
- {
- CFileItem item2(*item->GetVideoInfoTag());
- playerCoreFactory.GetPlayers(item2, players);
- }
- else
- playerCoreFactory.GetPlayers(*item, players);
-
- std:: string player = playerCoreFactory.SelectPlayerDialog(players);
- if (!player.empty())
- {
- // any other select actions but play or resume, resume, play or playpart
- // don't make any sense here since the user already decided that he'd
- // like to play the item (just with a specific player)
- SelectAction selectAction = CVideoSelectActionProcessorBase::GetDefaultSelectAction();
- if (selectAction != SELECT_ACTION_PLAY_OR_RESUME &&
- selectAction != SELECT_ACTION_RESUME &&
- selectAction != SELECT_ACTION_PLAY &&
- selectAction != SELECT_ACTION_PLAYPART)
- selectAction = SELECT_ACTION_PLAY_OR_RESUME;
- return OnFileAction(itemNumber, selectAction, player);
- }
- return true;
+ return OnFileAction(itemNumber, VIDEO::GUILIB::ACTION_PLAYPART, "");
}
case CONTEXT_BUTTON_PLAY_PARTYMODE:
@@ -959,7 +963,7 @@ bool CGUIWindowVideoBase::OnPlayMedia(int iItem, const std::string &player)
CServiceBroker::GetPlaylistPlayer().Reset();
CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
- const auto itemCopy = std::make_shared<CFileItem>(*pItem);
+ auto itemCopy = std::make_shared<CFileItem>(*pItem);
if (pItem->IsVideoDb())
{
@@ -1119,6 +1123,10 @@ bool CGUIWindowVideoBase::Update(const std::string &strDirectory, bool updateFil
if (!m_thumbLoader.IsLoading())
m_thumbLoader.Load(*m_vecItems);
+ UpdateVideoVersionItems();
+
+ UpdateVideoVersionItemsLabel(strDirectory);
+
return true;
}
@@ -1143,7 +1151,7 @@ bool CGUIWindowVideoBase::GetDirectory(const std::string &strDirectory, CFileIte
newPlaylist->SetLabelPreformatted(true);
items.Add(newPlaylist);
*/
- newPlaylist.reset(new CFileItem("newsmartplaylist://video", false));
+ newPlaylist = std::make_shared<CFileItem>("newsmartplaylist://video", false);
newPlaylist->SetLabel(g_localizeStrings.Get(21437)); // "new smart playlist..."
newPlaylist->SetArt("icon", "DefaultAddSource.png");
newPlaylist->SetLabelPreformatted(true);
@@ -1445,3 +1453,60 @@ void CGUIWindowVideoBase::OnAssignContent(const std::string &path)
CVideoLibraryQueue::GetInstance().ScanLibrary(path, true, true);
}
}
+
+void CGUIWindowVideoBase::UpdateVideoVersionItems()
+{
+ if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER))
+ return;
+
+ for (const auto& item : *m_vecItems)
+ {
+ if (item->m_bIsFolder || !item->HasVideoInfoTag() ||
+ item->GetVideoInfoTag()->m_idVideoVersion > 0)
+ continue;
+
+ MediaType type = item->GetVideoInfoTag()->m_type;
+ if (type == MediaTypeMovie)
+ {
+ if (item->GetVideoInfoTag()->m_hasVideoVersions)
+ {
+ CVideoDbUrl itemUrl;
+ if (!itemUrl.FromString(
+ StringUtils::Format("videodb://movies/videoversions/{}", VIDEO_VERSION_ID_ALL)))
+ return;
+
+ itemUrl.AddOption("mediaid", item->GetVideoInfoTag()->m_iDbId);
+
+ item->GetVideoInfoTag()->m_idVideoVersion = VIDEO_VERSION_ID_ALL;
+ item->SetPath(itemUrl.ToString());
+ item->m_bIsFolder = true;
+
+ item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, true);
+ }
+ }
+ }
+}
+
+void CGUIWindowVideoBase::UpdateVideoVersionItemsLabel(const std::string& directory)
+{
+ CVideoDbUrl videoUrl;
+ if (videoUrl.FromString(directory) && videoUrl.HasOption("videoversionid"))
+ {
+ CVariant value;
+ if (videoUrl.GetOption("videoversionid", value))
+ {
+ const int idVideoVersion = value.asInteger(-1);
+ if (idVideoVersion == VIDEO_VERSION_ID_ALL && videoUrl.GetOption("mediaid", value))
+ {
+ int idMedia = value.asInteger(-1);
+ if (idMedia != -1)
+ {
+ m_vecItems->SetLabel(StringUtils::Format(
+ "{} {}", m_database.GetVideoItemTitle(m_vecItems->GetVideoContentType(), idMedia),
+ g_localizeStrings.Get(40000)));
+ }
+ }
+ }
+ }
+}
diff --git a/xbmc/video/windows/GUIWindowVideoBase.h b/xbmc/video/windows/GUIWindowVideoBase.h
index 38a54a40a3..1542ac4dce 100644
--- a/xbmc/video/windows/GUIWindowVideoBase.h
+++ b/xbmc/video/windows/GUIWindowVideoBase.h
@@ -11,17 +11,19 @@
#include "playlists/PlayListTypes.h"
#include "video/VideoDatabase.h"
#include "video/VideoThumbLoader.h"
-#include "video/guilib/VideoSelectAction.h"
+#include "video/guilib/VideoAction.h"
#include "windows/GUIMediaWindow.h"
namespace
{
class CVideoSelectActionProcessor;
+class CVideoPlayActionProcessor;
} // unnamed namespace
class CGUIWindowVideoBase : public CGUIMediaWindow, public IBackgroundLoaderObserver
{
friend class ::CVideoSelectActionProcessor;
+ friend class ::CVideoPlayActionProcessor;
public:
CGUIWindowVideoBase(int id, const std::string &xmlFile);
@@ -93,7 +95,7 @@ protected:
\param action the action to perform
\return true if the action is performed, false otherwise
*/
- bool OnFileAction(int item, VIDEO::GUILIB::SelectAction action, const std::string& player);
+ bool OnFileAction(int item, VIDEO::GUILIB::Action action, const std::string& player);
void OnRestartItem(int iItem, const std::string &player = "");
bool OnPlayOrResumeItem(int iItem, const std::string& player = "");
@@ -115,6 +117,9 @@ protected:
bool OnPlayStackPart(int itemIndex, unsigned int partNumber);
+ void UpdateVideoVersionItems();
+ void UpdateVideoVersionItemsLabel(const std::string& directory);
+
CGUIDialogProgress* m_dlgProgress;
CVideoDatabase m_database;
diff --git a/xbmc/video/windows/GUIWindowVideoNav.cpp b/xbmc/video/windows/GUIWindowVideoNav.cpp
index 0a626e90d2..17fbd17972 100644
--- a/xbmc/video/windows/GUIWindowVideoNav.cpp
+++ b/xbmc/video/windows/GUIWindowVideoNav.cpp
@@ -27,7 +27,6 @@
#include "messaging/helpers/DialogOKHelper.h"
#include "music/MusicDatabase.h"
#include "profiles/ProfileManager.h"
-#include "pvr/recordings/PVRRecording.h"
#include "settings/AdvancedSettings.h"
#include "settings/MediaSettings.h"
#include "settings/MediaSourceSettings.h"
@@ -42,6 +41,7 @@
#include "video/VideoInfoScanner.h"
#include "video/VideoLibraryQueue.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
+#include "video/dialogs/GUIDialogVideoVersion.h"
#include "view/GUIViewState.h"
#include <utility>
@@ -491,14 +491,26 @@ bool CGUIWindowVideoNav::GetDirectory(const std::string &strDirectory, CFileItem
}
CVideoDbUrl videoUrl;
- if (videoUrl.FromString(items.GetPath()) && items.GetContent() == "tags" &&
- !items.Contains("newtag://" + videoUrl.GetType()))
+ if (videoUrl.FromString(items.GetPath()))
{
- CFileItemPtr newTag(new CFileItem("newtag://" + videoUrl.GetType(), false));
- newTag->SetLabel(g_localizeStrings.Get(20462));
- newTag->SetLabelPreformatted(true);
- newTag->SetSpecialSort(SortSpecialOnTop);
- items.Add(newTag);
+ if (items.GetContent() == "tags" && !items.Contains("newtag://" + videoUrl.GetType()))
+ {
+ const auto newTag{std::make_shared<CFileItem>("newtag://" + videoUrl.GetType(), false)};
+ newTag->SetLabel(g_localizeStrings.Get(20462));
+ newTag->SetLabelPreformatted(true);
+ newTag->SetSpecialSort(SortSpecialOnTop);
+ items.Add(newTag);
+ }
+ else if (items.GetContent() == "videoversions" &&
+ !items.Contains("newvideoversion://" + videoUrl.GetType()))
+ {
+ const auto newVideoVersion{
+ std::make_shared<CFileItem>("newvideoversion://" + videoUrl.GetType(), false)};
+ newVideoVersion->SetLabel(g_localizeStrings.Get(40004));
+ newVideoVersion->SetLabelPreformatted(true);
+ newVideoVersion->SetSpecialSort(SortSpecialOnTop);
+ items.Add(newVideoVersion);
+ }
}
}
return bResult;
@@ -684,10 +696,9 @@ void CGUIWindowVideoNav::OnDeleteItem(const CFileItemPtr& pItem)
if (!m_vecItems->IsVideoDb() && !pItem->IsVideoDb())
{
- if (!pItem->IsPath("newsmartplaylist://video") &&
- !pItem->IsPath("special://videoplaylists/") &&
- !pItem->IsPath("sources://video/") &&
- !URIUtils::IsProtocol(pItem->GetPath(), "newtag"))
+ if (!pItem->IsPath("newsmartplaylist://video") && !pItem->IsPath("special://videoplaylists/") &&
+ !pItem->IsPath("sources://video/") && !URIUtils::IsProtocol(pItem->GetPath(), "newtag") &&
+ !URIUtils::IsProtocol(pItem->GetPath(), "newvideoversion"))
CGUIWindowVideoBase::OnDeleteItem(pItem);
}
else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "videodb://movies/sets/") &&
@@ -818,7 +829,8 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt
item->GetVideoInfoTag()->m_type == MediaTypeEpisode || // episodes
item->GetVideoInfoTag()->m_type == MediaTypeMusicVideo || // musicvideos
item->GetVideoInfoTag()->m_type == "tag" || // tags
- item->GetVideoInfoTag()->m_type == MediaTypeVideoCollection)) // sets
+ item->GetVideoInfoTag()->m_type == MediaTypeVideoCollection || // sets
+ item->GetVideoInfoTag()->m_type == MediaTypeVideoVersion)) // videoversions
{
buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
}
@@ -898,7 +910,11 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
{
case CONTEXT_BUTTON_EDIT:
{
- CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
+ CONTEXT_BUTTON ret =
+ item->GetVideoInfoTag()->m_type == MediaTypeVideoVersion
+ ? (CONTEXT_BUTTON)CGUIDialogVideoVersion::ManageVideoVersionContextMenu(item)
+ : (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
+
if (ret != CONTEXT_BUTTON_CANCELLED)
{
Refresh(true);
@@ -1045,6 +1061,20 @@ bool CGUIWindowVideoNav::OnClick(int iItem, const std::string &player)
Refresh(true);
return true;
}
+ else if (StringUtils::StartsWithNoCase(item->GetPath(), "newvideoversion://"))
+ {
+ // dont allow update while scanning
+ if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{14057});
+ return true;
+ }
+
+ CGUIDialogVideoVersion::NewVideoVersion();
+
+ Refresh(true);
+ return true;
+ }
return CGUIWindowVideoBase::OnClick(iItem, player);
}
diff --git a/xbmc/video/windows/GUIWindowVideoPlaylist.cpp b/xbmc/video/windows/GUIWindowVideoPlaylist.cpp
index 6b96fde98f..1af835c793 100644
--- a/xbmc/video/windows/GUIWindowVideoPlaylist.cpp
+++ b/xbmc/video/windows/GUIWindowVideoPlaylist.cpp
@@ -15,7 +15,6 @@
#include "Util.h"
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
-#include "cores/playercorefactory/PlayerCoreFactory.h"
#include "dialogs/GUIDialogSmartPlaylistEditor.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIKeyboardFactory.h"
@@ -32,22 +31,22 @@
#include "utils/Variant.h"
#include "utils/log.h"
-#define CONTROL_BTNVIEWASICONS 2
-#define CONTROL_BTNSORTBY 3
-#define CONTROL_BTNSORTASC 4
-#define CONTROL_LABELFILES 12
+#define CONTROL_BTNVIEWASICONS 2
+#define CONTROL_BTNSORTBY 3
+#define CONTROL_BTNSORTASC 4
+#define CONTROL_LABELFILES 12
-#define CONTROL_BTNSHUFFLE 20
-#define CONTROL_BTNSAVE 21
-#define CONTROL_BTNCLEAR 22
+#define CONTROL_BTNSHUFFLE 20
+#define CONTROL_BTNSAVE 21
+#define CONTROL_BTNCLEAR 22
-#define CONTROL_BTNPLAY 23
-#define CONTROL_BTNNEXT 24
-#define CONTROL_BTNPREVIOUS 25
-#define CONTROL_BTNREPEAT 26
+#define CONTROL_BTNPLAY 23
+#define CONTROL_BTNNEXT 24
+#define CONTROL_BTNPREVIOUS 25
+#define CONTROL_BTNREPEAT 26
CGUIWindowVideoPlaylist::CGUIWindowVideoPlaylist()
-: CGUIWindowVideoBase(WINDOW_VIDEO_PLAYLIST, "MyPlaylist.xml")
+ : CGUIWindowVideoBase(WINDOW_VIDEO_PLAYLIST, "MyPlaylist.xml")
{
m_movingFrom = -1;
}
@@ -75,16 +74,16 @@ void CGUIWindowVideoPlaylist::OnPrepareFileItems(CFileItemList& items)
bool CGUIWindowVideoPlaylist::OnMessage(CGUIMessage& message)
{
- switch ( message.GetMessage() )
+ switch (message.GetMessage())
{
- case GUI_MSG_PLAYLISTPLAYER_REPEAT:
+ case GUI_MSG_PLAYLISTPLAYER_REPEAT:
{
UpdateButtons();
}
break;
- case GUI_MSG_PLAYLISTPLAYER_RANDOM:
- case GUI_MSG_PLAYLIST_CHANGED:
+ case GUI_MSG_PLAYLISTPLAYER_RANDOM:
+ case GUI_MSG_PLAYLIST_CHANGED:
{
// global playlist changed outside playlist window
UpdateButtons();
@@ -95,17 +94,16 @@ bool CGUIWindowVideoPlaylist::OnMessage(CGUIMessage& message)
m_iLastControl = CONTROL_BTNVIEWASICONS;
SET_CONTROL_FOCUS(m_iLastControl, 0);
}
-
}
break;
- case GUI_MSG_WINDOW_DEINIT:
+ case GUI_MSG_WINDOW_DEINIT:
{
m_movingFrom = -1;
}
break;
- case GUI_MSG_WINDOW_INIT:
+ case GUI_MSG_WINDOW_INIT:
{
m_vecItems->SetPath("playlistvideo://");
@@ -132,7 +130,7 @@ bool CGUIWindowVideoPlaylist::OnMessage(CGUIMessage& message)
}
break;
- case GUI_MSG_CLICKED:
+ case GUI_MSG_CLICKED:
{
int iControl = message.GetSenderId();
if (iControl == CONTROL_BTNSHUFFLE)
@@ -197,7 +195,7 @@ bool CGUIWindowVideoPlaylist::OnMessage(CGUIMessage& message)
UpdateButtons();
}
- else if (m_viewControl.HasControl(iControl)) // list/thumb control
+ else if (m_viewControl.HasControl(iControl)) // list/thumb control
{
int iAction = message.GetParam1();
int iItem = m_viewControl.GetSelectedItem();
@@ -213,7 +211,7 @@ bool CGUIWindowVideoPlaylist::OnMessage(CGUIMessage& message)
return CGUIWindowVideoBase::OnMessage(message);
}
-bool CGUIWindowVideoPlaylist::OnAction(const CAction &action)
+bool CGUIWindowVideoPlaylist::OnAction(const CAction& action)
{
if (action.GetID() == ACTION_PARENT_DIR)
{
@@ -244,7 +242,9 @@ bool CGUIWindowVideoPlaylist::OnBack(int actionID)
return CGUIWindowVideoBase::OnBack(actionID);
}
-bool CGUIWindowVideoPlaylist::MoveCurrentPlayListItem(int iItem, int iAction, bool bUpdate /* = true */)
+bool CGUIWindowVideoPlaylist::MoveCurrentPlayListItem(int iItem,
+ int iAction,
+ bool bUpdate /* = true */)
{
int iSelected = iItem;
int iNew = iSelected;
@@ -286,7 +286,6 @@ bool CGUIWindowVideoPlaylist::MoveCurrentPlayListItem(int iItem, int iAction, bo
return false;
}
-
void CGUIWindowVideoPlaylist::ClearPlayList()
{
ClearFileItems();
@@ -304,7 +303,7 @@ void CGUIWindowVideoPlaylist::ClearPlayList()
void CGUIWindowVideoPlaylist::UpdateButtons()
{
// Update playlist buttons
- if (m_vecItems->Size() )
+ if (m_vecItems->Size())
{
CONTROL_ENABLE(CONTROL_BTNCLEAR);
CONTROL_ENABLE(CONTROL_BTNSAVE);
@@ -360,9 +359,10 @@ void CGUIWindowVideoPlaylist::UpdateButtons()
MarkPlaying();
}
-bool CGUIWindowVideoPlaylist::OnPlayMedia(int iItem, const std::string &player)
+bool CGUIWindowVideoPlaylist::OnPlayMedia(int iItem, const std::string& player)
{
- if ( iItem < 0 || iItem >= m_vecItems->Size() ) return false;
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return false;
if (g_partyModeManager.IsEnabled())
g_partyModeManager.Play(iItem);
else
@@ -377,7 +377,8 @@ bool CGUIWindowVideoPlaylist::OnPlayMedia(int iItem, const std::string &player)
CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::TYPE_VIDEO)[iItem];
pPlaylistItem->SetStartOffset(pItem->GetStartOffset());
if (pPlaylistItem->HasVideoInfoTag() && pItem->HasVideoInfoTag())
- pPlaylistItem->GetVideoInfoTag()->SetResumePoint(pItem->GetVideoInfoTag()->GetResumePoint());
+ pPlaylistItem->GetVideoInfoTag()->SetResumePoint(
+ pItem->GetVideoInfoTag()->GetResumePoint());
}
// now play item
CServiceBroker::GetPlaylistPlayer().Play(iItem, player);
@@ -393,7 +394,7 @@ void CGUIWindowVideoPlaylist::RemovePlayListItem(int iItem)
// The current playing song can't be removed
if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_VIDEO &&
appPlayer->IsPlayingVideo() && CServiceBroker::GetPlaylistPlayer().GetCurrentSong() == iItem)
- return ;
+ return;
CServiceBroker::GetPlaylistPlayer().Remove(PLAYLIST::TYPE_VIDEO, iItem);
@@ -415,15 +416,16 @@ void CGUIWindowVideoPlaylist::RemovePlayListItem(int iItem)
void CGUIWindowVideoPlaylist::SavePlayList()
{
std::string strNewFileName;
- if (CGUIKeyboardFactory::ShowAndGetInput(strNewFileName, CVariant{g_localizeStrings.Get(16012)}, false))
+ if (CGUIKeyboardFactory::ShowAndGetInput(strNewFileName, CVariant{g_localizeStrings.Get(16012)},
+ false))
{
// need 2 rename it
strNewFileName = CUtil::MakeLegalFileName(std::move(strNewFileName));
strNewFileName += ".m3u8";
- std::string strPath = URIUtils::AddFileToFolder(
- CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH),
- "video",
- strNewFileName);
+ std::string strPath =
+ URIUtils::AddFileToFolder(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_SYSTEM_PLAYLISTSPATH),
+ "video", strNewFileName);
PLAYLIST::CPlayListM3U playlist;
playlist.Add(*m_vecItems);
@@ -433,37 +435,17 @@ void CGUIWindowVideoPlaylist::SavePlayList()
}
}
-void CGUIWindowVideoPlaylist::GetContextButtons(int itemNumber, CContextButtons &buttons)
+void CGUIWindowVideoPlaylist::GetContextButtons(int itemNumber, CContextButtons& buttons)
{
int itemPlaying = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
if (m_movingFrom >= 0)
{
if (itemNumber != m_movingFrom && (!g_partyModeManager.IsEnabled() || itemNumber > itemPlaying))
- buttons.Add(CONTEXT_BUTTON_MOVE_HERE, 13252); // move item here
+ buttons.Add(CONTEXT_BUTTON_MOVE_HERE, 13252); // move item here
buttons.Add(CONTEXT_BUTTON_CANCEL_MOVE, 13253);
-
}
else
{
- if (itemNumber > -1)
- {
- CFileItemPtr item = m_vecItems->Get(itemNumber);
-
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- // check what players we have, if we have multiple display play with option
- std::vector<std::string> players;
- if (item->IsVideoDb())
- {
- CFileItem item2(item->GetVideoInfoTag()->m_strFileNameAndPath, false);
- playerCoreFactory.GetPlayers(item2, players);
- }
- else
- playerCoreFactory.GetPlayers(*item, players);
-
- if (players.size() > 1)
- buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213); // Play With...
- }
if (itemNumber > (g_partyModeManager.IsEnabled() ? 1 : 0))
buttons.Add(CONTEXT_BUTTON_MOVE_ITEM_UP, 13332);
if (itemNumber + 1 < m_vecItems->Size())
@@ -477,7 +459,7 @@ void CGUIWindowVideoPlaylist::GetContextButtons(int itemNumber, CContextButtons
if (g_partyModeManager.IsEnabled())
{
buttons.Add(CONTEXT_BUTTON_EDIT_PARTYMODE, 21439);
- buttons.Add(CONTEXT_BUTTON_CANCEL_PARTYMODE, 588); // cancel party mode
+ buttons.Add(CONTEXT_BUTTON_CANCEL_PARTYMODE, 588); // cancel party mode
}
}
@@ -485,72 +467,47 @@ bool CGUIWindowVideoPlaylist::OnContextButton(int itemNumber, CONTEXT_BUTTON but
{
switch (button)
{
- case CONTEXT_BUTTON_PLAY_WITH:
- {
- CFileItemPtr item;
- if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
- item = m_vecItems->Get(itemNumber);
- if (!item)
- break;
-
- const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
-
- std::vector<std::string> players;
- if (item->IsVideoDb())
- {
- CFileItem item2(*item->GetVideoInfoTag());
- playerCoreFactory.GetPlayers(item2, players);
- }
- else
- playerCoreFactory.GetPlayers(*item, players);
-
- std::string player = playerCoreFactory.SelectPlayerDialog(players);
- if (!player.empty())
- OnClick(itemNumber, player);
+ case CONTEXT_BUTTON_MOVE_ITEM:
+ m_movingFrom = itemNumber;
return true;
- }
- case CONTEXT_BUTTON_MOVE_ITEM:
- m_movingFrom = itemNumber;
- return true;
-
- case CONTEXT_BUTTON_MOVE_HERE:
- if (m_movingFrom >= 0)
- MoveItem(m_movingFrom, itemNumber);
- m_movingFrom = -1;
- return true;
+ case CONTEXT_BUTTON_MOVE_HERE:
+ if (m_movingFrom >= 0)
+ MoveItem(m_movingFrom, itemNumber);
+ m_movingFrom = -1;
+ return true;
- case CONTEXT_BUTTON_CANCEL_MOVE:
- m_movingFrom = -1;
- return true;
+ case CONTEXT_BUTTON_CANCEL_MOVE:
+ m_movingFrom = -1;
+ return true;
- case CONTEXT_BUTTON_MOVE_ITEM_UP:
- OnMove(itemNumber, ACTION_MOVE_ITEM_UP);
- return true;
+ case CONTEXT_BUTTON_MOVE_ITEM_UP:
+ OnMove(itemNumber, ACTION_MOVE_ITEM_UP);
+ return true;
- case CONTEXT_BUTTON_MOVE_ITEM_DOWN:
- OnMove(itemNumber, ACTION_MOVE_ITEM_DOWN);
- return true;
+ case CONTEXT_BUTTON_MOVE_ITEM_DOWN:
+ OnMove(itemNumber, ACTION_MOVE_ITEM_DOWN);
+ return true;
- case CONTEXT_BUTTON_DELETE:
- RemovePlayListItem(itemNumber);
- return true;
- case CONTEXT_BUTTON_CANCEL_PARTYMODE:
- g_partyModeManager.Disable();
- return true;
- case CONTEXT_BUTTON_EDIT_PARTYMODE:
- {
- std::string playlist = "special://profile/PartyMode-Video.xsp";
- if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist))
- {
- // apply new rules
+ case CONTEXT_BUTTON_DELETE:
+ RemovePlayListItem(itemNumber);
+ return true;
+ case CONTEXT_BUTTON_CANCEL_PARTYMODE:
g_partyModeManager.Disable();
- g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO);
+ return true;
+ case CONTEXT_BUTTON_EDIT_PARTYMODE:
+ {
+ std::string playlist = "special://profile/PartyMode-Video.xsp";
+ if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist))
+ {
+ // apply new rules
+ g_partyModeManager.Disable();
+ g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO);
+ }
+ return true;
}
- return true;
- }
- default:
- break;
+ default:
+ break;
}
return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
@@ -558,14 +515,17 @@ bool CGUIWindowVideoPlaylist::OnContextButton(int itemNumber, CONTEXT_BUTTON but
void CGUIWindowVideoPlaylist::OnMove(int iItem, int iAction)
{
- if (iItem < 0 || iItem >= m_vecItems->Size()) return;
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return;
MoveCurrentPlayListItem(iItem, iAction);
}
void CGUIWindowVideoPlaylist::MoveItem(int iStart, int iDest)
{
- if (iStart < 0 || iStart >= m_vecItems->Size()) return;
- if (iDest < 0 || iDest >= m_vecItems->Size()) return;
+ if (iStart < 0 || iStart >= m_vecItems->Size())
+ return;
+ if (iDest < 0 || iDest >= m_vecItems->Size())
+ return;
// default to move up
int iAction = ACTION_MOVE_ITEM_UP;
@@ -606,4 +566,3 @@ void CGUIWindowVideoPlaylist::MarkPlaying()
m_vecItems->Get(iSong)->Select(true);
}*/
}
-
diff --git a/xbmc/video/windows/VideoFileItemListModifier.cpp b/xbmc/video/windows/VideoFileItemListModifier.cpp
index 4205d68af1..b17a71db87 100644
--- a/xbmc/video/windows/VideoFileItemListModifier.cpp
+++ b/xbmc/video/windows/VideoFileItemListModifier.cpp
@@ -18,6 +18,8 @@
#include "video/VideoDatabase.h"
#include "video/VideoDbUrl.h"
+#include <memory>
+
using namespace XFILE::VIDEODATABASEDIRECTORY;
bool CVideoFileItemListModifier::CanModify(const CFileItemList &items) const
@@ -65,7 +67,7 @@ void CVideoFileItemListModifier::AddQueuingFolder(CFileItemList& items)
case NODE_TYPE_SEASONS:
{
const std::string& strLabel = g_localizeStrings.Get(20366);
- pItem.reset(new CFileItem(strLabel)); // "All Seasons"
+ pItem = std::make_shared<CFileItem>(strLabel); // "All Seasons"
videoUrl.AppendPath("-1/");
pItem->SetPath(videoUrl.ToString());
// set the number of watched and unwatched items accordingly
@@ -120,7 +122,7 @@ void CVideoFileItemListModifier::AddQueuingFolder(CFileItemList& items)
}
break;
case NODE_TYPE_MUSICVIDEOS_ALBUM:
- pItem.reset(new CFileItem("* " + g_localizeStrings.Get(16100))); // "* All Videos"
+ pItem = std::make_shared<CFileItem>("* " + g_localizeStrings.Get(16100)); // "* All Videos"
videoUrl.AppendPath("-1/");
pItem->SetPath(videoUrl.ToString());
break;
diff --git a/xbmc/windowing/GraphicContext.cpp b/xbmc/windowing/GraphicContext.cpp
index a5513fa191..e3300e925c 100644
--- a/xbmc/windowing/GraphicContext.cpp
+++ b/xbmc/windowing/GraphicContext.cpp
@@ -37,7 +37,7 @@ void CGraphicContext::SetOrigin(float x, float y)
if (!m_origins.empty())
m_origins.push(CPoint(x,y) + m_origins.top());
else
- m_origins.push(CPoint(x,y));
+ m_origins.emplace(x, y);
AddTransform(TransformMatrix::CreateTranslation(x, y));
}
@@ -718,10 +718,10 @@ void CGraphicContext::SetScalingResolution(const RESOLUTION_INFO &res, bool need
// reset our origin and camera
while (!m_origins.empty())
m_origins.pop();
- m_origins.push(CPoint(0, 0));
+ m_origins.emplace(.0f, .0f);
while (!m_cameras.empty())
m_cameras.pop();
- m_cameras.push(CPoint(0.5f*m_iScreenWidth, 0.5f*m_iScreenHeight));
+ m_cameras.emplace(0.5f * m_iScreenWidth, 0.5f * m_iScreenHeight);
while (!m_stereoFactors.empty())
m_stereoFactors.pop();
m_stereoFactors.push(0.0f);
diff --git a/xbmc/windowing/WinSystem.cpp b/xbmc/windowing/WinSystem.cpp
index 33492876dc..aa8cbde021 100644
--- a/xbmc/windowing/WinSystem.cpp
+++ b/xbmc/windowing/WinSystem.cpp
@@ -10,6 +10,9 @@
#include "ServiceBroker.h"
#include "guilib/DispResource.h"
+#if HAS_GLES
+#include "guilib/GUIFontTTFGL.h"
+#endif
#include "powermanagement/DPMSSupport.h"
#include "settings/DisplaySettings.h"
#include "settings/Settings.h"
@@ -18,13 +21,14 @@
#include "utils/StringUtils.h"
#include "windowing/GraphicContext.h"
+#include <memory>
#include <mutex>
const char* CWinSystemBase::SETTING_WINSYSTEM_IS_HDR_DISPLAY = "winsystem.ishdrdisplay";
CWinSystemBase::CWinSystemBase()
{
- m_gfxContext.reset(new CGraphicContext());
+ m_gfxContext = std::make_unique<CGraphicContext>();
}
CWinSystemBase::~CWinSystemBase() = default;
@@ -255,7 +259,8 @@ KODI::WINDOWING::COSScreenSaverManager* CWinSystemBase::GetOSScreenSaver()
auto impl = GetOSScreenSaverImpl();
if (impl)
{
- m_screenSaverManager.reset(new KODI::WINDOWING::COSScreenSaverManager(std::move(impl)));
+ m_screenSaverManager =
+ std::make_unique<KODI::WINDOWING::COSScreenSaverManager>(std::move(impl));
}
}
diff --git a/xbmc/windowing/WindowSystemFactory.cpp b/xbmc/windowing/WindowSystemFactory.cpp
index edb6098e99..9f6dc84ed0 100644
--- a/xbmc/windowing/WindowSystemFactory.cpp
+++ b/xbmc/windowing/WindowSystemFactory.cpp
@@ -38,5 +38,5 @@ std::unique_ptr<CWinSystemBase> CWindowSystemFactory::CreateWindowSystem(const s
void CWindowSystemFactory::RegisterWindowSystem(
const std::function<std::unique_ptr<CWinSystemBase>()>& createFunction, const std::string& name)
{
- m_windowSystems.emplace_back(std::make_pair(name, createFunction));
+ m_windowSystems.emplace_back(name, createFunction);
}
diff --git a/xbmc/windowing/X11/WinSystemX11.cpp b/xbmc/windowing/X11/WinSystemX11.cpp
index 3cf1020c57..562df661fe 100644
--- a/xbmc/windowing/X11/WinSystemX11.cpp
+++ b/xbmc/windowing/X11/WinSystemX11.cpp
@@ -26,6 +26,7 @@
#include "utils/log.h"
#include "windowing/GraphicContext.h"
+#include <memory>
#include <mutex>
#include <string>
#include <vector>
@@ -496,7 +497,7 @@ std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> CWinSystemX11::GetOSScreenSaver
std::unique_ptr<IOSScreenSaver> ret;
if (m_dpy)
{
- ret.reset(new COSScreenSaverX11(m_dpy));
+ ret = std::make_unique<COSScreenSaverX11>(m_dpy);
}
return ret;
}
diff --git a/xbmc/windowing/X11/WinSystemX11GLContext.cpp b/xbmc/windowing/X11/WinSystemX11GLContext.cpp
index 7b3e9e40fe..44ddb89856 100644
--- a/xbmc/windowing/X11/WinSystemX11GLContext.cpp
+++ b/xbmc/windowing/X11/WinSystemX11GLContext.cpp
@@ -10,8 +10,10 @@
#include "GLContextEGL.h"
#include "OptionalsReg.h"
+#include "ServiceBroker.h"
#include "VideoSyncOML.h"
#include "X11DPMSSupport.h"
+#include "application/AppParams.h"
#include "application/ApplicationComponents.h"
#include "application/ApplicationSkinHandling.h"
#include "cores/RetroPlayer/process/X11/RPProcessInfoX11.h"
@@ -26,6 +28,7 @@
#include "windowing/GraphicContext.h"
#include "windowing/WindowSystemFactory.h"
+#include <memory>
#include <mutex>
#include <vector>
@@ -268,9 +271,10 @@ bool CWinSystemX11GLContext::RefreshGLContext(bool force)
std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
bool isNvidia = (gpuvendor.compare(0, 6, "nvidia") == 0);
bool isIntel = (gpuvendor.compare(0, 5, "intel") == 0);
- std::string gli = (getenv("KODI_GL_INTERFACE") != nullptr) ? getenv("KODI_GL_INTERFACE") : "";
- if (gli != "GLX")
+ std::string_view gli = CServiceBroker::GetAppParams()->GetGlInterface();
+
+ if (gli != "glx")
{
m_pGLContext = new CGLContextEGL(m_dpy, EGL_OPENGL_API);
success = m_pGLContext->Refresh(force, m_screen, m_glWindow, m_newGlContext);
@@ -289,11 +293,11 @@ bool CWinSystemX11GLContext::RefreshGLContext(bool force)
VAAPIRegister(m_vaapiProxy.get(), deepColor);
return true;
}
- if (isIntel || gli == "EGL")
+ if (isIntel || gli == "egl")
return true;
}
}
- else if (gli == "EGL_PB")
+ else if (gli == "egl-pb")
{
success = m_pGLContext->CreatePB();
if (success)
@@ -320,7 +324,7 @@ std::unique_ptr<CVideoSync> CWinSystemX11GLContext::GetVideoSync(CVideoReference
if (dynamic_cast<CGLContextEGL*>(m_pGLContext))
{
- pVSync.reset(new CVideoSyncOML(clock, *this));
+ pVSync = std::make_unique<CVideoSyncOML>(clock, *this);
}
else
{
diff --git a/xbmc/windowing/XBMC_events.h b/xbmc/windowing/XBMC_events.h
index 744cbff3f3..817425fa6f 100644
--- a/xbmc/windowing/XBMC_events.h
+++ b/xbmc/windowing/XBMC_events.h
@@ -17,6 +17,9 @@ typedef enum
XBMC_NOEVENT = 0, /* Unused (do not remove) */
XBMC_KEYDOWN, /* Keys pressed */
XBMC_KEYUP, /* Keys released */
+ XBMC_KEYCOMPOSING_COMPOSING, /* A composed key (sequence) is under processing */
+ XBMC_KEYCOMPOSING_FINISHED, /* A composed key is finished */
+ XBMC_KEYCOMPOSING_CANCELLED, /* A composed key is cancelled */
XBMC_MOUSEMOTION, /* Mouse moved */
XBMC_MOUSEBUTTONDOWN, /* Mouse button pressed */
XBMC_MOUSEBUTTONUP, /* Mouse button released */
diff --git a/xbmc/windowing/android/WinSystemAndroid.cpp b/xbmc/windowing/android/WinSystemAndroid.cpp
index 378968d886..bd4bae2892 100644
--- a/xbmc/windowing/android/WinSystemAndroid.cpp
+++ b/xbmc/windowing/android/WinSystemAndroid.cpp
@@ -24,7 +24,6 @@
#include "settings/DisplaySettings.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
-#include "system_egl.h"
#include "utils/HDRCapabilities.h"
#include "utils/log.h"
#include "windowing/GraphicContext.h"
@@ -35,9 +34,12 @@
#include "platform/android/media/drm/MediaDrmCryptoSession.h"
#include <float.h>
+#include <memory>
#include <mutex>
#include <string.h>
+#include "system_egl.h"
+
#include <EGL/eglplatform.h>
using namespace KODI;
@@ -54,7 +56,7 @@ CWinSystemAndroid::CWinSystemAndroid()
m_android = nullptr;
- m_winEvents.reset(new CWinEventsAndroid());
+ m_winEvents = std::make_unique<CWinEventsAndroid>();
}
CWinSystemAndroid::~CWinSystemAndroid()
diff --git a/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp b/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp
index 3c08226677..a4cb02d710 100644
--- a/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp
+++ b/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp
@@ -17,11 +17,14 @@
#include "platform/android/activity/XBMCApp.h"
-#include <EGL/eglext.h>
+#include <memory>
+
#include <unistd.h>
#include "PlatformDefs.h"
+#include <EGL/eglext.h>
+
void CWinSystemAndroidGLESContext::Register()
{
KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem);
@@ -254,7 +257,10 @@ bool CWinSystemAndroidGLESContext::SetHDR(const VideoPicture* videoPicture)
CLog::Log(LOGDEBUG, "CWinSystemAndroidGLESContext::SetHDR: ColorSpace: {}", HDRColorSpace);
m_HDRColorSpace = HDRColorSpace;
- m_displayMetadata = m_HDRColorSpace == EGL_NONE ? nullptr : std::unique_ptr<AVMasteringDisplayMetadata>(new AVMasteringDisplayMetadata(videoPicture->displayMetadata));
+ m_displayMetadata =
+ m_HDRColorSpace == EGL_NONE
+ ? nullptr
+ : std::make_unique<AVMasteringDisplayMetadata>(videoPicture->displayMetadata);
// TODO: discuss with NVIDIA why this prevent turning HDR display off
//m_lightMetadata = !videoPicture || m_HDRColorSpace == EGL_NONE ? nullptr : std::unique_ptr<AVContentLightMetadata>(new AVContentLightMetadata(videoPicture->lightMetadata));
m_pGLContext.DestroySurface();
diff --git a/xbmc/windowing/gbm/WinSystemGbm.cpp b/xbmc/windowing/gbm/WinSystemGbm.cpp
index da5609490a..6569718b3d 100644
--- a/xbmc/windowing/gbm/WinSystemGbm.cpp
+++ b/xbmc/windowing/gbm/WinSystemGbm.cpp
@@ -278,7 +278,7 @@ void CWinSystemGbm::UpdateDisplayHardwareScaling(const RESOLUTION_INFO& resInfo)
SetFullScreen(true, resMutable, false);
}
-void CWinSystemGbm::FlipPage(bool rendered, bool videoLayer)
+void CWinSystemGbm::FlipPage(bool rendered, bool videoLayer, bool async)
{
if (m_videoLayerBridge && !videoLayer)
{
@@ -293,7 +293,7 @@ void CWinSystemGbm::FlipPage(bool rendered, bool videoLayer)
bo = m_GBM->GetDevice().GetSurface().LockFrontBuffer().Get();
}
- m_DRM->FlipPage(bo, rendered, videoLayer);
+ m_DRM->FlipPage(bo, rendered, videoLayer, async);
if (m_videoLayerBridge && !videoLayer)
{
@@ -310,14 +310,14 @@ bool CWinSystemGbm::UseLimitedColor()
bool CWinSystemGbm::Hide()
{
bool ret = m_DRM->SetActive(false);
- FlipPage(false, false);
+ FlipPage(false, false, false);
return ret;
}
bool CWinSystemGbm::Show(bool raise)
{
bool ret = m_DRM->SetActive(true);
- FlipPage(false, false);
+ FlipPage(false, false, false);
return ret;
}
diff --git a/xbmc/windowing/gbm/WinSystemGbm.h b/xbmc/windowing/gbm/WinSystemGbm.h
index 8993b6d278..9609675d72 100644
--- a/xbmc/windowing/gbm/WinSystemGbm.h
+++ b/xbmc/windowing/gbm/WinSystemGbm.h
@@ -49,7 +49,7 @@ public:
bool DisplayHardwareScalingEnabled() override;
void UpdateDisplayHardwareScaling(const RESOLUTION_INFO& resInfo) override;
- void FlipPage(bool rendered, bool videoLayer);
+ void FlipPage(bool rendered, bool videoLayer, bool async);
bool CanDoWindowed() override { return false; }
void UpdateResolutions() override;
diff --git a/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp b/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp
index 2fb742efed..fac914d5e9 100644
--- a/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp
+++ b/xbmc/windowing/gbm/WinSystemGbmEGLContext.cpp
@@ -58,6 +58,17 @@ bool CWinSystemGbmEGLContext::InitWindowSystemEGL(EGLint renderableType, EGLint
return false;
}
+ if (CEGLUtils::HasExtension(m_eglContext.GetEGLDisplay(), "EGL_ANDROID_native_fence_sync") &&
+ CEGLUtils::HasExtension(m_eglContext.GetEGLDisplay(), "EGL_KHR_fence_sync"))
+ {
+ m_eglFence = std::make_unique<KODI::UTILS::EGL::CEGLFence>(m_eglContext.GetEGLDisplay());
+ }
+ else
+ {
+ CLog::Log(LOGWARNING, "[GBM] missing support for EGL_KHR_fence_sync and "
+ "EGL_ANDROID_native_fence_sync - performance may be impacted");
+ }
+
return true;
}
diff --git a/xbmc/windowing/gbm/WinSystemGbmEGLContext.h b/xbmc/windowing/gbm/WinSystemGbmEGLContext.h
index 84f863d6d3..fbd52354ee 100644
--- a/xbmc/windowing/gbm/WinSystemGbmEGLContext.h
+++ b/xbmc/windowing/gbm/WinSystemGbmEGLContext.h
@@ -9,6 +9,7 @@
#pragma once
#include "WinSystemGbm.h"
+#include "utils/EGLFence.h"
#include "utils/EGLUtils.h"
#include "windowing/linux/WinSystemEGL.h"
@@ -46,6 +47,8 @@ protected:
bool InitWindowSystemEGL(EGLint renderableType, EGLint apiType);
virtual bool CreateContext() = 0;
+ std::unique_ptr<KODI::UTILS::EGL::CEGLFence> m_eglFence;
+
struct delete_CVaapiProxy
{
void operator()(CVaapiProxy *p) const;
diff --git a/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp b/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp
index e4ff49c618..adbb539f21 100644
--- a/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp
+++ b/xbmc/windowing/gbm/WinSystemGbmGLContext.cpp
@@ -119,13 +119,37 @@ void CWinSystemGbmGLContext::PresentRender(bool rendered, bool videoLayer)
{
if (rendered)
{
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ if (m_eglFence)
+ {
+ int fd = m_DRM->TakeOutFenceFd();
+ if (fd != -1)
+ {
+ m_eglFence->CreateKMSFence(fd);
+ m_eglFence->WaitSyncGPU();
+ }
+
+ m_eglFence->CreateGPUFence();
+ }
+#endif
+
if (!m_eglContext.TrySwapBuffers())
{
CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
throw std::runtime_error("eglSwapBuffers failed");
}
+
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ if (m_eglFence)
+ {
+ int fd = m_eglFence->FlushFence();
+ m_DRM->SetInFenceFd(fd);
+
+ m_eglFence->WaitSyncCPU();
+ }
+#endif
}
- CWinSystemGbm::FlipPage(rendered, videoLayer);
+ CWinSystemGbm::FlipPage(rendered, videoLayer, static_cast<bool>(m_eglFence));
if (m_dispReset && m_dispResetTimer.IsTimePast())
{
diff --git a/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp b/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp
index 0d071c31f1..ad80abf46c 100644
--- a/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp
+++ b/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp
@@ -128,13 +128,38 @@ void CWinSystemGbmGLESContext::PresentRender(bool rendered, bool videoLayer)
{
if (rendered)
{
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ if (m_eglFence)
+ {
+ int fd = m_DRM->TakeOutFenceFd();
+ if (fd != -1)
+ {
+ m_eglFence->CreateKMSFence(fd);
+ m_eglFence->WaitSyncGPU();
+ }
+
+ m_eglFence->CreateGPUFence();
+ }
+#endif
+
if (!m_eglContext.TrySwapBuffers())
{
CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
throw std::runtime_error("eglSwapBuffers failed");
}
+
+#if defined(EGL_ANDROID_native_fence_sync) && defined(EGL_KHR_fence_sync)
+ if (m_eglFence)
+ {
+ int fd = m_eglFence->FlushFence();
+ m_DRM->SetInFenceFd(fd);
+
+ m_eglFence->WaitSyncCPU();
+ }
+#endif
}
- CWinSystemGbm::FlipPage(rendered, videoLayer);
+
+ CWinSystemGbm::FlipPage(rendered, videoLayer, static_cast<bool>(m_eglFence));
if (m_dispReset && m_dispResetTimer.IsTimePast())
{
diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.cpp b/xbmc/windowing/gbm/drm/DRMAtomic.cpp
index 029b5cae81..ff7f137d60 100644
--- a/xbmc/windowing/gbm/drm/DRMAtomic.cpp
+++ b/xbmc/windowing/gbm/drm/DRMAtomic.cpp
@@ -111,6 +111,11 @@ void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool video
AddProperty(m_gui_plane, "CRTC_H", m_mode->vdisplay);
}
+ if (m_inFenceFd != -1)
+ {
+ AddProperty(m_crtc, "OUT_FENCE_PTR", reinterpret_cast<uint64_t>(&m_outFenceFd));
+ AddProperty(m_gui_plane, "IN_FENCE_FD", m_inFenceFd);
+ }
}
else if (videoLayer && !CServiceBroker::GetGUI()->GetWindowManager().HasVisibleControls())
{
@@ -146,6 +151,12 @@ void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool video
strerror(errno));
}
+ if (m_inFenceFd != -1)
+ {
+ close(m_inFenceFd);
+ m_inFenceFd = -1;
+ }
+
if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
{
if (drmModeDestroyPropertyBlob(m_fd, blob_id) != 0)
@@ -160,9 +171,10 @@ void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool video
m_req = m_atomicRequestQueue.back().get();
}
-void CDRMAtomic::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer)
+void CDRMAtomic::FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer, bool async)
{
struct drm_fb *drm_fb = nullptr;
+ uint32_t flags = 0;
if (rendered)
{
@@ -177,9 +189,10 @@ void CDRMAtomic::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer)
CLog::Log(LOGERROR, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__);
return;
}
- }
- uint32_t flags = 0;
+ if (async && !m_need_modeset)
+ flags |= DRM_MODE_ATOMIC_NONBLOCK;
+ }
if (m_need_modeset)
{
diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.h b/xbmc/windowing/gbm/drm/DRMAtomic.h
index ca2cd9a1d0..6b19657587 100644
--- a/xbmc/windowing/gbm/drm/DRMAtomic.h
+++ b/xbmc/windowing/gbm/drm/DRMAtomic.h
@@ -27,7 +27,7 @@ class CDRMAtomic : public CDRMUtils
public:
CDRMAtomic() = default;
~CDRMAtomic() override = default;
- void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) override;
+ void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer, bool async) override;
bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) override;
bool SetActive(bool active) override;
bool InitDrm() override;
diff --git a/xbmc/windowing/gbm/drm/DRMConnector.cpp b/xbmc/windowing/gbm/drm/DRMConnector.cpp
index 94f8e909ff..1b2fac4d06 100644
--- a/xbmc/windowing/gbm/drm/DRMConnector.cpp
+++ b/xbmc/windowing/gbm/drm/DRMConnector.cpp
@@ -11,6 +11,7 @@
#include "utils/XTimeUtils.h"
#include "utils/log.h"
+#include <algorithm>
#include <map>
using namespace KODI::WINDOWING::GBM;
diff --git a/xbmc/windowing/gbm/drm/DRMLegacy.cpp b/xbmc/windowing/gbm/drm/DRMLegacy.cpp
index 418d067e70..4e9c3a6b9f 100644
--- a/xbmc/windowing/gbm/drm/DRMLegacy.cpp
+++ b/xbmc/windowing/gbm/drm/DRMLegacy.cpp
@@ -108,7 +108,7 @@ bool CDRMLegacy::QueueFlip(struct gbm_bo *bo)
return true;
}
-void CDRMLegacy::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer)
+void CDRMLegacy::FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer, bool async)
{
if (rendered || videoLayer)
{
diff --git a/xbmc/windowing/gbm/drm/DRMLegacy.h b/xbmc/windowing/gbm/drm/DRMLegacy.h
index 2b7ff45617..e763f298f7 100644
--- a/xbmc/windowing/gbm/drm/DRMLegacy.h
+++ b/xbmc/windowing/gbm/drm/DRMLegacy.h
@@ -22,7 +22,7 @@ class CDRMLegacy : public CDRMUtils
public:
CDRMLegacy() = default;
~CDRMLegacy() override = default;
- void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) override;
+ void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer, bool async) override;
bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) override;
bool SetActive(bool active) override;
bool InitDrm() override;
diff --git a/xbmc/windowing/gbm/drm/DRMPlane.cpp b/xbmc/windowing/gbm/drm/DRMPlane.cpp
index 90b5660ed5..865beccacb 100644
--- a/xbmc/windowing/gbm/drm/DRMPlane.cpp
+++ b/xbmc/windowing/gbm/drm/DRMPlane.cpp
@@ -13,6 +13,8 @@
#include "utils/StringUtils.h"
#include "utils/log.h"
+#include <algorithm>
+
using namespace KODI::WINDOWING::GBM;
CDRMPlane::CDRMPlane(int fd, uint32_t plane) : CDRMObject(fd), m_plane(drmModeGetPlane(m_fd, plane))
diff --git a/xbmc/windowing/gbm/drm/DRMUtils.h b/xbmc/windowing/gbm/drm/DRMUtils.h
index 5327e35570..f92f716fc4 100644
--- a/xbmc/windowing/gbm/drm/DRMUtils.h
+++ b/xbmc/windowing/gbm/drm/DRMUtils.h
@@ -15,6 +15,7 @@
#include "windowing/Resolution.h"
#include "windowing/gbm/GBMUtils.h"
+#include <utility>
#include <vector>
#include <gbm.h>
@@ -39,7 +40,7 @@ class CDRMUtils
public:
CDRMUtils() = default;
virtual ~CDRMUtils();
- virtual void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) {}
+ virtual void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer, bool async) {}
virtual bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) { return false; }
virtual bool SetActive(bool active) { return false; }
virtual bool InitDrm();
@@ -62,6 +63,13 @@ public:
static uint32_t FourCCWithAlpha(uint32_t fourcc);
static uint32_t FourCCWithoutAlpha(uint32_t fourcc);
+ void SetInFenceFd(int fd) { m_inFenceFd = fd; }
+ int TakeOutFenceFd()
+ {
+ int fd{-1};
+ return std::exchange(m_outFenceFd, fd);
+ }
+
protected:
bool OpenDrm(bool needConnector);
drm_fb* DrmFbGetFromBo(struct gbm_bo *bo);
@@ -78,6 +86,9 @@ protected:
int m_width = 0;
int m_height = 0;
+ int m_inFenceFd{-1};
+ int m_outFenceFd{-1};
+
std::vector<std::unique_ptr<CDRMPlane>> m_planes;
private:
diff --git a/xbmc/windowing/gbm/drm/OffScreenModeSetting.h b/xbmc/windowing/gbm/drm/OffScreenModeSetting.h
index 4270d4ecb2..bba0db9a53 100644
--- a/xbmc/windowing/gbm/drm/OffScreenModeSetting.h
+++ b/xbmc/windowing/gbm/drm/OffScreenModeSetting.h
@@ -22,7 +22,7 @@ class COffScreenModeSetting : public CDRMUtils
public:
COffScreenModeSetting() = default;
~COffScreenModeSetting() override = default;
- void FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer) override {}
+ void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer, bool async) override {}
bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo) override { return false; }
bool SetActive(bool active) override { return false; }
bool InitDrm() override;
diff --git a/xbmc/windowing/ios/WinSystemIOS.mm b/xbmc/windowing/ios/WinSystemIOS.mm
index 651d654b75..8935a342a6 100644
--- a/xbmc/windowing/ios/WinSystemIOS.mm
+++ b/xbmc/windowing/ios/WinSystemIOS.mm
@@ -36,6 +36,7 @@
#import "platform/darwin/ios/IOSScreenManager.h"
#import "platform/darwin/ios/XBMCController.h"
+#include <memory>
#include <mutex>
#include <vector>
@@ -102,7 +103,7 @@ CWinSystemIOS::CWinSystemIOS() : CWinSystemBase()
m_bIsBackgrounded = false;
m_pDisplayLink = new CADisplayLinkWrapper;
m_pDisplayLink->callbackClass = [[IOSDisplayLinkCallback alloc] init];
- m_winEvents.reset(new CWinEventsIOS());
+ m_winEvents = std::make_unique<CWinEventsIOS>();
CAESinkDARWINIOS::Register();
}
diff --git a/xbmc/windowing/osx/CMakeLists.txt b/xbmc/windowing/osx/CMakeLists.txt
index 433f5cfe14..da0024f182 100644
--- a/xbmc/windowing/osx/CMakeLists.txt
+++ b/xbmc/windowing/osx/CMakeLists.txt
@@ -1,17 +1,14 @@
set(SOURCES CocoaDPMSSupport.cpp
OSScreenSaverOSX.cpp
- VideoSyncOsx.mm)
+ VideoSyncOsx.mm
+ WinEventsOSX.mm
+ WinEventsOSXImpl.mm
+ WinSystemOSX.mm)
set(HEADERS CocoaDPMSSupport.h
OSScreenSaverOSX.h
- VideoSyncOsx.h)
-
-if(NOT SDL_FOUND)
- list(APPEND SOURCES WinEventsOSX.mm
- WinEventsOSXImpl.mm
- WinSystemOSX.mm)
- list(APPEND HEADERS WinEventsOSX.h
- WinEventsOSXImpl.h
- WinSystemOSX.h)
-endif()
+ WinEventsOSX.h
+ WinEventsOSXImpl.h
+ VideoSyncOsx.h
+ WinSystemOSX.h)
core_add_library(windowing_osx)
diff --git a/xbmc/windowing/osx/OpenGL/CMakeLists.txt b/xbmc/windowing/osx/OpenGL/CMakeLists.txt
index c0136fcd97..61dc24867f 100644
--- a/xbmc/windowing/osx/OpenGL/CMakeLists.txt
+++ b/xbmc/windowing/osx/OpenGL/CMakeLists.txt
@@ -1,13 +1,10 @@
if(TARGET OpenGL::GL)
- set(SOURCES WinSystemOSXGL.mm)
- set(HEADERS WinSystemOSXGL.h)
-
- if(NOT SDL_FOUND)
- list(APPEND SOURCES OSXGLView.mm
- WindowControllerMacOS.mm)
- list(APPEND HEADERS OSXGLView.h
- WindowControllerMacOS.h)
- endif()
+ list(APPEND SOURCES OSXGLView.mm
+ WindowControllerMacOS.mm
+ WinSystemOSXGL.mm)
+ list(APPEND HEADERS OSXGLView.h
+ WindowControllerMacOS.h
+ WinSystemOSXGL.h)
core_add_library(windowing_osx_opengl)
diff --git a/xbmc/windowing/osx/OpenGL/WinSystemOSXGL.h b/xbmc/windowing/osx/OpenGL/WinSystemOSXGL.h
index c38e5e3491..aa6f0811a7 100644
--- a/xbmc/windowing/osx/OpenGL/WinSystemOSXGL.h
+++ b/xbmc/windowing/osx/OpenGL/WinSystemOSXGL.h
@@ -8,12 +8,8 @@
#pragma once
-#if defined(HAS_SDL)
-#include "windowing/osx/SDL/WinSystemOSXSDL.h"
-#else
-#include "windowing/osx/WinSystemOSX.h"
-#endif
#include "rendering/gl/RenderSystemGL.h"
+#include "windowing/osx/WinSystemOSX.h"
class CWinSystemOSXGL : public CWinSystemOSX, public CRenderSystemGL
{
diff --git a/xbmc/windowing/osx/SDL/CMakeLists.txt b/xbmc/windowing/osx/SDL/CMakeLists.txt
deleted file mode 100644
index 33e25d8432..0000000000
--- a/xbmc/windowing/osx/SDL/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-if(SDL_FOUND)
- set(SOURCES WinEventsSDL.cpp
- WinSystemOSXSDL.mm)
- set(HEADERS WinEventsSDL.h
- WinSystemOSXSDL.h)
-endif()
-
-if(SOURCES)
- core_add_library(windowing_osx_SDL)
-endif()
diff --git a/xbmc/windowing/osx/SDL/WinEventsSDL.cpp b/xbmc/windowing/osx/SDL/WinEventsSDL.cpp
deleted file mode 100644
index 229f176fac..0000000000
--- a/xbmc/windowing/osx/SDL/WinEventsSDL.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2005-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 "WinEventsSDL.h"
-
-#include "GUIUserMessages.h"
-#include "ServiceBroker.h"
-#include "application/AppInboundProtocol.h"
-#include "application/Application.h"
-#include "guilib/GUIComponent.h"
-#include "guilib/GUIWindowManager.h"
-#include "input/InputManager.h"
-#include "input/Key.h"
-#include "input/mouse/MouseStat.h"
-#include "messaging/ApplicationMessenger.h"
-#include "settings/DisplaySettings.h"
-#include "windowing/WinSystem.h"
-
-#include "platform/darwin/osx/CocoaInterface.h"
-
-bool CWinEventsOSX::MessagePump()
-{
- SDL_Event event;
- bool ret = false;
-
- while (SDL_PollEvent(&event))
- {
- switch(event.type)
- {
- case SDL_QUIT:
- if (!g_application.m_bStop)
- CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
- break;
-
- case SDL_ACTIVEEVENT:
- //If the window was inconified or restored
- if( event.active.state & SDL_APPACTIVE )
- {
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- appPort->SetRenderGUI(event.active.gain != 0);
- CServiceBroker::GetWinSystem()->NotifyAppActiveChange(g_application.GetRenderGUI());
- }
- else if (event.active.state & SDL_APPINPUTFOCUS)
- {
- g_application.m_AppFocused = event.active.gain != 0;
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort && g_application.m_AppFocused)
- appPort->SetRenderGUI(g_application.m_AppFocused);
- CServiceBroker::GetWinSystem()->NotifyAppFocusChange(g_application.m_AppFocused);
- }
- break;
-
- case SDL_KEYDOWN:
- {
- // process any platform specific shortcuts before handing off to XBMC
- if (ProcessOSXShortcuts(event))
- {
- ret = true;
- break;
- }
-
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_KEYDOWN;
- newEvent.key.keysym.scancode = event.key.keysym.scancode;
- newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym;
- newEvent.key.keysym.unicode = event.key.keysym.unicode;
-
- // Check if the Windows keys are down because SDL doesn't flag this.
- uint16_t mod = event.key.keysym.mod;
- uint8_t* keystate = SDL_GetKeyState(NULL);
- if (keystate[SDLK_LSUPER] || keystate[SDLK_RSUPER])
- mod |= XBMCKMOD_LSUPER;
- newEvent.key.keysym.mod = (XBMCMod) mod;
-
- // don't handle any more messages in the queue until we've handled keydown,
- // if a keyup is in the queue it will reset the keypress before it is handled.
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- break;
- }
-
- case SDL_KEYUP:
- {
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_KEYUP;
- newEvent.key.keysym.scancode = event.key.keysym.scancode;
- newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym;
- newEvent.key.keysym.mod =(XBMCMod) event.key.keysym.mod;
- newEvent.key.keysym.unicode = event.key.keysym.unicode;
-
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- break;
- }
-
- case SDL_MOUSEBUTTONDOWN:
- {
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_MOUSEBUTTONDOWN;
- newEvent.button.button = event.button.button;
- newEvent.button.x = event.button.x;
- newEvent.button.y = event.button.y;
-
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- break;
- }
-
- case SDL_MOUSEBUTTONUP:
- {
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_MOUSEBUTTONUP;
- newEvent.button.button = event.button.button;
- newEvent.button.x = event.button.x;
- newEvent.button.y = event.button.y;
-
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- break;
- }
-
- case SDL_MOUSEMOTION:
- {
- if (0 == (SDL_GetAppState() & SDL_APPMOUSEFOCUS))
- {
- CServiceBroker::GetInputManager().SetMouseActive(false);
- // See CApplication::ProcessSlow() for a description as to why we call Cocoa_HideMouse.
- // this is here to restore the pointer when toggling back to window mode from fullscreen.
- Cocoa_ShowMouse();
- break;
- }
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_MOUSEMOTION;
- newEvent.motion.x = event.motion.x;
- newEvent.motion.y = event.motion.y;
-
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- break;
- }
- case SDL_VIDEORESIZE:
- {
- // Under newer osx versions sdl is so fucked up that it even fires resize events
- // that exceed the screen size (maybe some HiDP incompatibility in old SDL?)
- // ensure to ignore those events because it will mess with windowed size
- if((event.resize.w > CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth) ||
- (event.resize.h > CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight))
- {
- break;
- }
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_VIDEORESIZE;
- newEvent.resize.w = event.resize.w;
- newEvent.resize.h = event.resize.h;
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
- break;
- }
- case SDL_USEREVENT:
- {
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_USEREVENT;
- newEvent.user.code = event.user.code;
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- ret |= appPort->OnEvent(newEvent);
- break;
- }
- case SDL_VIDEOEXPOSE:
- CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
- break;
- }
- memset(&event, 0, sizeof(SDL_Event));
- }
-
- return ret;
-}
-
-bool CWinEventsOSX::ProcessOSXShortcuts(SDL_Event& event)
-{
- static bool cmd = false;
-
- cmd = !!(SDL_GetModState() & (KMOD_LMETA | KMOD_RMETA ));
-
- if (cmd && event.key.type == SDL_KEYDOWN)
- {
- char keysymbol = event.key.keysym.sym;
-
- // if the unicode is in the ascii range
- // use this instead for getting the real
- // character based on the used keyboard layout
- // see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2004-May/043716.html
- bool isControl = (event.key.keysym.mod & KMOD_CTRL) != 0;
- if (!isControl && !(event.key.keysym.unicode & 0xff80))
- keysymbol = event.key.keysym.unicode;
-
- switch(keysymbol)
- {
- case SDLK_q: // CMD-q to quit
- if (!g_application.m_bStop)
- CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
- return true;
-
- case SDLK_f: // CMD-Ctrl-f to toggle fullscreen
- if (!isControl)
- return false;
- g_application.OnAction(CAction(ACTION_TOGGLE_FULLSCREEN));
- return true;
-
- case SDLK_s: // CMD-3 to take a screenshot
- g_application.OnAction(CAction(ACTION_TAKE_SCREENSHOT));
- return true;
-
- case SDLK_h: // CMD-h to hide
- CServiceBroker::GetWinSystem()->Hide();
- return true;
-
- case SDLK_m: // CMD-m to minimize
- CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MINIMIZE);
- return true;
-
- default:
- return false;
- }
- }
-
- return false;
-}
diff --git a/xbmc/windowing/osx/SDL/WinEventsSDL.h b/xbmc/windowing/osx/SDL/WinEventsSDL.h
deleted file mode 100644
index 06e6325b50..0000000000
--- a/xbmc/windowing/osx/SDL/WinEventsSDL.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2005-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 "windowing/WinEvents.h"
-
-#include <SDL/SDL_events.h>
-
-class CWinEventsOSX : public IWinEvents
-{
-public:
- CWinEventsOSX() = default;
- ~CWinEventsOSX() override = default;
- bool MessagePump() override;
-
-private:
- static bool ProcessOSXShortcuts(SDL_Event& event);
-};
diff --git a/xbmc/windowing/osx/SDL/WinSystemOSXSDL.h b/xbmc/windowing/osx/SDL/WinSystemOSXSDL.h
deleted file mode 100644
index 2078d0fd91..0000000000
--- a/xbmc/windowing/osx/SDL/WinSystemOSXSDL.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2005-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 "threads/CriticalSection.h"
-#include "threads/SystemClock.h"
-#include "threads/Timer.h"
-#include "windowing/WinSystem.h"
-
-#include <chrono>
-#include <string>
-#include <vector>
-
-typedef struct SDL_Surface SDL_Surface;
-
-class IDispResource;
-class CWinEventsOSX;
-class CWinSystemOSXImpl;
-#ifdef __OBJC__
-@class NSOpenGLContext;
-#endif
-
-class CWinSystemOSX : public CWinSystemBase, public ITimerCallback
-{
-public:
-
- CWinSystemOSX();
- ~CWinSystemOSX() override;
-
- // ITimerCallback interface
- void OnTimeout() override;
-
- // CWinSystemBase
- bool InitWindowSystem() override;
- bool DestroyWindowSystem() override;
- bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override;
- bool DestroyWindow() override;
- bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
- bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
- void UpdateResolutions() override;
- void NotifyAppFocusChange(bool bGaining) override;
- void ShowOSMouse(bool show) override;
- bool Minimize() override;
- bool Restore() override;
- bool Hide() override;
- bool Show(bool raise = true) override;
- void OnMove(int x, int y) override;
-
- std::string GetClipboardText() override;
-
- void Register(IDispResource *resource) override;
- void Unregister(IDispResource *resource) override;
-
- std::unique_ptr<CVideoSync> GetVideoSync(CVideoReferenceClock* clock) override;
-
- std::vector<std::string> GetConnectedOutputs() override;
-
- void WindowChangedScreen();
-
- void AnnounceOnLostDevice();
- void AnnounceOnResetDevice();
- void HandleOnResetDevice();
- void StartLostDeviceTimer();
- void StopLostDeviceTimer();
-
- void* GetCGLContextObj();
-#ifdef __OBJC__
- NSOpenGLContext* GetNSOpenGLContext();
-#else
- void* GetNSOpenGLContext();
-#endif
-
- // winevents override
- bool MessagePump() override;
-
-protected:
- std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> GetOSScreenSaverImpl() override;
-
- void HandlePossibleRefreshrateChange();
- void GetScreenResolution(int* w, int* h, double* fps, int screenIdx);
- void EnableVSync(bool enable);
- bool SwitchToVideoMode(int width, int height, double refreshrate);
- void FillInVideoModes();
- bool FlushBuffer(void);
- bool IsObscured(void);
- void StartTextInput();
- void StopTextInput();
-
- std::unique_ptr<CWinSystemOSXImpl> m_impl;
- SDL_Surface* m_SDLSurface;
- CWinEventsOSX *m_osx_events;
- bool m_obscured;
- std::chrono::time_point<std::chrono::steady_clock> m_obscured_timecheck;
-
- bool m_movedToOtherScreen;
- int m_lastDisplayNr;
- double m_refreshRate;
-
- CCriticalSection m_resourceSection;
- std::vector<IDispResource*> m_resources;
- CTimer m_lostDeviceTimer;
- bool m_delayDispReset;
- XbmcThreads::EndTime<> m_dispResetTimer;
- int m_updateGLContext = 0;
-};
diff --git a/xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm b/xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm
deleted file mode 100644
index 4886d34265..0000000000
--- a/xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm
+++ /dev/null
@@ -1,1848 +0,0 @@
-/*
- * Copyright (C) 2005-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 "WinSystemOSXSDL.h"
-
-#include "CompileInfo.h"
-#include "ServiceBroker.h"
-#include "application/AppInboundProtocol.h"
-#include "cores/AudioEngine/AESinkFactory.h"
-#include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h"
-#include "cores/RetroPlayer/process/osx/RPProcessInfoOSX.h"
-#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.h"
-#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
-#include "cores/VideoPlayer/DVDCodecs/Video/VTB.h"
-#include "cores/VideoPlayer/Process/osx/ProcessInfoOSX.h"
-#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.h"
-#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h"
-#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
-#include "guilib/DispResource.h"
-#include "guilib/GUIWindowManager.h"
-#include "input/KeyboardStat.h"
-#include "messaging/ApplicationMessenger.h"
-#include "rendering/gl/ScreenshotSurfaceGL.h"
-#include "settings/DisplaySettings.h"
-#include "settings/Settings.h"
-#include "settings/SettingsComponent.h"
-#include "utils/StringUtils.h"
-#include "utils/SystemInfo.h"
-#include "utils/log.h"
-#include "windowing/osx/CocoaDPMSSupport.h"
-#include "windowing/osx/OSScreenSaverOSX.h"
-#include "windowing/osx/SDL/WinEventsSDL.h"
-#include "windowing/osx/VideoSyncOsx.h"
-
-#include "platform/darwin/DarwinUtils.h"
-#include "platform/darwin/DictionaryUtils.h"
-#include "platform/darwin/osx/CocoaInterface.h"
-#import "platform/darwin/osx/SDL/OSXTextInputResponder.h"
-#include "platform/darwin/osx/XBMCHelper.h"
-
-#include <cstdlib>
-#include <mutex>
-#include <signal.h>
-
-#import <Cocoa/Cocoa.h>
-#import <IOKit/graphics/IOGraphicsLib.h>
-#import <IOKit/pwr_mgt/IOPMLib.h>
-#import <QuartzCore/QuartzCore.h>
-#import <SDL/SDL.h>
-
-// turn off deprecated warning spew.
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-using namespace KODI;
-using namespace WINDOWING;
-using namespace std::chrono_literals;
-
-//------------------------------------------------------------------------------------------
-// special object-c class for handling the NSWindowDidMoveNotification callback.
-@interface windowDidMoveNoteClass : NSObject
-{
- void *m_userdata;
-}
-+ (windowDidMoveNoteClass*) initWith: (void*) userdata;
-- (void) windowDidMoveNotification:(NSNotification*) note;
-@end
-
-@implementation windowDidMoveNoteClass
-+ (windowDidMoveNoteClass*) initWith: (void*) userdata
-{
- windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new];
- windowDidMove->m_userdata = userdata;
- return windowDidMove;
-}
-- (void) windowDidMoveNotification:(NSNotification*) note
-{
- CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
- if (!winsys)
- return;
-
- NSOpenGLContext* context = [NSOpenGLContext currentContext];
- if (context)
- {
- if ([context view])
- {
- NSPoint window_origin = [[[context view] window] frame].origin;
- XBMC_Event newEvent = {};
- newEvent.type = XBMC_VIDEOMOVE;
- newEvent.move.x = window_origin.x;
- newEvent.move.y = window_origin.y;
- std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
- if (appPort)
- appPort->OnEvent(newEvent);
- }
- }
-}
-@end
-//------------------------------------------------------------------------------------------
-// special object-c class for handling the NSWindowDidReSizeNotification callback.
-@interface windowDidReSizeNoteClass : NSObject
-{
- void *m_userdata;
-}
-+ (windowDidReSizeNoteClass*) initWith: (void*) userdata;
-- (void) windowDidReSizeNotification:(NSNotification*) note;
-@end
-@implementation windowDidReSizeNoteClass
-+ (windowDidReSizeNoteClass*) initWith: (void*) userdata
-{
- windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new];
- windowDidReSize->m_userdata = userdata;
- return windowDidReSize;
-}
-- (void) windowDidReSizeNotification:(NSNotification*) note
-{
- CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
- if (!winsys)
- return;
-
-}
-@end
-
-//------------------------------------------------------------------------------------------
-// special object-c class for handling the NSWindowDidChangeScreenNotification callback.
-@interface windowDidChangeScreenNoteClass : NSObject
-{
- void *m_userdata;
-}
-+ (windowDidChangeScreenNoteClass*) initWith: (void*) userdata;
-- (void) windowDidChangeScreenNotification:(NSNotification*) note;
-@end
-@implementation windowDidChangeScreenNoteClass
-+ (windowDidChangeScreenNoteClass*) initWith: (void*) userdata
-{
- windowDidChangeScreenNoteClass *windowDidChangeScreen = [windowDidChangeScreenNoteClass new];
- windowDidChangeScreen->m_userdata = userdata;
- return windowDidChangeScreen;
-}
-- (void) windowDidChangeScreenNotification:(NSNotification*) note
-{
- CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
- if (!winsys)
- return;
- winsys->WindowChangedScreen();
-}
-@end
-//------------------------------------------------------------------------------------------
-
-class CWinSystemOSXImpl
-{
-public:
- NSOpenGLContext* m_glContext;
- static NSOpenGLContext* m_lastOwnedContext;
-
- windowDidMoveNoteClass* m_windowDidMove;
- windowDidReSizeNoteClass* m_windowDidReSize;
- windowDidChangeScreenNoteClass* m_windowChangedScreen;
-};
-
-NSOpenGLContext* CWinSystemOSXImpl::m_lastOwnedContext = nil;
-
-
-#define MAX_DISPLAYS 32
-// if there was a devicelost callback
-// but no device reset for 3 secs
-// a timeout fires the reset callback
-// (for ensuring that e.x. AE isn't stuck)
-constexpr auto LOST_DEVICE_TIMEOUT_MS = 3000ms;
-static NSWindow* blankingWindows[MAX_DISPLAYS];
-
-//------------------------------------------------------------------------------------------
-CRect CGRectToCRect(CGRect cgrect)
-{
- CRect crect = CRect(
- cgrect.origin.x,
- cgrect.origin.y,
- cgrect.origin.x + cgrect.size.width,
- cgrect.origin.y + cgrect.size.height);
- return crect;
-}
-//---------------------------------------------------------------------------------
-void SetMenuBarVisible(bool visible)
-{
- if(visible)
- {
- [[NSApplication sharedApplication]
- setPresentationOptions: NSApplicationPresentationDefault];
- }
- else
- {
- [[NSApplication sharedApplication]
- setPresentationOptions: NSApplicationPresentationHideMenuBar |
- NSApplicationPresentationHideDock];
- }
-}
-//---------------------------------------------------------------------------------
-CGDirectDisplayID GetDisplayID(int screen_index)
-{
- CGDirectDisplayID displayArray[MAX_DISPLAYS];
- CGDisplayCount numDisplays;
-
- // Get the list of displays.
- CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
- if (screen_index >= 0 && screen_index < static_cast<int>(numDisplays))
- return(displayArray[screen_index]);
- else
- return(displayArray[0]);
-}
-
-size_t DisplayBitsPerPixelForMode(CGDisplayModeRef mode)
-{
- size_t bitsPerPixel = 0;
-
- CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode);
- if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
- {
- bitsPerPixel = 32;
- }
- else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
- {
- bitsPerPixel = 16;
- }
- else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
- {
- bitsPerPixel = 8;
- }
-
- CFRelease(pixEnc);
-
- return bitsPerPixel;
-}
-
-CFArrayRef GetAllDisplayModes(CGDirectDisplayID display)
-{
- int value = 1;
-
- CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
- if (!number)
- {
- CLog::Log(LOGERROR, "GetAllDisplayModes - could not create Number!");
- return NULL;
- }
-
- CFStringRef key = kCGDisplayShowDuplicateLowResolutionModes;
- CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&key, (const void **)&number, 1, NULL, NULL);
- CFRelease(number);
-
- if (!options)
- {
- CLog::Log(LOGERROR, "GetAllDisplayModes - could not create Dictionary!");
- return NULL;
- }
-
- CFArrayRef displayModes = CGDisplayCopyAllDisplayModes(display, options);
- CFRelease(options);
-
- if (!displayModes)
- {
- CLog::Log(LOGERROR, "GetAllDisplayModes - no displaymodes found!");
- return NULL;
- }
-
- return displayModes;
-}
-
-// mimic former behavior of deprecated CGDisplayBestModeForParameters
-CGDisplayModeRef BestMatchForMode(CGDirectDisplayID display, size_t bitsPerPixel, size_t width, size_t height, boolean_t &match)
-{
-
- // Get a copy of the current display mode
- CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(display);
-
- // Loop through all display modes to determine the closest match.
- // CGDisplayBestModeForParameters is deprecated on 10.6 so we will emulate it's behavior
- // Try to find a mode with the requested depth and equal or greater dimensions first.
- // If no match is found, try to find a mode with greater depth and same or greater dimensions.
- // If still no match is found, just use the current mode.
- CFArrayRef allModes = GetAllDisplayModes(display);
-
- for(int i = 0; i < CFArrayGetCount(allModes); i++) {
- CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
-
- if(DisplayBitsPerPixelForMode(mode) != bitsPerPixel)
- continue;
-
- if((CGDisplayModeGetWidth(mode) == width) && (CGDisplayModeGetHeight(mode) == height))
- {
- CGDisplayModeRelease(displayMode); // release the copy we got before ...
- displayMode = mode;
- match = true;
- break;
- }
- }
-
- // No depth match was found
- if(!match)
- {
- for(int i = 0; i < CFArrayGetCount(allModes); i++)
- {
- CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
- if(DisplayBitsPerPixelForMode(mode) >= bitsPerPixel)
- continue;
-
- if((CGDisplayModeGetWidth(mode) == width) && (CGDisplayModeGetHeight(mode) == height))
- {
- displayMode = mode;
- match = true;
- break;
- }
- }
- }
-
- CFRelease(allModes);
-
- return displayMode;
-}
-
-CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen)
-{
- NSDictionary* screenInfo = [screen deviceDescription];
- NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
-
- return (CGDirectDisplayID)[screenID longValue];
-}
-
-int GetDisplayIndex(CGDirectDisplayID display)
-{
- CGDirectDisplayID displayArray[MAX_DISPLAYS];
- CGDisplayCount numDisplays;
-
- // Get the list of displays.
- CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
- while (numDisplays > 0)
- {
- if (display == displayArray[--numDisplays])
- return numDisplays;
- }
- return -1;
-}
-
-void BlankOtherDisplays(int screen_index)
-{
- int i;
- int numDisplays = [[NSScreen screens] count];
-
- // zero out blankingWindows for debugging
- for (i=0; i<MAX_DISPLAYS; i++)
- {
- blankingWindows[i] = 0;
- }
-
- // Blank.
- for (i=0; i<numDisplays; i++)
- {
- if (i != screen_index)
- {
- // Get the size.
- NSScreen* pScreen = [[NSScreen screens] objectAtIndex:i];
- NSRect screenRect = [pScreen frame];
-
- // Build a blanking window.
- screenRect.origin = NSZeroPoint;
- blankingWindows[i] = [[NSWindow alloc] initWithContentRect:screenRect
- styleMask:NSBorderlessWindowMask
- backing:NSBackingStoreBuffered
- defer:NO
- screen:pScreen];
-
- [blankingWindows[i] setBackgroundColor:[NSColor blackColor]];
- [blankingWindows[i] setLevel:CGShieldingWindowLevel()];
- [blankingWindows[i] makeKeyAndOrderFront:nil];
- }
- }
-}
-
-void UnblankDisplays(void)
-{
- int numDisplays = [[NSScreen screens] count];
- int i = 0;
-
- for (i=0; i<numDisplays; i++)
- {
- if (blankingWindows[i] != 0)
- {
- // Get rid of the blanking windows we created.
- [blankingWindows[i] close];
- blankingWindows[i] = 0;
- }
- }
-}
-
-CGDisplayFadeReservationToken DisplayFadeToBlack(bool fade)
-{
- // Fade to black to hide resolution-switching flicker and garbage.
- CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
- if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess && fade)
- CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
-
- return(fade_token);
-}
-
-void DisplayFadeFromBlack(CGDisplayFadeReservationToken fade_token, bool fade)
-{
- if (fade_token != kCGDisplayFadeReservationInvalidToken)
- {
- if (fade)
- CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
- CGReleaseDisplayFadeReservation(fade_token);
- }
-}
-
-NSString* screenNameForDisplay(CGDirectDisplayID displayID)
-{
- NSString* screenName;
- @autoreleasepool
- {
- NSDictionary* deviceInfo = (__bridge_transfer NSDictionary*)IODisplayCreateInfoDictionary(
- CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
- NSDictionary* localizedNames =
- [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
-
- if ([localizedNames count] > 0)
- {
- screenName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]];
- }
- }
-
- if (screenName == nil)
- {
- screenName = [[NSString alloc] initWithFormat:@"%i", displayID];
- }
- else
- {
- // ensure screen name is unique by appending displayid
- screenName = [screenName stringByAppendingFormat:@" (%@)", [@(displayID) stringValue]];
- }
-
- return screenName;
-}
-
-int GetDisplayIndex(const std::string& dispName)
-{
- int ret = 0;
-
- // Add full screen settings for additional monitors
- int numDisplays = [[NSScreen screens] count];
-
- for (int disp = 0; disp < numDisplays; disp++)
- {
- NSString *name = screenNameForDisplay(GetDisplayID(disp));
- if ([name UTF8String] == dispName)
- {
- ret = disp;
- break;
- }
- }
-
- return ret;
-}
-
-void ShowHideNSWindow(NSWindow *wind, bool show)
-{
- if (show)
- [wind orderFront:nil];
- else
- [wind orderOut:nil];
-}
-
-static NSWindow *curtainWindow;
-void fadeInDisplay(NSScreen *theScreen, double fadeTime)
-{
- int fadeSteps = 100;
- double fadeInterval = (fadeTime / (double) fadeSteps);
-
- if (curtainWindow != nil)
- {
- for (int step = 0; step < fadeSteps; step++)
- {
- double fade = 1.0 - (step * fadeInterval);
- [curtainWindow setAlphaValue:fade];
-
- NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
- [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
- }
- }
- [curtainWindow close];
- curtainWindow = nil;
-
- [NSCursor unhide];
-}
-
-void fadeOutDisplay(NSScreen *theScreen, double fadeTime)
-{
- int fadeSteps = 100;
- double fadeInterval = (fadeTime / (double) fadeSteps);
-
- [NSCursor hide];
-
- curtainWindow = [[NSWindow alloc]
- initWithContentRect:[theScreen frame]
- styleMask:NSBorderlessWindowMask
- backing:NSBackingStoreBuffered
- defer:YES
- screen:theScreen];
-
- [curtainWindow setAlphaValue:0.0];
- [curtainWindow setBackgroundColor:[NSColor blackColor]];
- [curtainWindow setLevel:NSScreenSaverWindowLevel];
-
- [curtainWindow makeKeyAndOrderFront:nil];
- [curtainWindow setFrame:[curtainWindow
- frameRectForContentRect:[theScreen frame]]
- display:YES
- animate:NO];
-
- for (int step = 0; step < fadeSteps; step++)
- {
- double fade = step * fadeInterval;
- [curtainWindow setAlphaValue:fade];
-
- NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
- [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
- }
-}
-
-// try to find mode that matches the desired size, refreshrate
-// non interlaced, nonstretched, safe for hardware
-CGDisplayModeRef GetMode(int width, int height, double refreshrate, int screenIdx)
-{
- if ( screenIdx >= (signed)[[NSScreen screens] count])
- return NULL;
-
- Boolean stretched;
- Boolean interlaced;
- Boolean safeForHardware;
- int w, h, bitsperpixel;
- double rate;
- RESOLUTION_INFO res;
-
- CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with {} x {} @ {:f} Hz on display {}",
- width, height, refreshrate, screenIdx);
-
- CFArrayRef displayModes = GetAllDisplayModes(GetDisplayID(screenIdx));
-
- if (!displayModes)
- return NULL;
-
- for (int i=0; i < CFArrayGetCount(displayModes); ++i)
- {
- CGDisplayModeRef displayMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i);
- uint32_t flags = CGDisplayModeGetIOFlags(displayMode);
- stretched = flags & kDisplayModeStretchedFlag ? true : false;
- interlaced = flags & kDisplayModeInterlacedFlag ? true : false;
- bitsperpixel = DisplayBitsPerPixelForMode(displayMode);
- safeForHardware = flags & kDisplayModeSafetyFlags ? true : false;
- w = CGDisplayModeGetWidth(displayMode);
- h = CGDisplayModeGetHeight(displayMode);
- rate = CGDisplayModeGetRefreshRate(displayMode);
-
-
- if ((bitsperpixel == 32) &&
- (safeForHardware == YES) &&
- (stretched == NO) &&
- (interlaced == NO) &&
- (w == width) &&
- (h == height) &&
- (rate == refreshrate || rate == 0))
- {
- CLog::Log(LOGDEBUG, "GetMode found a match!");
- return displayMode;
- }
- }
-
- CFRelease(displayModes);
- CLog::Log(LOGERROR, "GetMode - no match found!");
- return NULL;
-}
-
-//---------------------------------------------------------------------------------
-static void DisplayReconfigured(CGDirectDisplayID display,
- CGDisplayChangeSummaryFlags flags, void* userData)
-{
- CWinSystemOSX *winsys = (CWinSystemOSX*)userData;
- if (!winsys)
- return;
-
- CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured with flags {}", flags);
-
- // we fire the callbacks on start of configuration
- // or when the mode set was finished
- // or when we are called with flags == 0 (which is undocumented but seems to happen
- // on some macs - we treat it as device reset)
-
- // first check if we need to call OnLostDevice
- if (flags & kCGDisplayBeginConfigurationFlag)
- {
- // pre/post-reconfiguration changes
- RESOLUTION res = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
- if (res == RES_INVALID)
- return;
-
- NSScreen* pScreen = nil;
- unsigned int screenIdx = 0;
-
- if ( screenIdx < [[NSScreen screens] count] )
- {
- pScreen = [[NSScreen screens] objectAtIndex:screenIdx];
- }
-
- // kCGDisplayBeginConfigurationFlag is only fired while the screen is still
- // valid
- if (pScreen)
- {
- CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen);
- if (xbmc_display == display)
- {
- // we only respond to changes on the display we are running on.
- winsys->AnnounceOnLostDevice();
- winsys->StartLostDeviceTimer();
- }
- }
- }
- else // the else case checks if we need to call OnResetDevice
- {
- // we fire if kCGDisplaySetModeFlag is set or if flags == 0
- // (which is undocumented but seems to happen
- // on some macs - we treat it as device reset)
- // we also don't check the screen here as we might not even have
- // one anymore (e.x. when tv is turned off)
- if (flags & kCGDisplaySetModeFlag || flags == 0)
- {
- winsys->StopLostDeviceTimer(); // no need to timeout - we've got the callback
- winsys->HandleOnResetDevice();
- }
- }
-
- if ((flags & kCGDisplayAddFlag) || (flags & kCGDisplayRemoveFlag))
- winsys->UpdateResolutions();
-}
-
-//------------------------------------------------------------------------------
-NSOpenGLContext* CreateWindowedContext(NSOpenGLContext* shareCtx);
-void ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, NSView* last_view);
-
-//------------------------------------------------------------------------------
-CWinSystemOSX::CWinSystemOSX()
- : CWinSystemBase()
- , m_impl{new CWinSystemOSXImpl}
- , m_lostDeviceTimer(this)
-{
- m_SDLSurface = NULL;
- m_osx_events = NULL;
- m_obscured = false;
- m_obscured_timecheck = std::chrono::steady_clock::now() + std::chrono::milliseconds(1000);
- m_lastDisplayNr = -1;
- m_movedToOtherScreen = false;
- m_refreshRate = 0.0;
- m_delayDispReset = false;
-
- m_winEvents.reset(new CWinEventsOSX());
-
- AE::CAESinkFactory::ClearSinks();
- CAESinkDARWINOSX::Register();
- m_dpms = std::make_shared<CCocoaDPMSSupport>();
-}
-
-CWinSystemOSX::~CWinSystemOSX() = default;
-
-void CWinSystemOSX::StartLostDeviceTimer()
-{
- if (m_lostDeviceTimer.IsRunning())
- m_lostDeviceTimer.Restart();
- else
- m_lostDeviceTimer.Start(LOST_DEVICE_TIMEOUT_MS, false);
-}
-
-void CWinSystemOSX::StopLostDeviceTimer()
-{
- m_lostDeviceTimer.Stop();
-}
-
-void CWinSystemOSX::OnTimeout()
-{
- HandleOnResetDevice();
-}
-
-bool CWinSystemOSX::InitWindowSystem()
-{
- CLog::LogF(LOGINFO, "Setup SDL");
-
- /* Clean up on exit, exit on window close and interrupt */
- std::atexit(SDL_Quit);
-
- if (SDL_Init(SDL_INIT_VIDEO) != 0)
- {
- CLog::LogF(LOGFATAL, "Unable to initialize SDL: {}", SDL_GetError());
- return false;
- }
- // SDL_Init will install a handler for segfaults, restore the default handler.
- signal(SIGSEGV, SIG_DFL);
-
- SDL_EnableUNICODE(1);
-
- // set repeat to 10ms to ensure repeat time < frame time
- // so that hold times can be reliably detected
- SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10);
-
- if (!CWinSystemBase::InitWindowSystem())
- return false;
-
- m_osx_events = new CWinEventsOSX();
-
- CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this);
-
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- auto windowDidMove = [windowDidMoveNoteClass initWith:this];
- [center addObserver:windowDidMove
- selector:@selector(windowDidMoveNotification:)
- name:NSWindowDidMoveNotification object:nil];
- m_impl->m_windowDidMove = windowDidMove;
-
- auto windowDidReSize = [windowDidReSizeNoteClass initWith:this];
- [center addObserver:windowDidReSize
- selector:@selector(windowDidReSizeNotification:)
- name:NSWindowDidResizeNotification object:nil];
- m_impl->m_windowDidReSize = windowDidReSize;
-
- auto windowDidChangeScreen = [windowDidChangeScreenNoteClass initWith:this];
- [center addObserver:windowDidChangeScreen
- selector:@selector(windowDidChangeScreenNotification:)
- name:NSWindowDidChangeScreenNotification object:nil];
- m_impl->m_windowChangedScreen = windowDidChangeScreen;
-
- return true;
-}
-
-bool CWinSystemOSX::DestroyWindowSystem()
-{
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center removeObserver:m_impl->m_windowDidMove name:NSWindowDidMoveNotification object:nil];
- [center removeObserver:m_impl->m_windowDidReSize name:NSWindowDidResizeNotification object:nil];
- [center removeObserver:m_impl->m_windowChangedScreen
- name:NSWindowDidChangeScreenNotification
- object:nil];
-
- CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this);
-
- delete m_osx_events;
- m_osx_events = NULL;
-
- UnblankDisplays();
- m_impl->m_glContext = nil;
- return true;
-}
-
-bool CWinSystemOSX::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
-{
- // force initial window creation to be windowed, if fullscreen, it will switch to it below
- // fixes the white screen of death if starting fullscreen and switching to windowed.
- RESOLUTION_INFO resInfo = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW);
- m_nWidth = resInfo.iWidth;
- m_nHeight = resInfo.iHeight;
- m_bFullScreen = false;
-
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-
- // Enable vertical sync to avoid any tearing.
- SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
-
- m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE);
- if (!m_SDLSurface)
- return false;
-
- // the context SDL creates isn't full screen compatible, so we create new one
- // first, find the current contect and make sure a view is attached
- NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
- NSView* view = [cur_context view];
- if (!view)
- return false;
-
- if (CDisplaySettings::GetInstance().GetCurrentResolution() != RES_WINDOW)
- {
- // If we are not starting up windowed, then hide the initial SDL window
- // so we do not see it flash before the fade-out and switch to fullscreen.
- ShowHideNSWindow([view window], false);
- }
-
- // disassociate view from context
- [cur_context clearDrawable];
-
- // release the context
- if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context)
- {
- [ NSOpenGLContext clearCurrentContext ];
- [ cur_context clearDrawable ];
- cur_context = nil;
- }
-
- // create a new context
- auto new_context = CreateWindowedContext(nil);
- if (!new_context)
- return false;
-
- // associate with current view
- [new_context setView:view];
- [new_context makeCurrentContext];
-
- // set the window title
- [[[new_context view] window]
- setTitle:[NSString stringWithFormat:@"%s Media Center", CCompileInfo::GetAppName()]];
-
- m_impl->m_glContext = new_context;
- CWinSystemOSXImpl::m_lastOwnedContext = new_context;
- m_bWindowCreated = true;
-
- // get screen refreshrate - this is needed
- // when we startup in windowed mode and don't run through SetFullScreen
- int dummy;
- GetScreenResolution(&dummy, &dummy, &m_refreshRate, m_lastDisplayNr);
-
- // register platform dependent objects
- CDVDFactoryCodec::ClearHWAccels();
- VTB::CDecoder::Register();
- VIDEOPLAYER::CRendererFactory::ClearRenderer();
- CLinuxRendererGL::Register();
- CRendererVTB::Register();
- VIDEOPLAYER::CProcessInfoOSX::Register();
- RETRO::CRPProcessInfoOSX::Register();
- RETRO::CRPProcessInfoOSX::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGL);
- CScreenshotSurfaceGL::Register();
-
- return true;
-}
-
-bool CWinSystemOSX::DestroyWindow()
-{
- return true;
-}
-
-void ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, NSView* last_view)
-{
- if (last_view && [last_view window])
- {
- auto size = NSMakeSize(newWidth, newHeight);
- NSWindow* lastWindow = [last_view window];
- [lastWindow setContentSize:size];
- [lastWindow update];
- [last_view setFrameSize:size];
- }
-}
-bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
-{
- if (!m_impl->m_glContext)
- return false;
-
- NSOpenGLContext* context = [NSOpenGLContext currentContext];
- NSView* view;
- NSWindow* window;
-
- view = [context view];
-
- if (view)
- {
- // It seems, that in macOS 10.15 this defaults to YES, but we currently do not support
- // Retina resolutions properly. Ensure that the view uses a 1 pixel per point framebuffer.
- view.wantsBestResolutionOpenGLSurface = NO;
- }
-
- if (view && (newWidth > 0) && (newHeight > 0))
- {
- window = [view window];
- if (window)
- {
- [window setContentSize:NSMakeSize(newWidth, newHeight)];
- [window update];
- [view setFrameSize:NSMakeSize(newWidth, newHeight)];
- [context update];
- // this is needed in case we traverse from fullscreen screen 2
- // to windowed on screen 1 directly where in ScreenChangedNotification
- // we don't have a window to get the current screen on
- // in that case ResizeWindow is called at a later stage from SetFullScreen(false)
- // and we can grab the correct display number here then
- m_lastDisplayNr = GetDisplayIndex(GetDisplayIDFromScreen( [window screen] ));
- }
- }
-
- // HACK: resize SDL's view manually so that mouse bounds are correctly updated.
- // there are two parts to this, the internal SDL (current_video->screen) and
- // the cocoa view ( handled in SetFullScreen).
- SDL_SetWidthHeight(newWidth, newHeight);
-
- [context makeCurrentContext];
-
- m_nWidth = newWidth;
- m_nHeight = newHeight;
- m_impl->m_glContext = context;
- CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(m_refreshRate);
-
- return true;
-}
-
-static bool needtoshowme = true;
-
-bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
-{
- static NSWindow* windowedFullScreenwindow = nil;
- static NSPoint last_window_origin;
- static NSView* last_view = nil;
- static NSSize last_view_size;
- static NSPoint last_view_origin;
- static NSInteger last_window_level = NSNormalWindowLevel;
- bool was_fullscreen = m_bFullScreen;
- NSOpenGLContext* cur_context;
-
- // Fade to black to hide resolution-switching flicker and garbage.
- CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme);
-
- // If we're already fullscreen then we must be moving to a different display.
- // or if we are still on the same display - it might be only a refreshrate/resolution
- // change request.
- // Recurse to reset fullscreen mode and then continue.
- if (was_fullscreen && fullScreen)
- {
- needtoshowme = false;
- ShowHideNSWindow([last_view window], needtoshowme);
- RESOLUTION_INFO& window = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW);
- CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays);
- needtoshowme = true;
- }
-
- const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
- m_lastDisplayNr = GetDisplayIndex(settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR));
- m_nWidth = res.iWidth;
- m_nHeight = res.iHeight;
- m_bFullScreen = fullScreen;
-
- cur_context = [NSOpenGLContext currentContext];
-
- //handle resolution/refreshrate switching early here
- if (m_bFullScreen)
- {
- // switch videomode
- SwitchToVideoMode(res.iWidth, res.iHeight, static_cast<double>(res.fRefreshRate));
- }
-
- //no context? done.
- if (!cur_context)
- {
- DisplayFadeFromBlack(fade_token, needtoshowme);
- return false;
- }
-
- if (windowedFullScreenwindow != nil)
- {
- [windowedFullScreenwindow close];
- windowedFullScreenwindow = nil;
- }
-
- if (m_bFullScreen)
- {
- // FullScreen Mode
- NSOpenGLContext* newContext = nil;
-
- // Save info about the windowed context so we can restore it when returning to windowed.
- last_view = [cur_context view];
- last_view_size = [last_view frame].size;
- last_view_origin = [last_view frame].origin;
- last_window_origin = [[last_view window] frame].origin;
- last_window_level = [[last_view window] level];
-
- // This is Cocoa Windowed FullScreen Mode
- // Get the screen rect of our current display
- NSScreen* pScreen = [[NSScreen screens] objectAtIndex:m_lastDisplayNr];
- NSRect screenRect = [pScreen frame];
-
- // remove frame origin offset of original display
- screenRect.origin = NSZeroPoint;
-
- // make a new window to act as the windowedFullScreen
- windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect
- styleMask:NSBorderlessWindowMask
- backing:NSBackingStoreBuffered
- defer:NO
- screen:pScreen];
- windowedFullScreenwindow.releasedWhenClosed = NO;
-
- [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]];
- [windowedFullScreenwindow makeKeyAndOrderFront:nil];
-
- // make our window the same level as the rest to enable cmd+tab switching
- [windowedFullScreenwindow setLevel:NSNormalWindowLevel];
- // this will make our window topmost and hide all system messages
- //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()];
-
- // ...and the original one beneath it and on the same screen.
- [[last_view window] setLevel:NSNormalWindowLevel-1];
- [[last_view window] setFrameOrigin:[pScreen frame].origin];
- // expand the mouse bounds in SDL view to fullscreen
- [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
- [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
-
- NSView* blankView = [[NSView alloc] init];
- [windowedFullScreenwindow setContentView:blankView];
- [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
- [windowedFullScreenwindow update];
- [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
-
- // force SDL's window origin to be at zero:
- // on Monterey, X coordinate becomes non-zero somewhere after this method returns
- const auto twoSeconds = dispatch_time(DISPATCH_TIME_NOW, 2LL * NSEC_PER_SEC);
- dispatch_after(twoSeconds, dispatch_get_main_queue(), ^{
- for (NSWindow* w in NSApp.windows)
- {
- if (w == windowedFullScreenwindow)
- continue;
- [w setFrameOrigin:w.screen.frame.origin];
- break;
- }
- });
-
- // Obtain windowed pixel format and create a new context.
- newContext = CreateWindowedContext(cur_context);
- [newContext setView:blankView];
-
- // Hide the menu bar.
- SetMenuBarVisible(false);
-
- // Blank other displays if requested.
- if (blankOtherDisplays)
- BlankOtherDisplays(m_lastDisplayNr);
-
- // Hide the mouse.
- [NSCursor hide];
-
- // Release old context if we created it.
- if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context)
- {
- [NSOpenGLContext clearCurrentContext];
- [cur_context clearDrawable];
- }
-
- // activate context
- [newContext makeCurrentContext];
- CWinSystemOSXImpl::m_lastOwnedContext = newContext;
- }
- else
- {
- // Windowed Mode
- // exit fullscreen
- [cur_context clearDrawable];
-
- [NSCursor unhide];
-
- // Show menubar.
- SetMenuBarVisible(true);
-
- // restore the windowed window level
- [[last_view window] setLevel:last_window_level];
-
- // Unblank.
- // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false.
- //if (blankOtherDisplays)
- UnblankDisplays();
-
- // create our new context (sharing with the current one)
- auto newContext = CreateWindowedContext(cur_context);
- if (!newContext)
- return false;
-
- // Assign view from old context, move back to original screen.
- [newContext setView:last_view];
- [[last_view window] setFrameOrigin:last_window_origin];
- // return the mouse bounds in SDL view to previous size
- [last_view setFrameSize:last_view_size];
- [last_view setFrameOrigin:last_view_origin];
-
- // Release the fullscreen context.
- if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context)
- {
- [NSOpenGLContext clearCurrentContext];
- [cur_context clearDrawable];
- }
-
- // Activate context.
- [newContext makeCurrentContext];
- CWinSystemOSXImpl::m_lastOwnedContext = newContext;
- }
-
- DisplayFadeFromBlack(fade_token, needtoshowme);
-
- ShowHideNSWindow([last_view window], needtoshowme);
- // need to make sure SDL tracks any window size changes
- ResizeWindow(m_nWidth, m_nHeight, -1, -1);
- ResizeWindowInternal(m_nWidth, m_nHeight, -1, -1, last_view);
- // restore origin once again when going to windowed mode
- if (!fullScreen)
- {
- [[last_view window] setFrameOrigin:last_window_origin];
- }
- HandlePossibleRefreshrateChange();
-
- m_updateGLContext = 0;
- return true;
-}
-
-void CWinSystemOSX::UpdateResolutions()
-{
- CWinSystemBase::UpdateResolutions();
-
- // Add desktop resolution
- int w, h;
- double fps;
-
- int dispIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR));
- GetScreenResolution(&w, &h, &fps, dispIdx);
- NSString* dispName = screenNameForDisplay(GetDisplayID(dispIdx));
- UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), [dispName UTF8String], w, h, fps, 0);
-
- CDisplaySettings::GetInstance().ClearCustomResolutions();
-
- // now just fill in the possible resolutions for the attached screens
- // and push to the resolution info vector
- FillInVideoModes();
- CDisplaySettings::GetInstance().ApplyCalibrations();
-}
-
-/*
-void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx)
-{
- if (!pixFmt)
- return nil;
-
- NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
- shareContext:(NSOpenGLContext*)shareCtx];
-
- // snipit from SDL_cocoaopengl.m
- //
- // Wisdom from Apple engineer in reference to UT2003's OpenGL performance:
- // "You are blowing a couple of the internal OpenGL function caches. This
- // appears to be happening in the VAO case. You can tell OpenGL to up
- // the cache size by issuing the following calls right after you create
- // the OpenGL context. The default cache size is 16." --ryan.
- //
-
- #ifndef GLI_ARRAY_FUNC_CACHE_MAX
- #define GLI_ARRAY_FUNC_CACHE_MAX 284
- #endif
-
- #ifndef GLI_SUBMIT_FUNC_CACHE_MAX
- #define GLI_SUBMIT_FUNC_CACHE_MAX 280
- #endif
-
- {
- long cache_max = 64;
- CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj];
- CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max);
- CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max);
- }
-
- // End Wisdom from Apple Engineer section. --ryan.
- return newContext;
-}
-*/
-
-NSOpenGLContext* CreateWindowedContext(NSOpenGLContext* shareCtx)
-{
- NSOpenGLPixelFormat* pixFmt;
- if (getenv("KODI_GL_PROFILE_LEGACY"))
- {
- NSOpenGLPixelFormatAttribute wattrs[] = {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFANoRecovery,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFADepthSize,
- static_cast<NSOpenGLPixelFormatAttribute>(8),
- static_cast<NSOpenGLPixelFormatAttribute>(0)};
- pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs];
- }
- else
- {
- NSOpenGLPixelFormatAttribute wattrs_gl3[] = {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFAOpenGLProfile,
- NSOpenGLProfileVersion3_2Core,
- NSOpenGLPFANoRecovery,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFADepthSize,
- static_cast<NSOpenGLPixelFormatAttribute>(24),
- static_cast<NSOpenGLPixelFormatAttribute>(0)};
- pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs_gl3];
- }
-
- auto newContext = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:shareCtx];
-
- if (!newContext)
- {
- // bah, try again for non-accelerated renderer
- NSOpenGLPixelFormatAttribute wattrs2[] =
- {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFANoRecovery,
- NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
- (NSOpenGLPixelFormatAttribute)0
- };
- newContext = [[NSOpenGLContext alloc]
- initWithFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2]
- shareContext:shareCtx];
- }
-
- return newContext;
-}
-
-NSOpenGLContext* CreateFullScreenContext(int screen_index, NSOpenGLContext* shareCtx)
-{
- CGDirectDisplayID displayArray[MAX_DISPLAYS];
- CGDisplayCount numDisplays;
- CGDirectDisplayID displayID;
-
- // Get the list of displays.
- CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
- displayID = displayArray[screen_index];
-
- NSOpenGLPixelFormat* pixFmt;
- if (getenv("KODI_GL_PROFILE_LEGACY"))
- {
- NSOpenGLPixelFormatAttribute fsattrs[] = {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFANoRecovery,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFADepthSize,
- static_cast<NSOpenGLPixelFormatAttribute>(8),
- NSOpenGLPFAScreenMask,
- static_cast<NSOpenGLPixelFormatAttribute>(CGDisplayIDToOpenGLDisplayMask(displayID)),
- static_cast<NSOpenGLPixelFormatAttribute>(0)};
- pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs];
- }
- else
- {
- NSOpenGLPixelFormatAttribute fsattrs_gl3[] = {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFANoRecovery,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFADepthSize,
- static_cast<NSOpenGLPixelFormatAttribute>(24),
- NSOpenGLPFAOpenGLProfile,
- NSOpenGLProfileVersion3_2Core,
- NSOpenGLPFAScreenMask,
- static_cast<NSOpenGLPixelFormatAttribute>(CGDisplayIDToOpenGLDisplayMask(displayID)),
- static_cast<NSOpenGLPixelFormatAttribute>(0)};
- pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs_gl3];
- }
-
- if (!pixFmt)
- return nil;
-
- auto newContext = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:shareCtx];
-
- return newContext;
-}
-
-void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
-{
- CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx);
- CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id);
- *w = CGDisplayModeGetWidth(mode);
- *h = CGDisplayModeGetHeight(mode);
- *fps = CGDisplayModeGetRefreshRate(mode);
- CGDisplayModeRelease(mode);
- if ((int)*fps == 0)
- {
- // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
- *fps = 60.0;
- }
-}
-
-void CWinSystemOSX::EnableVSync(bool enable)
-{
- // OpenGL Flush synchronised with vertical retrace
- GLint swapInterval = enable ? 1 : 0;
- [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
-}
-
-bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate)
-{
- boolean_t match = false;
- CGDisplayModeRef dispMode = NULL;
-
- int screenIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR));
-
- // Figure out the screen size. (default to main screen)
- CGDirectDisplayID display_id = GetDisplayID(screenIdx);
-
- // find mode that matches the desired size, refreshrate
- // non interlaced, nonstretched, safe for hardware
- dispMode = GetMode(width, height, refreshrate, screenIdx);
-
- //not found - fallback to bestemdeforparameters
- if (!dispMode)
- {
- dispMode = BestMatchForMode(display_id, 32, width, height, match);
-
- if (!match)
- dispMode = BestMatchForMode(display_id, 16, width, height, match);
-
- // still no match? fallback to current resolution of the display which HAS to work [tm]
- if (!match)
- {
- int tmpWidth;
- int tmpHeight;
- double tmpRefresh;
-
- GetScreenResolution(&tmpWidth, &tmpHeight, &tmpRefresh, screenIdx);
- dispMode = GetMode(tmpWidth, tmpHeight, tmpRefresh, screenIdx);
-
- // no way to get a resolution set
- if (!dispMode)
- return false;
- }
-
- if (!match)
- return false;
- }
-
- // switch mode and return success
- CGDisplayCapture(display_id);
- CGDisplayConfigRef cfg;
- CGBeginDisplayConfiguration(&cfg);
- CGConfigureDisplayWithDisplayMode(cfg, display_id, dispMode, nullptr);
- CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly);
- CGDisplayRelease(display_id);
-
- m_refreshRate = CGDisplayModeGetRefreshRate(dispMode);
-
- Cocoa_CVDisplayLinkUpdate();
-
- return (err == kCGErrorSuccess);
-}
-
-void CWinSystemOSX::FillInVideoModes()
-{
- int dispIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR));
-
- // Add full screen settings for additional monitors
- int numDisplays = [[NSScreen screens] count];
-
- for (int disp = 0; disp < numDisplays; disp++)
- {
- Boolean stretched;
- Boolean interlaced;
- Boolean safeForHardware;
- int w, h, bitsperpixel;
- double refreshrate;
- RESOLUTION_INFO res;
-
- CFArrayRef displayModes = GetAllDisplayModes(GetDisplayID(disp));
- NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
-
- CLog::Log(LOGINFO, "Display {} has name {}", disp, [dispName UTF8String]);
-
- if (NULL == displayModes)
- continue;
-
- for (int i=0; i < CFArrayGetCount(displayModes); ++i)
- {
- CGDisplayModeRef displayMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i);
-
- uint32_t flags = CGDisplayModeGetIOFlags(displayMode);
- stretched = flags & kDisplayModeStretchedFlag ? true : false;
- interlaced = flags & kDisplayModeInterlacedFlag ? true : false;
- bitsperpixel = DisplayBitsPerPixelForMode(displayMode);
- safeForHardware = flags & kDisplayModeSafetyFlags ? true : false;
-
- if ((bitsperpixel == 32) &&
- (safeForHardware == YES) &&
- (stretched == NO) &&
- (interlaced == NO))
- {
- w = CGDisplayModeGetWidth(displayMode);
- h = CGDisplayModeGetHeight(displayMode);
- refreshrate = CGDisplayModeGetRefreshRate(displayMode);
- if ((int)refreshrate == 0) // LCD display?
- {
- // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
- refreshrate = 60.0;
- }
- CLog::Log(LOGINFO, "Found possible resolution for display {} with {} x {} @ {:f} Hz", disp,
- w, h, refreshrate);
-
- // only add the resolution if it belongs to "our" screen
- // all others are only logged above...
- if (disp == dispIdx)
- {
- UpdateDesktopResolution(res, (dispName != nil) ? [dispName UTF8String] : "Unknown", w, h, refreshrate, 0);
- CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
- CDisplaySettings::GetInstance().AddResolutionInfo(res);
- }
- }
- }
- CFRelease(displayModes);
- }
-}
-
-bool CWinSystemOSX::FlushBuffer(void)
-{
- if (m_updateGLContext < 5)
- {
- [m_impl->m_glContext update];
- m_updateGLContext++;
- }
-
- [m_impl->m_glContext flushBuffer];
-
- return true;
-}
-
-bool CWinSystemOSX::IsObscured(void)
-{
- // check once a second if we are obscured.
- auto now_time = std::chrono::steady_clock::now();
- if (m_obscured_timecheck > now_time)
- return m_obscured;
- else
- m_obscured_timecheck = now_time + std::chrono::milliseconds(1000);
-
- NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
- NSView* view = [cur_context view];
- if (!view)
- {
- // sanity check, we should always have a view
- m_obscured = true;
- return m_obscured;
- }
-
- NSWindow *window = [view window];
- if (!window)
- {
- // sanity check, we should always have a window
- m_obscured = true;
- return m_obscured;
- }
-
- if ([window isVisible] == NO)
- {
- // not visible means the window is not showing.
- // this should never really happen as we are always visible
- // even when minimized in dock.
- m_obscured = true;
- return m_obscured;
- }
-
- // check if we are minimized (to an icon in the Dock).
- if ([window isMiniaturized] == YES)
- {
- m_obscured = true;
- return m_obscured;
- }
-
- // check if we are showing on the active workspace.
- if ([window isOnActiveSpace] == NO)
- {
- m_obscured = true;
- return m_obscured;
- }
-
- // default to false before we start parsing though the windows.
- // if we are are obscured by any windows, then set true.
- m_obscured = false;
- static bool obscureLogged = false;
-
- CGWindowListOption opts;
- opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
- CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]);
-
- if (!windowIDs)
- return m_obscured;
-
- CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs);
- if (!windowDescs)
- {
- CFRelease(windowIDs);
- return m_obscured;
- }
-
- CGRect bounds = NSRectToCGRect([window frame]);
- // kCGWindowBounds measures the origin as the top-left corner of the rectangle
- // relative to the top-left corner of the screen.
- // NSWindow’s frame property measures the origin as the bottom-left corner
- // of the rectangle relative to the bottom-left corner of the screen.
- // convert bounds from NSWindow to CGWindowBounds here.
- bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height;
-
- std::vector<CRect> partialOverlaps;
- CRect ourBounds = CGRectToCRect(bounds);
-
- for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++)
- {
- // walk the window list of windows that are above us and are not desktop elements
- CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx);
-
- // skip the Dock window, it actually covers the entire screen.
- CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName);
- if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo)
- continue;
-
- // Ignore known brightness tools for dimming the screen. They claim to cover
- // the whole XBMC window and therefore would make the framerate limiter
- // kicking in. Unfortunately even the alpha of these windows is 1.0 so
- // we have to check the ownerName.
- if (CFStringCompare(ownerName, CFSTR("Shades"), 0) == kCFCompareEqualTo ||
- CFStringCompare(ownerName, CFSTR("SmartSaver"), 0) == kCFCompareEqualTo ||
- CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo ||
- CFStringCompare(ownerName, CFSTR("Displaperture"), 0) == kCFCompareEqualTo ||
- CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0) == kCFCompareEqualTo ||
- CFStringCompare(ownerName, CFSTR("Window Server"), 0) == kCFCompareEqualTo)
- continue;
-
- CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
- if (!rectDictionary)
- continue;
-
- CGRect windowBounds;
- if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds))
- {
- if (CGRectContainsRect(windowBounds, bounds))
- {
- // if the windowBounds completely encloses our bounds, we are obscured.
- if (!obscureLogged)
- {
- std::string appName;
- if (CDarwinUtils::CFStringRefToUTF8String(ownerName, appName))
- CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window {} obscures Kodi!", appName);
- obscureLogged = true;
- }
- m_obscured = true;
- break;
- }
-
- // handle overlapping windows above us that combine
- // to obscure by collecting any partial overlaps,
- // then subtract them from our bounds and check
- // for any remaining area.
- CRect intersection = CGRectToCRect(windowBounds);
- intersection.Intersect(ourBounds);
- if (!intersection.IsEmpty())
- partialOverlaps.push_back(intersection);
- }
- }
-
- if (!m_obscured)
- {
- // if we are here we are not obscured by any fullscreen window - reset flag
- // for allowing the logmessage above to show again if this changes.
- if (obscureLogged)
- obscureLogged = false;
- std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
- // they got us covered
- if (rects.empty())
- m_obscured = true;
- }
-
- CFRelease(windowDescs);
- CFRelease(windowIDs);
-
- return m_obscured;
-}
-
-void CWinSystemOSX::NotifyAppFocusChange(bool bGaining)
-{
- if (!(m_bFullScreen && bGaining))
- return;
- @autoreleasepool
- {
- // find the window
- NSOpenGLContext* context = [NSOpenGLContext currentContext];
- if (context)
- {
- NSView* view;
-
- view = [context view];
- if (view)
- {
- NSWindow* window;
- window = [view window];
- if (window)
- {
- SetMenuBarVisible(false);
- [window orderFront:nil];
- }
- }
- }
- }
-}
-
-void CWinSystemOSX::ShowOSMouse(bool show)
-{
- SDL_ShowCursor(show ? 1 : 0);
-}
-
-bool CWinSystemOSX::Minimize()
-{
- @autoreleasepool
- {
- [[NSApplication sharedApplication] miniaturizeAll:nil];
- }
- return true;
-}
-
-bool CWinSystemOSX::Restore()
-{
- @autoreleasepool
- {
- [[NSApplication sharedApplication] unhide:nil];
- }
- return true;
-}
-
-bool CWinSystemOSX::Hide()
-{
- @autoreleasepool
- {
- [[NSApplication sharedApplication] hide:nil];
- }
- return true;
-}
-
-void CWinSystemOSX::HandlePossibleRefreshrateChange()
-{
- static double oldRefreshRate = m_refreshRate;
- Cocoa_CVDisplayLinkUpdate();
- int dummy = 0;
-
- GetScreenResolution(&dummy, &dummy, &m_refreshRate, m_lastDisplayNr);
-
- if (oldRefreshRate != m_refreshRate)
- {
- oldRefreshRate = m_refreshRate;
- // send a message so that videoresolution (and refreshrate)
- // is changed
- CServiceBroker::GetAppMessenger()->PostMsg(TMSG_VIDEORESIZE, m_SDLSurface->w, m_SDLSurface->h);
- }
-}
-
-void CWinSystemOSX::OnMove(int x, int y)
-{
- HandlePossibleRefreshrateChange();
-}
-
-std::unique_ptr<IOSScreenSaver> CWinSystemOSX::GetOSScreenSaverImpl()
-{
- return std::unique_ptr<IOSScreenSaver> (new COSScreenSaverOSX);
-}
-
-OSXTextInputResponder *g_textInputResponder = nil;
-
-void CWinSystemOSX::StartTextInput()
-{
- NSView *parentView = [[NSApp keyWindow] contentView];
-
- /* We only keep one field editor per process, since only the front most
- * window can receive text input events, so it make no sense to keep more
- * than one copy. When we switched to another window and requesting for
- * text input, simply remove the field editor from its superview then add
- * it to the front most window's content view */
- if (!g_textInputResponder) {
- g_textInputResponder =
- [[OSXTextInputResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
- }
-
- if (![[g_textInputResponder superview] isEqual: parentView])
- {
-// DLOG(@"add fieldEdit to window contentView");
- [g_textInputResponder removeFromSuperview];
- [parentView addSubview: g_textInputResponder];
- [[NSApp keyWindow] makeFirstResponder: g_textInputResponder];
- }
-}
-void CWinSystemOSX::StopTextInput()
-{
- if (g_textInputResponder)
- {
- [g_textInputResponder removeFromSuperview];
- g_textInputResponder = nil;
- }
-}
-
-void CWinSystemOSX::Register(IDispResource *resource)
-{
- std::unique_lock<CCriticalSection> lock(m_resourceSection);
- m_resources.push_back(resource);
-}
-
-void CWinSystemOSX::Unregister(IDispResource* resource)
-{
- std::unique_lock<CCriticalSection> lock(m_resourceSection);
- std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
- if (i != m_resources.end())
- m_resources.erase(i);
-}
-
-bool CWinSystemOSX::Show(bool raise)
-{
- @autoreleasepool
- {
- auto app = [NSApplication sharedApplication];
- if (raise)
- {
- [app unhide:nil];
- [app activateIgnoringOtherApps:YES];
- [app arrangeInFront:nil];
- }
- else
- {
- [app unhideWithoutActivation];
- }
- }
- return true;
-}
-
-void CWinSystemOSX::WindowChangedScreen()
-{
- // user has moved the window to a
- // different screen
- NSOpenGLContext* context = [NSOpenGLContext currentContext];
- m_lastDisplayNr = -1;
-
- // if we are here the user dragged the window to a different
- // screen and we return the screen of the window
- if (context)
- {
- NSView* view;
-
- view = [context view];
- if (view)
- {
- NSWindow* window;
- window = [view window];
- if (window)
- {
- m_lastDisplayNr = GetDisplayIndex(GetDisplayIDFromScreen([window screen]));
- }
- }
- }
- if (m_lastDisplayNr == -1)
- m_lastDisplayNr = 0;// default to main screen
-}
-
-void CWinSystemOSX::AnnounceOnLostDevice()
-{
- std::unique_lock<CCriticalSection> lock(m_resourceSection);
- // tell any shared resources
- CLog::Log(LOGDEBUG, "CWinSystemOSX::AnnounceOnLostDevice");
- for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
- (*i)->OnLostDisplay();
-}
-
-void CWinSystemOSX::HandleOnResetDevice()
-{
-
- auto delay =
- std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
- "videoscreen.delayrefreshchange") *
- 100);
- if (delay > 0ms)
- {
- m_delayDispReset = true;
- m_dispResetTimer.Set(delay);
- }
- else
- {
- AnnounceOnResetDevice();
- }
-}
-
-void CWinSystemOSX::AnnounceOnResetDevice()
-{
- double currentFps = m_refreshRate;
- int w = 0;
- int h = 0;
- int currentScreenIdx = m_lastDisplayNr;
- // ensure that graphics context knows about the current refreshrate before
- // doing the callbacks
- GetScreenResolution(&w, &h, &currentFps, currentScreenIdx);
-
- CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(currentFps);
-
- std::unique_lock<CCriticalSection> lock(m_resourceSection);
- // tell any shared resources
- CLog::Log(LOGDEBUG, "CWinSystemOSX::AnnounceOnResetDevice");
- for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
- (*i)->OnResetDisplay();
-}
-
-void* CWinSystemOSX::GetCGLContextObj()
-{
- return [m_impl->m_glContext CGLContextObj];
-}
-
-NSOpenGLContext* CWinSystemOSX::GetNSOpenGLContext()
-{
- return m_impl->m_glContext;
-}
-
-std::string CWinSystemOSX::GetClipboardText(void)
-{
- std::string utf8_text;
-
- const char *szStr = Cocoa_Paste();
- if (szStr)
- utf8_text = szStr;
-
- return utf8_text;
-}
-
-std::unique_ptr<CVideoSync> CWinSystemOSX::GetVideoSync(CVideoReferenceClock* clock)
-{
- std::unique_ptr<CVideoSync> pVSync(new CVideoSyncOsx(clock));
- return pVSync;
-}
-
-bool CWinSystemOSX::MessagePump()
-{
- return m_winEvents->MessagePump();
-}
-
-std::vector<std::string> CWinSystemOSX::GetConnectedOutputs()
-{
- std::vector<std::string> outputs;
- outputs.emplace_back("Default");
-
- int numDisplays = [[NSScreen screens] count];
-
- for (int disp = 0; disp < numDisplays; disp++)
- {
- NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
- outputs.emplace_back([dispName UTF8String]);
- }
-
- return outputs;
-}
diff --git a/xbmc/windowing/osx/WinEventsOSXImpl.mm b/xbmc/windowing/osx/WinEventsOSXImpl.mm
index 27b609da98..4a35ba1c20 100644
--- a/xbmc/windowing/osx/WinEventsOSXImpl.mm
+++ b/xbmc/windowing/osx/WinEventsOSXImpl.mm
@@ -108,6 +108,7 @@
case NSDeleteCharacter:
return XBMCK_BACKSPACE;
case NSCarriageReturnCharacter:
+ case NSEnterCharacter:
return XBMCK_RETURN;
default:
return character;
diff --git a/xbmc/windowing/osx/WinSystemOSX.mm b/xbmc/windowing/osx/WinSystemOSX.mm
index a5b4897b8e..04a0670a17 100644
--- a/xbmc/windowing/osx/WinSystemOSX.mm
+++ b/xbmc/windowing/osx/WinSystemOSX.mm
@@ -40,6 +40,7 @@
#include <array>
#include <chrono>
+#include <memory>
#include <mutex>
#import <IOKit/graphics/IOGraphicsLib.h>
@@ -583,7 +584,7 @@ CWinSystemOSX::CWinSystemOSX() : CWinSystemBase(), m_lostDeviceTimer(this)
m_refreshRate = 0.0;
m_delayDispReset = false;
- m_winEvents.reset(new CWinEventsOSX());
+ m_winEvents = std::make_unique<CWinEventsOSX>();
AE::CAESinkFactory::ClearSinks();
CAESinkDARWINOSX::Register();
@@ -1345,7 +1346,7 @@ std::unique_ptr<CVideoSync> CWinSystemOSX::GetVideoSync(CVideoReferenceClock* cl
std::vector<std::string> CWinSystemOSX::GetConnectedOutputs()
{
std::vector<std::string> outputs;
- outputs.push_back(DEFAULT_SCREEN_NAME);
+ outputs.emplace_back(DEFAULT_SCREEN_NAME);
// screen 0 is always the "Default" setting, avoid duplicating the available
// screens here.
@@ -1355,7 +1356,7 @@ std::vector<std::string> CWinSystemOSX::GetConnectedOutputs()
for (NSUInteger disp = 1; disp <= numDisplays - 1; disp++)
{
NSString* const dispName = screenNameForDisplay(disp);
- outputs.push_back(dispName.UTF8String);
+ outputs.emplace_back(dispName.UTF8String);
}
}
diff --git a/xbmc/windowing/tvos/WinSystemTVOS.mm b/xbmc/windowing/tvos/WinSystemTVOS.mm
index bd47c6facb..18546f3cd6 100644
--- a/xbmc/windowing/tvos/WinSystemTVOS.mm
+++ b/xbmc/windowing/tvos/WinSystemTVOS.mm
@@ -140,7 +140,7 @@ CWinSystemTVOS::CWinSystemTVOS() : CWinSystemBase(), m_lostDeviceTimer(this)
m_pDisplayLink = new CADisplayLinkWrapper;
m_pDisplayLink->callbackClass = [[TVOSDisplayLinkCallback alloc] init];
- m_winEvents.reset(new CWinEventsTVOS());
+ m_winEvents = std::make_unique<CWinEventsTVOS>();
CAESinkDARWINTVOS::Register();
}
diff --git a/xbmc/windowing/wayland/InputProcessorKeyboard.cpp b/xbmc/windowing/wayland/InputProcessorKeyboard.cpp
index 6ec9ccb9f3..7db37592d5 100644
--- a/xbmc/windowing/wayland/InputProcessorKeyboard.cpp
+++ b/xbmc/windowing/wayland/InputProcessorKeyboard.cpp
@@ -8,10 +8,12 @@
#include "InputProcessorKeyboard.h"
+#include "LangInfo.h"
#include "utils/log.h"
#include <cassert>
#include <limits>
+#include <memory>
using namespace KODI::WINDOWING::WAYLAND;
@@ -44,10 +46,10 @@ void CInputProcessorKeyboard::OnKeyboardKeymap(CSeat* seat, wayland::keyboard_ke
if (!m_xkbContext)
{
// Lazily initialize XkbcommonContext
- m_xkbContext.reset(new CXkbcommonContext);
+ m_xkbContext = std::make_unique<CXkbcommonContext>();
}
- m_keymap = m_xkbContext->KeymapFromString(keymap);
+ m_keymap = m_xkbContext->LocalizedKeymapFromString(keymap, g_langInfo.GetSystemLocale().name());
}
catch(std::exception const& e)
{
@@ -105,10 +107,43 @@ void CInputProcessorKeyboard::OnKeyboardRepeatInfo(CSeat* seat, std::int32_t rat
void CInputProcessorKeyboard::ConvertAndSendKey(std::uint32_t scancode, bool pressed)
{
- std::uint32_t xkbCode{scancode + WL_KEYBOARD_XKB_CODE_OFFSET};
- XBMCKey xbmcKey{m_keymap->XBMCKeyForKeycode(xkbCode)};
- std::uint32_t utf32{m_keymap->UnicodeCodepointForKeycode(xkbCode)};
+ const std::uint32_t xkbCode{scancode + WL_KEYBOARD_XKB_CODE_OFFSET};
+
+ bool flushComposer{false};
+ if (pressed && m_keymap->SupportsKeyComposition())
+ {
+ // feed it first to the key composer if from a press
+ const KeyComposerStatus feedResult = m_keymap->KeyComposerFeed(xkbCode);
+ if (feedResult.state == KeyComposerState::COMPOSING)
+ {
+ // let the outside world (e.g. GUI controls) know we're composing a key
+ NotifyKeyComposingEvent(XBMC_KEYCOMPOSING_COMPOSING, feedResult.keysym);
+ return;
+ }
+ else if (feedResult.state == KeyComposerState::FINISHED)
+ {
+ flushComposer = true;
+ // let the outside world (e.g. GUI Controls) know we're back to normal input
+ NotifyKeyComposingEvent(XBMC_KEYCOMPOSING_FINISHED, XBMCK_UNKNOWN);
+ }
+ else if (feedResult.state == KeyComposerState::CANCELLED)
+ {
+ // composed sequence was cancelled, we're back to normal input
+ // let the outside world know what key lead to the cancellation (replay sequence)
+ const std::uint32_t unicodeCodePointCancellationKey{
+ m_keymap->UnicodeCodepointForKeycode(xkbCode)};
+ NotifyKeyComposingEvent(XBMC_KEYCOMPOSING_CANCELLED, unicodeCodePointCancellationKey);
+ m_keymap->KeyComposerFlush();
+ // do not allow key fallthrough if we are simply cancelling with a backspace (we are cancelling the
+ // composition behavior not really removing any character)
+ if (unicodeCodePointCancellationKey == XBMCK_BACKSPACE)
+ {
+ return;
+ }
+ }
+ }
+ std::uint32_t utf32{m_keymap->UnicodeCodepointForKeycode(xkbCode)};
if (utf32 > std::numeric_limits<std::uint16_t>::max())
{
// Kodi event system only supports UTF16, so ignore the codepoint if
@@ -122,6 +157,13 @@ void CInputProcessorKeyboard::ConvertAndSendKey(std::uint32_t scancode, bool pre
scancode = 0;
}
+ // flush composer if set (after a finished sequence)
+ if (flushComposer)
+ {
+ m_keymap->KeyComposerFlush();
+ }
+
+ const XBMCKey xbmcKey{m_keymap->XBMCKeyForKeycode(xkbCode)};
if (xbmcKey == XBMCKey::XBMCK_UNKNOWN && utf32 == 0)
{
// Such an event would carry no useful information in it and thus can be safely dropped here
@@ -176,3 +218,12 @@ void CInputProcessorKeyboard::KeyRepeatTimeout()
event.type = XBMC_KEYDOWN;
m_handler.OnKeyboardEvent(event);
}
+
+void CInputProcessorKeyboard::NotifyKeyComposingEvent(uint8_t eventType,
+ std::uint16_t unicodeCodepoint)
+{
+ XBMC_Event event{};
+ event.type = eventType;
+ event.key.keysym.unicode = unicodeCodepoint;
+ m_handler.OnKeyboardEvent(event);
+}
diff --git a/xbmc/windowing/wayland/InputProcessorKeyboard.h b/xbmc/windowing/wayland/InputProcessorKeyboard.h
index 73a37cee7f..bc385c35c0 100644
--- a/xbmc/windowing/wayland/InputProcessorKeyboard.h
+++ b/xbmc/windowing/wayland/InputProcessorKeyboard.h
@@ -57,6 +57,13 @@ private:
void ConvertAndSendKey(std::uint32_t scancode, bool pressed);
XBMC_Event SendKey(unsigned char scancode, XBMCKey key, std::uint16_t unicodeCodepoint, bool pressed);
+ /**
+ * Notify the outside world about key composing events
+ *
+ * \param eventType - the key composition event type
+ * \param unicodeCodepoint - unicode codepoint of the pressed dead key
+ */
+ void NotifyKeyComposingEvent(uint8_t eventType, std::uint16_t unicodeCodepoint);
void KeyRepeatTimeout();
IInputHandlerKeyboard& m_handler;
diff --git a/xbmc/windowing/wayland/SeatInputProcessing.cpp b/xbmc/windowing/wayland/SeatInputProcessing.cpp
index 6430aad20c..66ddc632cc 100644
--- a/xbmc/windowing/wayland/SeatInputProcessing.cpp
+++ b/xbmc/windowing/wayland/SeatInputProcessing.cpp
@@ -9,6 +9,7 @@
#include "SeatInputProcessing.h"
#include <cassert>
+#include <memory>
using namespace KODI::WINDOWING::WAYLAND;
@@ -22,11 +23,11 @@ void CSeatInputProcessing::AddSeat(CSeat* seat)
assert(m_seats.find(seat->GetGlobalName()) == m_seats.end());
auto& seatState = m_seats.emplace(seat->GetGlobalName(), seat).first->second;
- seatState.keyboardProcessor.reset(new CInputProcessorKeyboard(*this));
+ seatState.keyboardProcessor = std::make_unique<CInputProcessorKeyboard>(*this);
seat->AddRawInputHandlerKeyboard(seatState.keyboardProcessor.get());
- seatState.pointerProcessor.reset(new CInputProcessorPointer(m_inputSurface, *this));
+ seatState.pointerProcessor = std::make_unique<CInputProcessorPointer>(m_inputSurface, *this);
seat->AddRawInputHandlerPointer(seatState.pointerProcessor.get());
- seatState.touchProcessor.reset(new CInputProcessorTouch(m_inputSurface));
+ seatState.touchProcessor = std::make_unique<CInputProcessorTouch>(m_inputSurface);
seat->AddRawInputHandlerTouch(seatState.touchProcessor.get());
}
diff --git a/xbmc/windowing/wayland/SeatInputProcessing.h b/xbmc/windowing/wayland/SeatInputProcessing.h
index ce1f0ee759..e24d0e3247 100644
--- a/xbmc/windowing/wayland/SeatInputProcessing.h
+++ b/xbmc/windowing/wayland/SeatInputProcessing.h
@@ -79,7 +79,7 @@ public:
* Multi-seat support is not currently implemented completely, but each seat has
* separate state.
*/
-class CSeatInputProcessing final : IInputHandlerPointer, IInputHandlerKeyboard
+class CSeatInputProcessing final : public IInputHandlerPointer, public IInputHandlerKeyboard
{
public:
/**
diff --git a/xbmc/windowing/wayland/ShellSurfaceWebOSShell.cpp b/xbmc/windowing/wayland/ShellSurfaceWebOSShell.cpp
index a4b9ab869d..2bc6606587 100644
--- a/xbmc/windowing/wayland/ShellSurfaceWebOSShell.cpp
+++ b/xbmc/windowing/wayland/ShellSurfaceWebOSShell.cpp
@@ -61,6 +61,7 @@ CShellSurfaceWebOSShell::CShellSurfaceWebOSShell(IShellSurfaceHandler& handler,
case webos_shell_surface_state::minimized:
CLog::Log(LOGDEBUG, "CShellSurfaceWebOSShell: State changed to minimized");
m_surfaceState.reset();
+ m_handler.OnConfigure(0, m_windowSize, m_surfaceState);
break;
case webos_shell_surface_state::_default:
CLog::Log(LOGDEBUG, "CShellSurfaceWebOSShell: State changed to default (windowed)");
diff --git a/xbmc/windowing/wayland/WinEventsWayland.cpp b/xbmc/windowing/wayland/WinEventsWayland.cpp
index 4345cf00d9..da6e618b56 100644
--- a/xbmc/windowing/wayland/WinEventsWayland.cpp
+++ b/xbmc/windowing/wayland/WinEventsWayland.cpp
@@ -216,7 +216,7 @@ void CWinEventsWayland::SetDisplay(wayland::display_t* display)
if (display && !g_WlMessagePump)
{
// Start message processing as soon as we have a display
- g_WlMessagePump.reset(new CWinEventsWaylandThread(*display));
+ g_WlMessagePump = std::make_unique<CWinEventsWaylandThread>(*display);
}
else if (g_WlMessagePump)
{
diff --git a/xbmc/windowing/wayland/WinSystemWayland.cpp b/xbmc/windowing/wayland/WinSystemWayland.cpp
index 244b4b6820..e226563a35 100644
--- a/xbmc/windowing/wayland/WinSystemWayland.cpp
+++ b/xbmc/windowing/wayland/WinSystemWayland.cpp
@@ -47,13 +47,10 @@
#include <algorithm>
#include <limits>
+#include <memory>
#include <mutex>
#include <numeric>
-#if defined(HAS_DBUS)
-# include "windowing/linux/OSScreenSaverFreedesktop.h"
-#endif
-
using namespace KODI::WINDOWING;
using namespace KODI::WINDOWING::WAYLAND;
using namespace std::placeholders;
@@ -139,7 +136,7 @@ struct MsgBufferScale
CWinSystemWayland::CWinSystemWayland()
: CWinSystemBase{}, m_protocol{"WinSystemWaylandInternal"}
{
- m_winEvents.reset(new CWinEventsWayland());
+ m_winEvents = std::make_unique<CWinEventsWayland>();
}
CWinSystemWayland::~CWinSystemWayland() noexcept
@@ -167,7 +164,7 @@ bool CWinSystemWayland::InitWindowSystem()
VIDEOPLAYER::CProcessInfoWayland::Register();
RETRO::CRPProcessInfoWayland::Register();
- m_registry.reset(new CRegistry{*m_connection});
+ m_registry = std::make_unique<CRegistry>(*m_connection);
m_registry->RequestSingleton(m_compositor, 1, 4);
m_registry->RequestSingleton(m_shm, 1, 1);
@@ -279,10 +276,10 @@ bool CWinSystemWayland::CreateNewWindow(const std::string& name,
}
};
- m_windowDecorator.reset(new CWindowDecorator(*this, *m_connection, m_surface));
+ m_windowDecorator = std::make_unique<CWindowDecorator>(*this, *m_connection, m_surface);
- m_seatInputProcessing.reset(new CSeatInputProcessing(m_surface, *this));
- m_seatRegistry.reset(new CRegistry{*m_connection});
+ m_seatInputProcessing = std::make_unique<CSeatInputProcessing>(m_surface, *this);
+ m_seatRegistry = std::make_unique<CRegistry>(*m_connection);
// version 2 adds name event -> optional
// version 4 adds wl_keyboard repeat_info -> optional
// version 5 adds discrete axis events in wl_pointer -> unused
diff --git a/xbmc/windowing/wayland/WinSystemWayland.h b/xbmc/windowing/wayland/WinSystemWayland.h
index 62720c5e18..95f2a9cfea 100644
--- a/xbmc/windowing/wayland/WinSystemWayland.h
+++ b/xbmc/windowing/wayland/WinSystemWayland.h
@@ -45,8 +45,8 @@ class CRegistry;
class CWindowDecorator;
class CWinSystemWayland : public CWinSystemBase,
- IInputHandler,
- IWindowDecorationHandler,
+ public IInputHandler,
+ public IWindowDecorationHandler,
public IShellSurfaceHandler
{
public:
@@ -118,6 +118,10 @@ protected:
virtual void SetContextSize(CSizeInt size) = 0;
virtual IShellSurface* CreateShellSurface(const std::string& name);
+ // IShellSurfaceHandler
+ void OnConfigure(std::uint32_t serial, CSizeInt size, IShellSurface::StateBitset state) override;
+ void OnClose() override;
+
private:
// IInputHandler
void OnEnter(InputType type) override;
@@ -133,10 +137,6 @@ private:
void OnWindowMaximize() override;
void OnWindowMinimize() override;
- // IShellSurfaceHandler
- void OnConfigure(std::uint32_t serial, CSizeInt size, IShellSurface::StateBitset state) override;
- void OnClose() override;
-
// Registry handlers
void OnSeatAdded(std::uint32_t name, wayland::proxy_t&& seat);
void OnSeatRemoved(std::uint32_t name);
diff --git a/xbmc/windowing/wayland/WinSystemWaylandWebOS.cpp b/xbmc/windowing/wayland/WinSystemWaylandWebOS.cpp
index ec1567b2a2..bfbb2fa286 100644
--- a/xbmc/windowing/wayland/WinSystemWaylandWebOS.cpp
+++ b/xbmc/windowing/wayland/WinSystemWaylandWebOS.cpp
@@ -12,9 +12,14 @@
#include "OSScreenSaverWebOS.h"
#include "Registry.h"
#include "ShellSurfaceWebOSShell.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
#include "cores/AudioEngine/Sinks/AESinkStarfish.h"
#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecStarfish.h"
#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererStarfish.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "messaging/ApplicationMessenger.h"
#include "utils/JSONVariantParser.h"
#include "utils/log.h"
@@ -127,6 +132,11 @@ bool CWinSystemWaylandWebOS::SetExportedWindow(CRect orig, CRect src, CRect dest
return false;
}
+bool CWinSystemWaylandWebOS::SupportsExportedWindow()
+{
+ return m_webosForeign;
+}
+
IShellSurface* CWinSystemWaylandWebOS::CreateShellSurface(const std::string& name)
{
return new CShellSurfaceWebOSShell(*this, *GetConnection(), GetMainSurface(), name,
@@ -144,6 +154,37 @@ bool CWinSystemWaylandWebOS::OnAppLifecycleEventWrapper(LSHandle* sh, LSMessage*
return static_cast<CWinSystemWaylandWebOS*>(context->userdata)->OnAppLifecycleEvent(sh, reply);
}
+void CWinSystemWaylandWebOS::OnConfigure(std::uint32_t serial,
+ CSizeInt size,
+ IShellSurface::StateBitset state)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto player = components.GetComponent<CApplicationPlayer>();
+
+ // intercept minimized event, passing the minimized event causes a weird animation
+ if (state.none())
+ {
+ m_resumePlayback = false;
+
+ if (player->IsPlaying() && player->HasVideo() && !player->IsPaused())
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
+ static_cast<void*>(new CAction(ACTION_PAUSE)));
+ m_resumePlayback = true;
+ }
+ }
+ else
+ {
+ if (m_resumePlayback && player->IsPlaying() && player->HasVideo() && player->IsPaused())
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_PLAYER_PLAY)));
+ m_resumePlayback = false;
+ }
+ CWinSystemWayland::OnConfigure(serial, size, state);
+ }
+}
+
bool CWinSystemWaylandWebOS::OnAppLifecycleEvent(LSHandle* sh, LSMessage* reply)
{
const char* msg = HLunaServiceMessage(reply);
diff --git a/xbmc/windowing/wayland/WinSystemWaylandWebOS.h b/xbmc/windowing/wayland/WinSystemWaylandWebOS.h
index 5584d5eb32..3f3c9821b4 100644
--- a/xbmc/windowing/wayland/WinSystemWaylandWebOS.h
+++ b/xbmc/windowing/wayland/WinSystemWaylandWebOS.h
@@ -40,10 +40,13 @@ public:
*/
bool SetExportedWindow(CRect orig, CRect src, CRect dest);
+ bool SupportsExportedWindow();
+
IShellSurface* CreateShellSurface(const std::string& name) override;
bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override;
~CWinSystemWaylandWebOS() noexcept override;
bool HasCursor() override;
+ void OnConfigure(std::uint32_t serial, CSizeInt size, IShellSurface::StateBitset state) override;
protected:
std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> GetOSScreenSaverImpl() override;
@@ -62,6 +65,8 @@ private:
std::unique_ptr<HContext, int (*)(HContext*)> m_requestContext{new HContext(),
HUnregisterServiceCallback};
+
+ bool m_resumePlayback{false};
};
} // namespace KODI::WINDOWING::WAYLAND
diff --git a/xbmc/windowing/wayland/WindowDecorator.cpp b/xbmc/windowing/wayland/WindowDecorator.cpp
index 54f52d8987..a0ae2ea2e4 100644
--- a/xbmc/windowing/wayland/WindowDecorator.cpp
+++ b/xbmc/windowing/wayland/WindowDecorator.cpp
@@ -15,6 +15,7 @@
#include <algorithm>
#include <cassert>
#include <cmath>
+#include <memory>
#include <mutex>
#include <vector>
@@ -885,7 +886,7 @@ void CWindowDecorator::ResetShm()
{
if (IsDecorationActive())
{
- m_memory.reset(new CSharedMemory(MemoryBytesForSize(m_mainSurfaceSize, m_scale)));
+ m_memory = std::make_unique<CSharedMemory>(MemoryBytesForSize(m_mainSurfaceSize, m_scale));
m_memoryAllocatedSize = 0;
m_shmPool = m_shm.create_pool(m_memory->Fd(), m_memory->Size());
}
diff --git a/xbmc/windowing/wayland/XkbcommonKeymap.cpp b/xbmc/windowing/wayland/XkbcommonKeymap.cpp
index d0aed35c95..81190ccac9 100644
--- a/xbmc/windowing/wayland/XkbcommonKeymap.cpp
+++ b/xbmc/windowing/wayland/XkbcommonKeymap.cpp
@@ -9,17 +9,36 @@
#include "XkbcommonKeymap.h"
#include "Util.h"
+#include "utils/StringUtils.h"
#include "utils/log.h"
#include <iostream>
+#include <memory>
+#include <optional>
#include <sstream>
#include <stdexcept>
+#include <unordered_map>
#include <vector>
using namespace KODI::WINDOWING::WAYLAND;
namespace
{
+static const std::unordered_map<xkb_log_level, int> logLevelMap = {
+ {XKB_LOG_LEVEL_CRITICAL, LOGERROR}, {XKB_LOG_LEVEL_ERROR, LOGERROR},
+ {XKB_LOG_LEVEL_WARNING, LOGWARNING}, {XKB_LOG_LEVEL_INFO, LOGINFO},
+ {XKB_LOG_LEVEL_DEBUG, LOGDEBUG},
+};
+
+static void xkbLogger(xkb_context* context,
+ xkb_log_level priority,
+ const char* format,
+ va_list args)
+{
+ const std::string message = StringUtils::FormatV(format, args);
+ auto logLevel = logLevelMap.find(priority);
+ CLog::Log(logLevel != logLevelMap.end() ? logLevel->second : LOGDEBUG, "[xkb] {}", message);
+}
struct ModifierNameXBMCMapping
{
@@ -195,6 +214,62 @@ static const std::map<xkb_keycode_t, XBMCKey> XkbKeycodeXBMCMappings = {
{XKB_KEY_WEBOS_INVALID, XBMCK_UNKNOWN},
#endif
};
+
+static const std::unordered_map<xkb_keycode_t, XBMCKey> XkbDeadKeyXBMCMapping = {
+ {XKB_KEY_dead_grave, XBMCK_GRAVE},
+ {XKB_KEY_dead_tilde, XBMCK_TILDE},
+ {XKB_KEY_dead_acute, XBMCK_ACUTE},
+ {XKB_KEY_dead_circumflex, XBMCK_CIRCUMFLEX},
+ {XKB_KEY_dead_perispomeni, XBMCK_PERISPOMENI},
+ {XKB_KEY_dead_macron, XBMCK_MACRON},
+ {XKB_KEY_dead_breve, XBMCK_BREVE},
+ {XKB_KEY_dead_abovedot, XBMCK_ABOVEDOT},
+ {XKB_KEY_dead_diaeresis, XBMCK_DIAERESIS},
+ {XKB_KEY_dead_abovering, XBMCK_ABOVERING},
+ {XKB_KEY_dead_doubleacute, XBMCK_DOUBLEACUTE},
+ {XKB_KEY_dead_caron, XBMCK_CARON},
+ {XKB_KEY_dead_cedilla, XBMCK_CEDILLA},
+ {XKB_KEY_dead_ogonek, XBMCK_OGONEK},
+ {XKB_KEY_dead_iota, XBMCK_IOTA},
+ {XKB_KEY_dead_voiced_sound, XBMCK_VOICESOUND},
+ {XKB_KEY_dead_semivoiced_sound, XBMCK_SEMIVOICESOUND},
+ {XKB_KEY_dead_belowdot, XBMCK_BELOWDOT},
+ {XKB_KEY_dead_hook, XBMCK_HOOK},
+ {XKB_KEY_dead_horn, XBMCK_HORN},
+ {XKB_KEY_dead_stroke, XBMCK_STROKE},
+ {XKB_KEY_dead_abovecomma, XBMCK_ABOVECOMMA},
+ {XKB_KEY_dead_psili, XBMCK_ABOVECOMMA},
+ {XKB_KEY_dead_abovereversedcomma, XBMCK_ABOVEREVERSEDCOMMA},
+ {XKB_KEY_dead_dasia, XBMCK_OGONEK},
+ {XKB_KEY_dead_doublegrave, XBMCK_DOUBLEGRAVE},
+ {XKB_KEY_dead_belowring, XBMCK_BELOWRING},
+ {XKB_KEY_dead_belowmacron, XBMCK_BELOWMACRON},
+ {XKB_KEY_dead_belowcircumflex, XBMCK_BELOWCIRCUMFLEX},
+ {XKB_KEY_dead_belowtilde, XBMCK_BELOWTILDE},
+ {XKB_KEY_dead_belowbreve, XBMCK_BELOWBREVE},
+ {XKB_KEY_dead_belowdiaeresis, XBMCK_BELOWDIAERESIS},
+ {XKB_KEY_dead_invertedbreve, XBMCK_INVERTEDBREVE},
+ {XKB_KEY_dead_belowcomma, XBMCK_BELOWCOMMA},
+ {XKB_KEY_dead_a, XBMCK_DEAD_A},
+ {XKB_KEY_dead_A, XBMCK_DEAD_A},
+ {XKB_KEY_dead_e, XBMCK_DEAD_E},
+ {XKB_KEY_dead_E, XBMCK_DEAD_E},
+ {XKB_KEY_dead_i, XBMCK_DEAD_I},
+ {XKB_KEY_dead_I, XBMCK_DEAD_I},
+ {XKB_KEY_dead_o, XBMCK_DEAD_O},
+ {XKB_KEY_dead_O, XBMCK_DEAD_O},
+ {XKB_KEY_dead_u, XBMCK_DEAD_U},
+ {XKB_KEY_dead_U, XBMCK_DEAD_U},
+ {XKB_KEY_dead_small_schwa, XBMCK_SCHWA},
+ {XKB_KEY_dead_capital_schwa, XBMCK_SCHWA},
+};
+
+std::optional<XBMCKey> TranslateDeadKey(uint32_t keySym)
+{
+ auto mapping = XkbDeadKeyXBMCMapping.find(keySym);
+ return mapping != XkbDeadKeyXBMCMapping.end() ? std::optional<XBMCKey>(mapping->second)
+ : std::nullopt;
+}
}
CXkbcommonContext::CXkbcommonContext(xkb_context_flags flags)
@@ -204,6 +279,10 @@ CXkbcommonContext::CXkbcommonContext(xkb_context_flags flags)
{
throw std::runtime_error("Failed to create xkb context");
}
+
+ // install logger
+ xkb_context_set_log_level(m_context.get(), XKB_LOG_LEVEL_DEBUG);
+ xkb_context_set_log_fn(m_context.get(), &xkbLogger);
}
void CXkbcommonContext::XkbContextDeleter::operator()(xkb_context* ctx) const
@@ -211,8 +290,10 @@ void CXkbcommonContext::XkbContextDeleter::operator()(xkb_context* ctx) const
xkb_context_unref(ctx);
}
-std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::KeymapFromString(std::string const& keymap)
+std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::LocalizedKeymapFromString(
+ const std::string& keymap, const std::string& locale)
{
+
std::unique_ptr<xkb_keymap, CXkbcommonKeymap::XkbKeymapDeleter> xkbKeymap{xkb_keymap_new_from_string(m_context.get(), keymap.c_str(), XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS), CXkbcommonKeymap::XkbKeymapDeleter()};
if (!xkbKeymap)
@@ -220,44 +301,55 @@ std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::KeymapFromString(std::strin
throw std::runtime_error("Failed to compile keymap");
}
- return std::unique_ptr<CXkbcommonKeymap>{new CXkbcommonKeymap(std::move(xkbKeymap))};
+ std::unique_ptr<xkb_compose_table, CXkbcommonKeymap::XkbComposeTableDeleter> xkbComposeTable{
+ xkb_compose_table_new_from_locale(m_context.get(), locale.c_str(),
+ XKB_COMPOSE_COMPILE_NO_FLAGS),
+ CXkbcommonKeymap::XkbComposeTableDeleter()};
+
+ if (!xkbComposeTable)
+ {
+ CLog::LogF(LOGWARNING,
+ "Failed to compile localized compose table, composed key support will be disabled");
+ }
+ return std::make_unique<CXkbcommonKeymap>(std::move(xkbKeymap), std::move(xkbComposeTable));
}
-std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::KeymapFromNames(const std::string& rules, const std::string& model, const std::string& layout, const std::string& variant, const std::string& options)
+std::unique_ptr<xkb_state, CXkbcommonKeymap::XkbStateDeleter> CXkbcommonKeymap::
+ CreateXkbStateFromKeymap(xkb_keymap* keymap)
{
- xkb_rule_names names = {
- rules.c_str(),
- model.c_str(),
- layout.c_str(),
- variant.c_str(),
- options.c_str()
- };
-
- std::unique_ptr<xkb_keymap, CXkbcommonKeymap::XkbKeymapDeleter> keymap{xkb_keymap_new_from_names(m_context.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS), CXkbcommonKeymap::XkbKeymapDeleter()};
+ std::unique_ptr<xkb_state, XkbStateDeleter> state{xkb_state_new(keymap), XkbStateDeleter()};
- if (!keymap)
+ if (!state)
{
- throw std::runtime_error("Failed to compile keymap");
+ throw std::runtime_error("Failed to create keyboard state");
}
- return std::unique_ptr<CXkbcommonKeymap>{new CXkbcommonKeymap(std::move(keymap))};
+ return state;
}
-std::unique_ptr<xkb_state, CXkbcommonKeymap::XkbStateDeleter> CXkbcommonKeymap::CreateXkbStateFromKeymap(xkb_keymap* keymap)
+std::unique_ptr<xkb_compose_state, CXkbcommonKeymap::XkbComposeStateDeleter> CXkbcommonKeymap::
+ CreateXkbComposedStateStateFromTable(xkb_compose_table* composeTable)
{
- std::unique_ptr<xkb_state, XkbStateDeleter> state{xkb_state_new(keymap), XkbStateDeleter()};
+ std::unique_ptr<xkb_compose_state, XkbComposeStateDeleter> state{
+ xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS), XkbComposeStateDeleter()};
if (!state)
{
- throw std::runtime_error("Failed to create keyboard state");
+ throw std::runtime_error("Failed to create keyboard composer");
}
return state;
}
-CXkbcommonKeymap::CXkbcommonKeymap(std::unique_ptr<xkb_keymap, XkbKeymapDeleter> keymap)
-: m_keymap{std::move(keymap)}, m_state{CreateXkbStateFromKeymap(m_keymap.get())}
+CXkbcommonKeymap::CXkbcommonKeymap(std::unique_ptr<xkb_keymap, XkbKeymapDeleter> keymap,
+ std::unique_ptr<xkb_compose_table, XkbComposeTableDeleter> table)
+ : m_keymap{std::move(keymap)}, m_state{CreateXkbStateFromKeymap(m_keymap.get())}
{
+ if (table)
+ {
+ m_composeState = CreateXkbComposedStateStateFromTable(table.get());
+ }
+
// Lookup modifier indices and create new map - this is more efficient
// than looking the modifiers up by name each time
for (auto const& nameMapping : ModifierNameXBMCMappings)
@@ -275,11 +367,21 @@ void CXkbcommonKeymap::XkbStateDeleter::operator()(xkb_state* state) const
xkb_state_unref(state);
}
+void CXkbcommonKeymap::XkbComposeStateDeleter::operator()(xkb_compose_state* state) const
+{
+ xkb_compose_state_unref(state);
+}
+
void CXkbcommonKeymap::XkbKeymapDeleter::operator()(xkb_keymap* keymap) const
{
xkb_keymap_unref(keymap);
}
+void CXkbcommonKeymap::XkbComposeTableDeleter::operator()(xkb_compose_table* table) const
+{
+ xkb_compose_table_unref(table);
+}
+
xkb_keysym_t CXkbcommonKeymap::KeysymForKeycode(xkb_keycode_t code) const
{
return xkb_state_key_get_one_sym(m_state.get(), code);
@@ -341,9 +443,80 @@ XBMCKey CXkbcommonKeymap::XBMCKeyForKeycode(xkb_keycode_t code) const
return XBMCKeyForKeysym(KeysymForKeycode(code));
}
+bool CXkbcommonKeymap::SupportsKeyComposition() const
+{
+ return m_composeState != nullptr;
+}
+
+KeyComposerStatus CXkbcommonKeymap::KeyComposerFeed(xkb_keycode_t code)
+{
+ KeyComposerStatus composerStatus;
+ const uint32_t keysym = xkb_state_key_get_one_sym(m_state.get(), code);
+ // store the pressed deadkey unicode value
+ const std::optional<XBMCKey> xbmcKey = TranslateDeadKey(keysym);
+ if (xbmcKey)
+ {
+ composerStatus.keysym = xbmcKey.value();
+ }
+ xkb_compose_state_feed(m_composeState.get(), keysym);
+ const xkb_compose_status composeStatus = xkb_compose_state_get_status(m_composeState.get());
+ // started composing a key
+ if (composeStatus == XKB_COMPOSE_COMPOSING)
+ {
+ composerStatus.state = KeyComposerState::COMPOSING;
+ }
+ // managed to compose a key from the buffer/key sequence
+ else if (composeStatus == XKB_COMPOSE_COMPOSED)
+ {
+ composerStatus.state = KeyComposerState::FINISHED;
+ }
+ // cancelled key composition, composer state should be reset
+ else if (composeStatus == XKB_COMPOSE_CANCELLED)
+ {
+ composerStatus.state = KeyComposerState::CANCELLED;
+ }
+ return composerStatus;
+}
+
+void CXkbcommonKeymap::KeyComposerFlush()
+{
+ xkb_compose_state_reset(m_composeState.get());
+}
+
std::uint32_t CXkbcommonKeymap::UnicodeCodepointForKeycode(xkb_keycode_t code) const
{
- return xkb_state_key_get_utf32(m_state.get(), code);
+ uint32_t unicode;
+
+ if (SupportsKeyComposition())
+ {
+ const xkb_compose_status composerStatus = xkb_compose_state_get_status(m_composeState.get());
+ if (composerStatus == XKB_COMPOSE_COMPOSED)
+ {
+ const uint32_t keysym = xkb_compose_state_get_one_sym(m_composeState.get());
+ unicode = xkb_keysym_to_utf32(keysym);
+ }
+ else
+ {
+ unicode = xkb_state_key_get_utf32(m_state.get(), code);
+ }
+ }
+ else
+ {
+ unicode = xkb_state_key_get_utf32(m_state.get(), code);
+ }
+
+ // check if it is a dead key and try to translate
+ if (unicode == XBMCK_UNKNOWN)
+ {
+ const uint32_t keysym = xkb_state_key_get_one_sym(m_state.get(), code);
+ const std::optional<XBMCKey> xbmcKey = TranslateDeadKey(keysym);
+ if (xbmcKey)
+ {
+ unicode = xbmcKey.value();
+ }
+ }
+
+ return unicode;
}
bool CXkbcommonKeymap::ShouldKeycodeRepeat(xkb_keycode_t code) const
diff --git a/xbmc/windowing/wayland/XkbcommonKeymap.h b/xbmc/windowing/wayland/XkbcommonKeymap.h
index 7daffc830f..6a48ea402a 100644
--- a/xbmc/windowing/wayland/XkbcommonKeymap.h
+++ b/xbmc/windowing/wayland/XkbcommonKeymap.h
@@ -14,6 +14,7 @@
#include <memory>
#include <vector>
+#include <xkbcommon/xkbcommon-compose.h>
#include <xkbcommon/xkbcommon.h>
#ifdef TARGET_WEBOS
#include <xkbcommon/xkbcommon-webos-keysyms.h>
@@ -41,6 +42,33 @@ namespace WAYLAND
*
* Instances can be easily created from keymap strings with \ref CXkbcommonContext
*/
+
+/**
+ * Abstracts the key composer key state
+ */
+enum class KeyComposerState
+{
+ /*! Key composer is idle */
+ IDLE,
+ /*! Key composer is busy composing a key */
+ COMPOSING,
+ /*! Key composer finished composing a key, it has a valid key in the buffer */
+ FINISHED,
+ /*! Key composer was cancelled */
+ CANCELLED
+};
+
+/**
+ * Container for the key composer status
+ */
+struct KeyComposerStatus
+{
+ /*! Key composer state */
+ KeyComposerState state{KeyComposerState::IDLE};
+ /*! The unicode key symbol that was pressed */
+ std::uint32_t keysym{0};
+};
+
class CXkbcommonKeymap
{
public:
@@ -48,11 +76,17 @@ public:
{
void operator()(xkb_keymap* keymap) const;
};
+ struct XkbComposeTableDeleter
+ {
+ void operator()(xkb_compose_table* composeTable) const;
+ };
/**
* Construct for known xkb_keymap
*/
- explicit CXkbcommonKeymap(std::unique_ptr<xkb_keymap, XkbKeymapDeleter> keymap);
+ explicit CXkbcommonKeymap(
+ std::unique_ptr<xkb_keymap, XkbKeymapDeleter> keymap,
+ std::unique_ptr<xkb_compose_table, XkbComposeTableDeleter> composeTable);
/**
* Get xkb keysym for keycode - only a single keysym is supported
@@ -90,6 +124,21 @@ public:
* Check whether a given keycode should have key repeat
*/
bool ShouldKeycodeRepeat(xkb_keycode_t code) const;
+ /**
+ * Check if the system supports key composition
+ * \return true if composition is supported, false otherwise
+ */
+ bool SupportsKeyComposition() const;
+ /**
+ * Feed a given keycode to the key composer
+ * \param code - the keycode
+ * \return the status of the operation (both the state and the equivalent unicode dead key char resulting from the dead key press)
+ */
+ KeyComposerStatus KeyComposerFeed(xkb_keycode_t code);
+ /**
+ * Reset the composer state
+ */
+ void KeyComposerFlush();
static XBMCKey XBMCKeyForKeysym(xkb_keysym_t sym);
@@ -98,10 +147,17 @@ private:
{
void operator()(xkb_state* state) const;
};
+ struct XkbComposeStateDeleter
+ {
+ void operator()(xkb_compose_state* state) const;
+ };
static std::unique_ptr<xkb_state, XkbStateDeleter> CreateXkbStateFromKeymap(xkb_keymap* keymap);
+ static std::unique_ptr<xkb_compose_state, XkbComposeStateDeleter>
+ CreateXkbComposedStateStateFromTable(xkb_compose_table* composeTable);
std::unique_ptr<xkb_keymap, XkbKeymapDeleter> m_keymap;
std::unique_ptr<xkb_state, XkbStateDeleter> m_state;
+ std::unique_ptr<xkb_compose_state, XkbComposeStateDeleter> m_composeState;
struct ModifierMapping
{
@@ -126,8 +182,8 @@ public:
* This function does not own the file descriptor. It must not be closed
* from this function.
*/
- std::unique_ptr<CXkbcommonKeymap> KeymapFromString(std::string const& keymap);
- std::unique_ptr<CXkbcommonKeymap> KeymapFromNames(const std::string &rules, const std::string &model, const std::string &layout, const std::string &variant, const std::string &options);
+ std::unique_ptr<CXkbcommonKeymap> LocalizedKeymapFromString(const std::string& keymap,
+ const std::string& locale);
private:
struct XkbContextDeleter
diff --git a/xbmc/windowing/win10/WinEventsWin10.cpp b/xbmc/windowing/win10/WinEventsWin10.cpp
index 35da91169b..b534ff5660 100644
--- a/xbmc/windowing/win10/WinEventsWin10.cpp
+++ b/xbmc/windowing/win10/WinEventsWin10.cpp
@@ -276,8 +276,10 @@ void CWinEventsWin10::OnWindowActivationChanged(const CoreWindow& sender, const
}
if (g_application.GetRenderGUI() != active)
DX::Windowing()->NotifyAppActiveChange(g_application.GetRenderGUI());
- CLog::Log(LOGDEBUG, __FUNCTION__ ": window is {}",
- g_application.GetRenderGUI() ? "active" : "inactive");
+
+ if (CServiceBroker::IsLoggingUp())
+ CLog::Log(LOGDEBUG, __FUNCTION__ ": window is {}",
+ g_application.GetRenderGUI() ? "active" : "inactive");
}
void CWinEventsWin10::OnWindowClosed(const CoreWindow& sender, const CoreWindowEventArgs& args)
diff --git a/xbmc/windowing/win10/WinSystemWin10.cpp b/xbmc/windowing/win10/WinSystemWin10.cpp
index 15eda060ba..ccdbc395f3 100644
--- a/xbmc/windowing/win10/WinSystemWin10.cpp
+++ b/xbmc/windowing/win10/WinSystemWin10.cpp
@@ -316,11 +316,15 @@ bool CWinSystemWin10::ChangeResolution(const RESOLUTION_INFO& res, bool forceCha
{
bool changed = false;
auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView();
+ const bool needHDR = DX::DeviceResources::Get()->IsHDROutput();
+
if (hdmiInfo != nullptr)
{
// default mode not in list of supported display modes
- if (res.iScreenWidth == details->ScreenWidth && res.iScreenHeight == details->ScreenHeight
- && fabs(res.fRefreshRate - details->RefreshRate) <= 0.00001)
+ // TO DO: is still necessary? (or now all modes are listed?)
+ if (!needHDR && res.iScreenWidth == details->ScreenWidth &&
+ res.iScreenHeight == details->ScreenHeight &&
+ fabs(res.fRefreshRate - details->RefreshRate) <= 0.00001)
{
Wait(hdmiInfo.SetDefaultDisplayModeAsync());
changed = true;
@@ -330,11 +334,17 @@ bool CWinSystemWin10::ChangeResolution(const RESOLUTION_INFO& res, bool forceCha
bool needStereo = CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED;
auto hdmiModes = hdmiInfo.GetSupportedDisplayModes();
+ // For backward compatibility (also old Xbox models) only match color space for HDR modes
+ // and keep SDR modes selection as it is (any color space). Assumes SDR modes listed first.
+ // TO DO: for HDR modes make use of IsSmpte2084Supported() but has issues with current code.
+ // TO DO: for SDR implement preference for BT.709 color space but also fallback to sRGB.
HdmiDisplayMode selected = nullptr;
for (const auto& mode : hdmiModes)
{
- if (res.iScreenWidth == mode.ResolutionWidthInRawPixels() && res.iScreenHeight == mode.ResolutionHeightInRawPixels()
- && fabs(res.fRefreshRate - mode.RefreshRate()) <= 0.00001)
+ if ((!needHDR || (needHDR && mode.ColorSpace() == HdmiDisplayColorSpace::BT2020)) &&
+ res.iScreenWidth == mode.ResolutionWidthInRawPixels() &&
+ res.iScreenHeight == mode.ResolutionHeightInRawPixels() &&
+ fabs(res.fRefreshRate - mode.RefreshRate()) <= 0.00001)
{
selected = mode;
if (needStereo == mode.StereoEnabled())
@@ -344,7 +354,8 @@ bool CWinSystemWin10::ChangeResolution(const RESOLUTION_INFO& res, bool forceCha
if (selected != nullptr)
{
- changed = Wait(hdmiInfo.RequestSetCurrentDisplayModeAsync(selected));
+ changed = Wait(hdmiInfo.RequestSetCurrentDisplayModeAsync(
+ selected, needHDR ? HdmiDisplayHdrOption::Eotf2084 : HdmiDisplayHdrOption::None));
}
}
}
diff --git a/xbmc/windowing/win10/WinSystemWin10DX.cpp b/xbmc/windowing/win10/WinSystemWin10DX.cpp
index c3d2fca026..1101b1a7ef 100644
--- a/xbmc/windowing/win10/WinSystemWin10DX.cpp
+++ b/xbmc/windowing/win10/WinSystemWin10DX.cpp
@@ -166,7 +166,7 @@ void CWinSystemWin10DX::InitHooks(IDXGIOutput* pOutput)
bool CWinSystemWin10DX::IsHDRDisplay()
{
- return false; // use tone mapping by default on Xbox
+ return (CWIN32Util::GetWindowsHDRStatus() != HDR_STATUS::HDR_UNSUPPORTED);
}
HDR_STATUS CWinSystemWin10DX::GetOSHDRStatus()
diff --git a/xbmc/windowing/windows/VideoSyncD3D.cpp b/xbmc/windowing/windows/VideoSyncD3D.cpp
index 564df55fdd..084c34e0e0 100644
--- a/xbmc/windowing/windows/VideoSyncD3D.cpp
+++ b/xbmc/windowing/windows/VideoSyncD3D.cpp
@@ -20,6 +20,10 @@
#include <mutex>
+#ifdef TARGET_WINDOWS_STORE
+#include <winrt/Windows.Graphics.Display.Core.h>
+#endif
+
using namespace std::chrono_literals;
void CVideoSyncD3D::OnLostDisplay()
@@ -54,6 +58,11 @@ bool CVideoSyncD3D::Setup()
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
CLog::Log(LOGDEBUG, "CVideoSyncD3D: SetThreadPriority failed");
+ CreateDXGIFactory1(IID_PPV_ARGS(m_factory.ReleaseAndGetAddressOf()));
+
+ Microsoft::WRL::ComPtr<IDXGIOutput> pOutput;
+ DX::DeviceResources::Get()->GetCachedOutputAndDesc(&pOutput, &m_outputDesc);
+
return true;
}
@@ -63,18 +72,44 @@ void CVideoSyncD3D::Run(CEvent& stopEvent)
int64_t LastVBlankTime;
int NrVBlanks;
double VBlankTime;
- int64_t systemFrequency = CurrentHostFrequency();
+ const int64_t systemFrequency = CurrentHostFrequency();
+ bool validVBlank{true};
// init the vblanktime
Now = CurrentHostCounter();
LastVBlankTime = Now;
- m_lastUpdateTime = Now - systemFrequency;
+
while (!stopEvent.Signaled() && !m_displayLost && !m_displayReset)
{
// sleep until vblank
Microsoft::WRL::ComPtr<IDXGIOutput> pOutput;
- DX::DeviceResources::Get()->GetOutput(&pOutput);
- pOutput->WaitForVBlank();
+ DX::DeviceResources::Get()->GetCachedOutputAndDesc(pOutput.ReleaseAndGetAddressOf(),
+ &m_outputDesc);
+
+ const int64_t WaitForVBlankStartTime = CurrentHostCounter();
+ const HRESULT hr = pOutput ? pOutput->WaitForVBlank() : E_INVALIDARG;
+ const int64_t WaitForVBlankElapsedTime = CurrentHostCounter() - WaitForVBlankStartTime;
+
+ // WaitForVBlank() can return very quickly due to errors or screen sleeping
+ if (!SUCCEEDED(hr) || WaitForVBlankElapsedTime - (systemFrequency / 1000) <= 0)
+ {
+ if (SUCCEEDED(hr) && validVBlank)
+ CLog::LogF(LOGWARNING, "failed to detect vblank - screen asleep?");
+
+ if (!SUCCEEDED(hr))
+ CLog::LogF(LOGERROR, "error waiting for vblank, {}", DX::GetErrorDescription(hr));
+
+ validVBlank = false;
+
+ // Wait a while, until vblank may have come back. No need for accurate sleep.
+ ::Sleep(250);
+ continue;
+ }
+ else if (!validVBlank)
+ {
+ CLog::LogF(LOGWARNING, "vblank detected - resuming reference clock updates");
+ validVBlank = true;
+ }
// calculate how many vblanks happened
Now = CurrentHostCounter();
@@ -87,20 +122,14 @@ void CVideoSyncD3D::Run(CEvent& stopEvent)
// save the timestamp of this vblank so we can calculate how many vblanks happened next time
LastVBlankTime = Now;
- if ((Now - m_lastUpdateTime) >= systemFrequency)
+ if (!m_factory->IsCurrent())
{
+ CreateDXGIFactory1(IID_PPV_ARGS(m_factory.ReleaseAndGetAddressOf()));
+
float fps = m_fps;
if (fps != GetFps())
break;
}
-
- // because we had a vblank, sleep until half the refreshrate period because i think WaitForVBlank block any rendering stuf
- // without sleeping we have freeze rendering
- int SleepTime = (int)((LastVBlankTime + (systemFrequency / MathUtils::round_int(m_fps) / 2) - Now) * 1000 / systemFrequency);
- if (SleepTime > 50)
- SleepTime = 50; //failsafe
- if (SleepTime > 0)
- ::Sleep(SleepTime);
}
m_lostEvent.Set();
@@ -120,14 +149,33 @@ void CVideoSyncD3D::Cleanup()
float CVideoSyncD3D::GetFps()
{
- DXGI_MODE_DESC DisplayMode = {};
- DX::DeviceResources::Get()->GetDisplayMode(&DisplayMode);
+#ifdef TARGET_WINDOWS_DESKTOP
+ DEVMODEW sDevMode = {};
+ sDevMode.dmSize = sizeof(sDevMode);
- m_fps = (DisplayMode.RefreshRate.Denominator != 0) ? (float)DisplayMode.RefreshRate.Numerator / (float)DisplayMode.RefreshRate.Denominator : 0.0f;
+ if (EnumDisplaySettingsW(m_outputDesc.DeviceName, ENUM_CURRENT_SETTINGS, &sDevMode))
+ {
+ if ((sDevMode.dmDisplayFrequency + 1) % 24 == 0 || (sDevMode.dmDisplayFrequency + 1) % 30 == 0)
+ m_fps = static_cast<float>(sDevMode.dmDisplayFrequency + 1) / 1.001f;
+ else
+ m_fps = static_cast<float>(sDevMode.dmDisplayFrequency);
+
+ if (sDevMode.dmDisplayFlags & DM_INTERLACED)
+ m_fps *= 2;
+ }
+#else
+ using namespace winrt::Windows::Graphics::Display::Core;
+
+ auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView();
+ if (hdmiInfo) // Xbox only
+ {
+ auto currentMode = hdmiInfo.GetCurrentDisplayMode();
+ m_fps = static_cast<float>(currentMode.RefreshRate());
+ }
+#endif
if (m_fps == 0.0)
m_fps = 60.0f;
return m_fps;
}
-
diff --git a/xbmc/windowing/windows/VideoSyncD3D.h b/xbmc/windowing/windows/VideoSyncD3D.h
index 8c706dd88d..01664c08d4 100644
--- a/xbmc/windowing/windows/VideoSyncD3D.h
+++ b/xbmc/windowing/windows/VideoSyncD3D.h
@@ -12,11 +12,13 @@
#include "threads/Event.h"
#include "windowing/VideoSync.h"
+#include <dxgi1_5.h>
+
class CVideoSyncD3D : public CVideoSync, IDispResource
{
public:
CVideoSyncD3D(CVideoReferenceClock* clock)
- : CVideoSync(clock), m_displayLost(false), m_displayReset(false), m_lastUpdateTime(0)
+ : CVideoSync(clock), m_displayLost(false), m_displayReset(false)
{
}
bool Setup() override;
@@ -32,6 +34,7 @@ private:
volatile bool m_displayLost;
volatile bool m_displayReset;
CEvent m_lostEvent;
- int64_t m_lastUpdateTime;
+ DXGI_OUTPUT_DESC m_outputDesc{};
+ Microsoft::WRL::ComPtr<IDXGIFactory2> m_factory;
};
diff --git a/xbmc/windowing/windows/WinEventsWin32.cpp b/xbmc/windowing/windows/WinEventsWin32.cpp
index 8d1e38e8eb..b4c5b350bf 100644
--- a/xbmc/windowing/windows/WinEventsWin32.cpp
+++ b/xbmc/windowing/windows/WinEventsWin32.cpp
@@ -72,8 +72,6 @@ int g_sizeMoveHight = 0;
int g_sizeMoveX = -10000;
int g_sizeMoveY = -10000;
-int XBMC_TranslateUNICODE = 1;
-
int CWinEventsWin32::m_originalZoomDistance = 0;
Pointer CWinEventsWin32::m_touchPointer;
CGenericTouchSwipeDetector* CWinEventsWin32::m_touchSwipeDetector = nullptr;
@@ -167,9 +165,13 @@ static XBMC_keysym *TranslateKey(WPARAM vkey, UINT scancode, XBMC_keysym *keysym
}
// Attempt to convert the keypress to a UNICODE character
- GetKeyboardState(keystate);
+ if (GetKeyboardState(keystate) == FALSE)
+ {
+ CLog::LogF(LOGERROR, "GetKeyboardState error {}", GetLastError());
+ return keysym;
+ }
- if (pressed && XBMC_TranslateUNICODE)
+ if (pressed)
{
std::array<uint16_t, 2> wchars;
@@ -180,7 +182,8 @@ static XBMC_keysym *TranslateKey(WPARAM vkey, UINT scancode, XBMC_keysym *keysym
keysym->unicode = static_cast<uint16_t>(vkey - VK_NUMPAD0 + '0');
}
else if (ToUnicode(static_cast<UINT>(vkey), scancode, keystate,
- reinterpret_cast<LPWSTR>(wchars.data()), wchars.size(), 0) > 0)
+ reinterpret_cast<LPWSTR>(wchars.data()), static_cast<int>(wchars.size()),
+ 0) > 0)
{
keysym->unicode = wchars[0];
}
@@ -363,7 +366,7 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
return 0;
default:;
}
- //deliberate fallthrough
+ [[fallthrough]];
case WM_KEYDOWN:
{
switch (wParam)
@@ -808,6 +811,7 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
else
CWin32StorageProvider::SetEvent();
}
+ break;
default:;
}
break;
@@ -867,7 +871,8 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
}
break;
}
- default:;
+ default:
+ break;
}
return(DefWindowProc(hWnd, uMsg, wParam, lParam));
}
diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp
index 22e614b790..b70a7b59eb 100644
--- a/xbmc/windows/GUIMediaWindow.cpp
+++ b/xbmc/windows/GUIMediaWindow.cpp
@@ -1818,34 +1818,6 @@ bool CGUIMediaWindow::OnPopupMenu(int itemIdx)
return CONTEXTMENU::LoopFrom(*addonMenu[idx - addonMenuRange.first], item);
}
-void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
-{
- CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
-
- if (!item || item->IsParentFolder())
- return;
-
- if (item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
- buttons.Add(CONTEXT_BUTTON_BROWSE_INTO, 37015);
-
-}
-
-bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
-{
- switch (button)
- {
- case CONTEXT_BUTTON_BROWSE_INTO:
- {
- CFileItemPtr item = m_vecItems->Get(itemNumber);
- Update(item->GetPath());
- return true;
- }
- default:
- break;
- }
- return false;
-}
-
const CGUIViewState *CGUIMediaWindow::GetViewState() const
{
return m_guiState.get();
diff --git a/xbmc/windows/GUIMediaWindow.h b/xbmc/windows/GUIMediaWindow.h
index 1fc1dfc165..6de5e2b320 100644
--- a/xbmc/windows/GUIMediaWindow.h
+++ b/xbmc/windows/GUIMediaWindow.h
@@ -75,8 +75,8 @@ protected:
virtual bool OnSelect(int item);
virtual bool OnPopupMenu(int iItem);
- virtual void GetContextButtons(int itemNumber, CContextButtons &buttons);
- virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
+ virtual void GetContextButtons(int itemNumber, CContextButtons& buttons) {}
+ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button) { return false; }
virtual bool OnAddMediaSource() { return false; }
virtual void FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks);
diff --git a/xbmc/windows/GUIWindowSplash.cpp b/xbmc/windows/GUIWindowSplash.cpp
index dadd61eab9..19ba189811 100644
--- a/xbmc/windows/GUIWindowSplash.cpp
+++ b/xbmc/windows/GUIWindowSplash.cpp
@@ -14,6 +14,8 @@
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
+#include <memory>
+
CGUIWindowSplash::CGUIWindowSplash(void) : CGUIWindow(WINDOW_SPLASH, ""), m_image(nullptr)
{
m_loadType = LOAD_ON_GUI_INIT;
@@ -26,7 +28,11 @@ void CGUIWindowSplash::OnInitWindow()
if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_splashImage)
return;
- m_image = std::unique_ptr<CGUIImage>(new CGUIImage(0, 0, 0, 0, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(), CTextureInfo(CUtil::GetSplashPath())));
+ m_image = std::make_unique<CGUIImage>(
+ 0, 0, .0f, .0f,
+ static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
+ static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()),
+ CTextureInfo(CUtil::GetSplashPath()));
m_image->SetAspectRatio(CAspectRatio::AR_SCALE);
}