diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9de063241..008ef44e5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,7 +31,8 @@ set(IPTV_SOURCES src/client.cpp
src/iptvsimple/data/EpgEntry.cpp
src/iptvsimple/data/EpgGenre.cpp
src/iptvsimple/utilities/FileUtils.cpp
- src/iptvsimple/utilities/Logger.cpp)
+ src/iptvsimple/utilities/Logger.cpp
+ src/iptvsimple/utilities/WebUtils.cpp)
set(IPTV_HEADERS src/client.h
src/PVRIptvData.h
@@ -47,6 +48,7 @@ set(IPTV_HEADERS src/client.h
src/iptvsimple/data/EpgGenre.h
src/iptvsimple/utilities/FileUtils.h
src/iptvsimple/utilities/Logger.h
+ src/iptvsimple/utilities/WebUtils.h
src/iptvsimple/utilities/XMLUtils.h)
addon_version(pvr.iptvsimple IPTV)
diff --git a/README.md b/README.md
index c99e04f12..75d62b2e8 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,8 @@
IPTV Live TV and Radio PVR client addon for [Kodi](https://kodi.tv)
+For a listing of the supported M3U and XMLTV elements see the appendix [here](#supported-m3u-and-xmltv-elements)
+
## Build instructions
### Linux
@@ -62,19 +64,34 @@ General settings required for the addon to function.
* **M3U play list URL**: If location is `Remote path` this setting must contain a valid URL for the addon to function.
* **Cache M3U at local storage**: If location is `Remote path` select whether or not the the M3U file should be cached locally.
* **Start channel number**: The number to start numbering channels from.
+* **Only number by channel order in M3U**: Ignore any 'tvg-chno' tags and only number channels by the order in the M3U starting at 'Start channel number'.
-### EPG Settings
+### EPG
Settings related to the EPG.
+For settings related to genres please see the next section.
+
* **Location**: Select where to find the XMLTV resource. The options are:
- `Local path` - A path to an XMLTV file whether it be on the device or the local network.
- `Remote path` - A URL specifying the location of the XMLTV file.
* **XMLTV path**: If location is `Local Path` this setting should contain a valid path.
* **XMLTV URL**: If location is `Remote Path` this setting should contain a valid URL.
* **Cache XMLTV at local storage**: If location is `Remote path` select whether or not the the XMLTV file should be cached locally.
-* **EPG time shift**: Adjust the EPG times by this value in minutes, range is from -720 mins to +720 mins (+/- 12 hours).
+* **EPG time shift**: Adjust the EPG times by this value in minutes, range is from -720 mins to +840 mins (- 12 hours to +14 hours).
* **Apply time shift to all channels**: Whether or not to override the time shift for all channels with `EPG time shift`. If not enabled `EPG time shift` plus the individual time shift per channel (if available) will be used.
+### Genres
+Settings related to genres.
+
+The addon will read all the `` elements of a `programme` and use this as the genre string. It is also possible to supply a mapping file to convert the genre string to a genre ID, allowing colour coding of the EPG. When using a mapping file each category will be checked in order until a match is found. Please see: [Using a mapping file for Genres](#using-a-mapping-file-for-genres) in the Appendix for details on how to set this up.
+
+* **Use genre text from XMLTV when mapping genres**: If enabled, and a genre mapping file is used to get a genre type and sub type use the EPG's genre text (i.e. 'category' text) for the genre instead of the kodi default text. Only the genre type (and not the sub type) will be used if a mapping is found.
+* **Location**: Select where to find the genres XML resource. The options are:
+ - `Local path` - A path to a gernes XML file whether it be on the device or the local network.
+ - `Remote path` - A URL specifying the location of the genres XML file.
+* **Genres path**: If location is `Local Path` this setting should contain a valid path.
+* **Genres URL**: If location is `Remote Path` this setting should contain a valid URL.
+
### Channel Logos
Settings realted to Channel Logos.
@@ -90,6 +107,190 @@ Settings realted to Channel Logos.
## Appendix
+The various config files have examples allowing users to create their own, making it possible to support custom config, currently regarding genres. The best way to learn about them is to read the config files themselves. Each contains details of how the config file works.
+
+All of the files listed below are overwritten each time the addon starts (excluding genres.xml). Therefore if you are customising files please create new copies with different file names. Note: that only the files below are overwritten any new files you create will not be touched.
+
+After adding and selecting new config files you will need to clear the EPG cache `Settings->PVR & Live TV->Guide->Clear cache` for it to take effect in the case of EPG relatd config and for channel related config will need to clear the full cache `Settings->PVR & Live TV->General->Clear cache`.
+
+If you would like to support other formats/languages please raise an issue at the github project https://github.com/kodi-pvr/pvr.iptvsimple, where you can either create a PR or request your new configs be shipped with the addon.
+
+There is one config file located here: `userdata/addon_data/pvr.iptvsimple/genres/kodiDvbGenres.xml`. This simply contains the DVB genre IDs that Kodi supports and uses hex for the IDs. Can be a useful reference if creating your own configs. There is also `userdata/addon_data/pvr.iptvsimple/genres/kodiDvbGenresTypeSubtype.xml`, which uses two decimal values instead of hex. This file is also overwritten each time the addon restarts.
+
+### Using a mapping file for Genres
+
+Users can create there own genre mapping files to map their genre strings to genre IDs. This allows the EPG UI to be colour coded per genre.
+
+Kodi uses the following standard for it's genre IDs: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.11.01_60/en_300468v011101p.pdf
+
+By default the addon will try to load a file called `genres.xml` and expect it to be here: `userdata/addon_data/pvr.iptvsimple/genres/genreTextMappings/`. However any genres file can be chosen in the addon settings.
+
+The following files are currently available with the addon (this file uses hexadeciaml genreId's):
+ - `Rytec-UK-Ireland.xml`
+
+The file can specify either a hexadecimal `genreId` attribute (recommended) or separate integer values for `type` and `subType`. Mathematically `genreId` is equals to the logical OR or `type` and `subType`, i.e. `genreId = type | subType`.
+
+Note: Once mapped to genre IDs the text displayed can either be the DVB standard text or the genre string text supplied in the XML. If using the text supplied in the XML only the genre type will be passed and each value will correspond to a category and colour (depedning on skin) on the UI. Here are the categories (all examples have 0 for the sub type). It's imortant you map correctly as genres can be used for search.
+
+```
+- 0x10: General Movie / Drama
+- 0x20: News / Current Affairs
+- 0x30: Show / Game Show
+- 0x40: Sports
+- 0x50: Children's / Youth Programmes
+- 0x60: Music / Ballet / Dance
+- 0x70: Arts / Culture
+- 0x80: Social / Political / Economics
+- 0x90: Education / Science / Factual
+- 0xA0: Leisure / Hobbies
+- 0xB0: Special Characteristics
+```
+
+- ``: There should be a single `` element. The value should denote the purpose of this genre mapping file.
+- The value of the `` element is what is used to map from in order to get the genre IDs. Many mapping values are allowed to map to the same IDs.
+
+**Example using hexadecimal `genreId` attributes (recommended)**:
+
+```
+
+ My Streams Genres Mappings
+ Movie
+ Movie - Comedy
+ Movie - Romance
+ TV Show
+ Game Show
+ Talk Show
+ Leisure
+
+```
+
+- The `genreId` attribute is a single hex value ranging from 0x10 to 0xFF.
+
+**Example using integer `type` and `subtype` attributes**:
+
+```
+
+ My Streams Genres Mappings
+ Movie
+ Movie - Comedy
+ Movie - Romance
+ TV Show
+ Game Show
+ Talk Show
+ Leisure
+
+```
+
+- The `type` attribute can contain a values ranging from 16 to 240 in multiples of 16 (would be 0x10 to 0xF0 if in hex) and the `subtype` attributes can contain a value from 0 to 15 (would be 0x00 to 0x0F if in hex). `subtype` is optional.
+
+
+### Supported M3U and XMLTV elements
+
+#### M3U format elemnents:
+
+```
+#EXTM3U tvg-shift="-4.5"
+#EXTINF:0 tvg-id="channel-x" tvg-name="Channel_X" group-title="Entertainment" tvg-chno="10" tvg-logo="http://path-to-icons/channel-x.png" radio="true" tvg-shift="-3.5",Channel X
+#EXTVLCOPT:program=745
+#KODIPROP:key=val
+http://path-to-stream/live/channel-x.ts
+#EXTINF:0 tvg-id="channel-x" tvg-name="Channel-X-HD" group-title="Entertainment;HD Channels",Channel X HD
+http://path-to-stream/live/channel-x-hd.ts
+#EXTINF:0 tvg-id="channel-y" tvg-name="Channel_Y",Channel Y
+#EXTGRP:Entertainment
+http://path-to-stream/live/channel-y.ts
+#EXTINF:0,Channel Z
+http://path-to-stream/live/channel-z.ts
+```
+
+Note: The minimum required for a channel/stream is an `#EXTINF` line with a channel name and the `URL` line. E.g. a minimal version of the exmaple file above would be:
+
+```
+#EXTM3U
+#EXTINF:0,Channel X
+http://path-to-stream/live/channel-x.ts
+#EXTINF:0,Channel X HD
+http://path-to-stream/live/channel-x-hd.ts
+#EXTINF:0,Channel Y
+http://path-to-stream/live/channel-y.ts
+#EXTINF:0,Channel Z
+http://path-to-stream/live/channel-z.ts
+```
+
+- `#EXTM3U`: Marker for the start of an M3U file. Has an optional `tvg-shift` value that will be used for all channels if a `tvg-shift` value is not supplied per channel.
+- `#EXTINF`: Contains a set of values, ending with a comma followed by the `channel name`.
+ - `tvg-id`: A unique identifier for this channel used to map to the EPG XMLTV data.
+ - `tvg-name`: A name for this channel in the EPG XMLTV data.
+ - `group-title`: A semi-colon separted list of channel groups that this channel belongs to.
+ - `tvg-chno`: The number to be used for this channel.
+ - `tvg-logo`: A URL pointing to the logo for this channel.
+ - `radio`: If the value matches "true" (case insensitive) this is a radio channel.
+ - `tvg-shift`: Channel specific shift value in hours.
+- `#EXTGRP`: A semi-colon separted list of channel groups that this channel belongs to.
+- `#KODIPROP`: A single property in the format `key=value` that can be passed to Kodi. Multiple can be passed.
+- `#EXTVLCOPT`: A single property in the format `key=value` that can be passed to Kodi. Multiple can be passed.
+- `#EXT-X-PLAYLIST-TYPE`: If this element is present with a value of `VOD` (Video on Demand) the stream is marked as not being live.
+- `URL`: The final line in each channel stanza is the URL used for the stream. Appending `|User-Agent=` will change the user agent.
+
+When processing an XMLTV file the addon will attempt to find a channel loaded from the M3U that matches the EPG channel. It will cycle through the full set of M3U channels checking for one condition on each pass. The first channel found to match is the channel chosen for this EPG channel data.
+
+ - *1st pass*: Does the`id` attribute of the `` element from the XMLTV match the `tvg-id` from the M3U channel. If yes we have a match, don't continue.
+ - *Before the second pass*: Was a value provided, if not skip this channels EPG data.
+ - *2nd pass*: Does the as it is or with spaces replaced with '_''s match `tvg-name` from the M3U channel. If yes we have a match, don't continue.
+ - *3rd pass*: Does the match the M3U `channel name`. If yes we have a match, phew, eventually found a match.
+
+#### XMLTV format elemnents:
+
+General information on the XMLTV format can be found [here](http://wiki.xmltv.org/index.php/XMLTVFormat). There is also the [DTD](https://github.com/XMLTV/xmltv/blob/master/xmltv.dtd).
+
+**Channel elements**
+```
+
+ Channel X
+ Channel X HD
+
+
+```
+
+- When matching against M3U channels the `id` attribute will be used first, followed by each `display-name`.
+- If multiple `icon` elements are provided only the first will be used.
+
+**Programme elements**
+```
+
+ My Show
+ Description of My Show
+ Drama
+ Mystery
+ Episode name for My Show
+ 20080711
+
+ 6/10
+
+ 0.1.0/1
+ S01E02
+
+ Director One
+ Writer One
+ Actor One
+
+
+
+```
+The `programme` element supports the attributes `start`/`stop` in the format `YYYmmddHHMMSS +/-HHMM` and the attribute `channel` which needs to match the `channel` element's attribute `id`.
+
+- `title`: The title of the prgramme.
+- `desc`: A descption of the programme.
+- `category`: If multiple elements are provided only the first will be used to populate the genre.
+- `sub-title`: Used to populate episode name.
+- `date`: Used to populate year and first aired date.
+- `star-rating`: If multiple elements are provided only the first will be used. The value will be converted to a scale of 10 if required.
+- `episode-num`: The`xmltv_ns`system will be preferred over `onscreen` and the first successfully parsed element will be used.
+ - For `episode-num` elements using the `xmltv_ns` system at least season and episode must be supplied, i.e. `0.1` (season 1, episode 2). If the 3rd element episode part number is supplied it must contain both the part number and the total number of parts, i.e. `0.1.0/2` (season 1, episode 2, part 1 of 2).
+ - For `episode-num` elements using the `onscreen` system only the `S01E02` format is supported.
+- `credits`: Only director, writer and actor are supported (multiple of each can be supplied).
+- `icon`: If multiple elements are provided only the first will be used.
+
### Manual Steps to rebuild the addon on MacOSX
The following steps can be followed manually instead of using the `build-install-mac.sh` in the root of the addon repo after the [initial addon build](#build-tools-and-initial-addon-build) has been completed.
@@ -97,7 +298,7 @@ The following steps can be followed manually instead of using the `build-install
**To rebuild the addon after changes**
1. `rm tools/depends/target/binary-addons/.installed-macosx*`
-2. `make -j$(getconf _NPROCESSORS_ONLN) -C tools/depends/target/binary-addons ADDONS="pvr.vuplus" ADDON_SRC_PREFIX=$HOME`
+2. `make -j$(getconf _NPROCESSORS_ONLN) -C tools/depends/target/binary-addons ADDONS="pvr.iptvsimple" ADDON_SRC_PREFIX=$HOME`
or
@@ -106,5 +307,5 @@ or
**Copy the addon to the Kodi addon directory on Mac**
-1. `rm -rf "$HOME/Library/Application Support/Kodi/addons/pvr.vuplus"`
-2. `cp -rf $HOME/xbmc-addon/addons/pvr.vuplus "$HOME/Library/Application Support/Kodi/addons"`
\ No newline at end of file
+1. `rm -rf "$HOME/Library/Application Support/Kodi/addons/pvr.iptvsimple"`
+2. `cp -rf $HOME/xbmc-addon/addons/pvr.iptvsimple "$HOME/Library/Application Support/Kodi/addons"`
\ No newline at end of file
diff --git a/build-install-mac.sh b/build-install-mac.sh
index 079aa89bd..6cc85127f 100755
--- a/build-install-mac.sh
+++ b/build-install-mac.sh
@@ -54,4 +54,6 @@ make
XBMC_BUILD_ADDON_INSTALL_DIR=$(cd "$SCRIPT_DIR$1/addons/$ADDON_NAME" 2> /dev/null && pwd -P)
rm -rf "$KODI_ADDONS_DIR/$ADDON_NAME"
+echo "Removed previous addon build from: $KODI_ADDONS_DIR"
cp -rf "$XBMC_BUILD_ADDON_INSTALL_DIR" "$KODI_ADDONS_DIR"
+echo "Copied new addon build to: $KODI_ADDONS_DIR"
diff --git a/pvr.iptvsimple/addon.xml.in b/pvr.iptvsimple/addon.xml.in
index fa01874e9..d9113c730 100644
--- a/pvr.iptvsimple/addon.xml.in
+++ b/pvr.iptvsimple/addon.xml.in
@@ -1,7 +1,7 @@
@ADDON_DEPENDS@
@@ -19,7 +19,7 @@
Kodi PVR Addon für IPTV Unterstützung. https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-HomeΠρόσθετο του Kodi για PVR, με υποστήριξη IPTV. Για λεπτομέρειες επισκεφθείτε το: https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-HomeKodi PVR addon for IPTV support. https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-Home
- Kodi PVR addon for IPTV support. https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-Home
+ Kodi PVR addon for IPTV support.Kodi PVR addon for IPTV support. https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-HomeKodi PVR addon for IPTV support. https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-HomeAddon Kodi PVR para soporte IPTV. https://github.com/afedchin/Kodi-addon-iptvsimple/wiki/IPTV-Simple-Home
@@ -69,7 +69,7 @@
IPTV Simple PVR Client unterstützt m3u Wiedergabelisten, Streaming von Live TV für Multicast/Unicast Quellen, Radiosender und EPG.Απλός Πελάτης PVR του IPTV, με υποστήριξη λιστών αναπαραγωγής m3u, αναπαραγωγή ροών Live TV για πηγές πολλαπλής/μοναδικής διανομής, ακρόαση ραδιοφωνικών καναλιών και EPG.IPTV Simple PVR Client support m3u playlists, streaming of Live TV for multicast/unicast sources, listening to Radio channels and EPG.
- IPTV Simple PVR Client support m3u playlists, streaming of Live TV for multicast/unicast sources, listening to Radio channels and EPG.
+ IPTV Simple PVR Client support m3u playlists, streaming of Live TV for multicast/unicast sources, listening to Radio channels and EPG.
For documentation visit: https://github.com/kodi-pvr/pvr.iptvsimple/blob/master/README.md
IPTV Simple PVR Client support m3u playlists, streaming of Live TV for multicast/unicast sources, listening to Radio channels and EPG.IPTV Simple PVR Client support m3u playlists, streaming of Live TV for multicast/unicast sources, listening to Radio channels and EPG.Cliente simple PVR IPTV. Reproduce de TV en Vivo multicast/unicast y listas m3u8
. reproduce tambien canales de radio y GEP
@@ -158,5 +158,58 @@
这是不稳定版的软件!作者不对录像失败、错误定时造成时间浪费或其它不良影响负责。這是測試中的軟體!原創作者無法針對以下情況負責:包括播放失敗,不正確的電子節目表,多餘的時數,或任何不可預期的不良影響。@PLATFORM@
+
+v4.5.0
+- Fixed: Support full timeshift range of -12 to +14 hours
+- Fixed: Some providers incorrectly use tvg-ID instead of tvg-id
+- Fixed: Support multiple display-names and case insensitive tvg-id is always first, next tvg-name and then channel name find order
+- Added: support episode-num for both xmltv_ns and onscreen systems in epg entry
+- Added: Update readme for supported M3U and XMLTV formats and genres
+- Added: support star rating in epg entry
+- Added: support firstAired and year in epg entry
+- Added: Update OSX build script
+- Added: support multiple actor/director/writers elements in epg entry
+- Added: URLEncode and append .png ext for remote logos built from channel name
+- Added: Support for mapping by genre hex ID and added example files and settings
+- Added: Timing for Playlist and EPG Load
+- Added: Option to number channels by M3U order only
+- Update: Debug logging
+- Added: Channel group member order set to M3U order
+- Fixed: Fix segfault for compressed EPG files
+- Added: Add ordering for groups as per PVR API 6.1.0
+
+v4.4.0
+- Update: Recompile for 6.1.0 PVR Addon API compatibility
+
+v4.3.0
+- Added: Auto reload channels, groups and EPG on settings change
+- Added: Support for #EXTGRP tag in M3U file
+- Fixed: Channel with no groups inherit previous channels groups
+- Added: update new file kodi headers to start with kodi/
+
+v4.2.2
+- Update build system version
+- Change header include way
+- Add AppVeyor for Windows related build tests
+
+v4.2.1
+- Fix nullptr initialisation
+
+v4.2.0
+- Add support for sub-title/actor/director/writer in XML
+
+v4.1.0
+- Support EXTVCOPT in m3u8
+- Build helper script for OSX
+
+v4.0.2
+- Fix wrong EPG times due to DST on Windows
+
+v4.0.1
+- Remove channels loaded notification
+
+v4.0.0
+- Update to PVR addon API v6.0.0
+
diff --git a/pvr.iptvsimple/changelog.txt b/pvr.iptvsimple/changelog.txt
index a34f384d5..f5d9d8e4c 100644
--- a/pvr.iptvsimple/changelog.txt
+++ b/pvr.iptvsimple/changelog.txt
@@ -1,3 +1,22 @@
+v4.5.0
+- Fixed: Support full timeshift range of -12 to +14 hours
+- Fixed: Some providers incorrectly use tvg-ID instead of tvg-id
+- Fixed: Support multiple display-names and case insensitive tvg-id is always first, next tvg-name and then channel name find order
+- Added: support episode-num for both xmltv_ns and onscreen systems in epg entry
+- Added: Update readme for supported M3U and XMLTV formats and genres
+- Added: support star rating in epg entry
+- Added: support firstAired and year in epg entry
+- Added: Update OSX build script
+- Added: support multiple actor/director/writers elements in epg entry
+- Added: URLEncode and append .png ext for remote logos built from channel name
+- Added: Support for mapping by genre hex ID and added example files and settings
+- Added: Timing for Playlist and EPG Load
+- Added: Option to number channels by M3U order only
+- Update: Debug logging
+- Added: Channel group member order set to M3U order
+- Fixed: Fix segfault for compressed EPG files
+- Added: Add ordering for groups as per PVR API 6.1.0
+
v4.4.0
- Update: Recompile for 6.1.0 PVR Addon API compatibility
diff --git a/pvr.iptvsimple/resources/data/genres.xml b/pvr.iptvsimple/resources/data/genres.xml
new file mode 100755
index 000000000..869eac43d
--- /dev/null
+++ b/pvr.iptvsimple/resources/data/genres.xml
@@ -0,0 +1,9 @@
+
+
+
+ Placeholder Genres File
+
+
+
diff --git a/pvr.iptvsimple/resources/data/genres/genreTextMappings/Rytec-UK-Ireland.xml b/pvr.iptvsimple/resources/data/genres/genreTextMappings/Rytec-UK-Ireland.xml
new file mode 100755
index 000000000..d918df7b2
--- /dev/null
+++ b/pvr.iptvsimple/resources/data/genres/genreTextMappings/Rytec-UK-Ireland.xml
@@ -0,0 +1,134 @@
+
+
+
+ Rytec UK/Ireland
+
+ General Movie/Drama
+ Film
+ Animated Movie/Drama
+ Thriller
+ Detective/Thriller
+ Action
+ Adventure
+ Adventure/War
+ Western
+ Gangster
+ Fantasy
+ Science Fiction
+ Family
+ Sitcom
+ Comedy
+ TV Drama. Comedy
+ Drama
+ Soap/Melodrama/Folkloric
+ TV Drama
+ TV Drama. Melodrama
+ TV Drama. Factual
+ TV Drama. Crime
+ TV Drama. Period
+ Medical Drama
+ Romance
+ Crime drama
+ Historical/Period Drama
+ Police/Crime Drama
+
+
+ News
+ General News/Current Affairs
+ Documentary
+ Documentary. News
+ Discussion. News
+
+
+ Series
+ Show
+ Vets/Pets
+ Wildlife
+ Property
+ General Show/Game Show
+ Game Show
+ Challenge/Reality Show
+ Show. Variety Show
+ Variety Show
+ Entertainment
+ Miscellaneous
+ Talk Show
+ Show. Talk Show
+
+
+ Sport
+ Live/Sport
+ General Sports
+ Football. Sports
+ Martial Sports
+ Martial Sports. Sports
+ Wrestling
+
+
+ Children
+ Educational/Schools Programmes
+ Animation
+ Cartoons/Puppets
+
+
+ Music
+ General Music/Ballet/Dance
+ Music. Folk
+ Musical
+
+
+ General Arts/Culture
+ Arts/Culture
+ Arts/Culture. Fine Arts
+ Religion
+
+
+ Social/Political
+ Social/Political. Famous People
+
+
+ Education
+ Educational
+ History"
+ Factual"
+ General Education/Science/Factual Topics
+ Science
+ Educational. Nature
+ Environment
+ Technology
+ Computers/Internet/Gaming
+
+
+ Leisure
+ Leisure. Lifestyle
+ Travel
+ Health
+ Leisure. Health
+ Medicine/Health
+ Cookery
+ Leisure. Cooking
+ Leisure. Shopping
+ Advertisement/Shopping
+ Consumer
+
+
+
+ Factual Crime
+
diff --git a/pvr.iptvsimple/resources/data/genres/kodiDvbGenres.xml b/pvr.iptvsimple/resources/data/genres/kodiDvbGenres.xml
new file mode 100755
index 000000000..b7b6369a7
--- /dev/null
+++ b/pvr.iptvsimple/resources/data/genres/kodiDvbGenres.xml
@@ -0,0 +1,138 @@
+
+
+
+ Kodi DVB Genres using Hexadecimal for genreId
+
+ Undefined
+
+
+ General Movie / Drama
+ Detective / Thriller
+ Adventure / Western / War
+ Science Fiction / Fantasy / Horror
+ Comedy
+ Soap / Melodrama / Folkloric
+ Romance
+ Serious / Classical / Religious / Historical Movie / Drama
+ Adult Movie / Drama
+
+
+ News / Current Affairs
+ News / Weather Report
+ News Magazine
+ Documentary
+ Discussion / Interview / Debate
+
+
+ Show / Game Show
+ Game Show / Quiz / Contest
+ Variety Show
+ Talk Show
+
+
+ Sports
+ Special Event
+ Sport Magazine
+ Football
+ Tennis / Squash
+ Team Sports
+ Athletics
+ Motor Sport
+ Water Sport
+ Winter Sports
+ Equestrian
+ Martial Sports
+
+
+ Children's / Youth Programmes
+ Pre-school Children's Programmes
+ Entertainment Programmes for 6 to 14
+ Entertainment Programmes for 10 to 16
+ Informational / Educational / School Programme
+ Cartoons / Puppets
+
+
+ Music / Ballet / Dance
+ Rock / Pop
+ Serious / Classical Music
+ Folk / Traditional Music
+ Jazz
+ Musical / Opera
+ Ballet
+
+
+ Arts / Culture
+ Performing Arts
+ Fine Arts
+ Religion
+ Popular Culture / Traditional Arts
+ Literature
+ Film / Cinema
+ Experimental Film / Video
+ Broadcasting / Press
+ New Media
+ Arts / Culture Magazines
+ Fashion
+
+
+ Social / Political / Economics
+ Magazines / Reports / Documentary
+ Economics / Social Advisory
+ Remarkable People
+
+
+ Education / Science / Factual
+ Nature / Animals / Environment
+ Technology / Natural Sciences
+ Medicine / Physiology / Psychology
+ Foreign Countries / Expeditions
+ Social / Spiritual Sciences
+ Further Education
+ Languages
+
+
+ Leisure / Hobbies
+ Tourism / Travel
+ Handicraft
+ Motoring
+ Fitness and Health
+ Cooking
+ Advertisement / Shopping
+ Gardening
+
+
+ Special Characteristics
+ Original Language
+ Black and White
+ Unpublished
+ Live Broadcast
+
+
+ Drama
+ Detective / Thriller
+ Adventure / Western / War
+ Science Fiction / Fantasy / Horror
+
+ Comedy
+ Soap / Melodrama / Folkloric
+ Romance
+ Serious / ClassicalReligion / Historical
+ Adult
+
diff --git a/pvr.iptvsimple/resources/data/genres/kodiDvbGenresTypeSubtype.xml b/pvr.iptvsimple/resources/data/genres/kodiDvbGenresTypeSubtype.xml
new file mode 100644
index 000000000..66e7516e4
--- /dev/null
+++ b/pvr.iptvsimple/resources/data/genres/kodiDvbGenresTypeSubtype.xml
@@ -0,0 +1,138 @@
+
+
+
+ Kodi DVB Genres using Integers for type and subtype
+
+ Undefined
+
+
+ Movie / Drama
+ Detective / Thriller
+ Adventure / Western / War
+ Science fiction / Fantasy / Horror
+ Comedy
+ Soap / Melodrama / Folkloric
+ Romance
+ Serious / Classical / Religious / Historical Movie / Drama
+ Adult Movie / Drama
+
+
+ News / Current Affairs
+ News / Weather Report
+ News Magazine
+ Documentary
+ Discussion / Interview / Debate
+
+
+ Show / Game Show
+ Game Show / Quiz / Contest
+ Variety show
+ Talk Show
+
+
+ Sports
+ Special Event
+ Sports Magazines
+ Football / Soccer
+ Tennis / Squash
+ Team Sports
+ Athletics
+ Motor Sport
+ Water Sport
+ Winter Sports
+ Equestrian
+ Martial Sports
+
+
+ Children's / Youth Programs
+ Pre-school Children's Programs
+ Entertainment programs for 6 to 14
+ Entertainment programs for 10 to 16
+ Informational / Educational / School programs
+ Cartoons / Puppets
+
+
+ Music / Ballet / Dance
+ Rock / Pop
+ Serious music / Classical Music
+ Folk / Traditional Music
+ Jazz
+ Musical / Opera
+ Ballet
+
+
+ Arts / Culture
+ Performing Arts
+ Fine Arts
+ Religion
+ Popular Culture / Traditional Arts
+ Literature
+ Film / Cinema
+ Experimental Film / Video
+ Broadcasting / Press
+ New Media
+ Arts magazines / Culture Magazines
+ Fashion
+
+
+ Social / Political issues / Economics
+ Magazines / Reports / Documentary
+ Economics / Social Advisory
+ Remarkable People
+
+
+ Education / Science / Factual topics
+ Nature / Animals / Environment
+ Technology / Natural sciences
+ Medicine / Physiology / Psychology
+ Foreign countries / Expeditions
+ Social / Spiritual Sciences
+ Further Education
+ Languages
+
+
+ Leisure Hobbies
+ Tourism / Travel
+ Handicraft
+ Motoring
+ Fitness and Health
+ Cooking
+ Advertisement / Shopping
+ Gardening
+
+
+ Special Characteristics
+ Original Language
+ Black & White
+ Unpublished
+ Live Broadcast
+
+
+ Drama
+ Detective/Thriller
+ Adventure/Western/War
+ Science Fiction/Fantasy/Horror
+
+ Comedy
+ Soap/Melodrama/Folkloric
+ Romance
+ Serious/ClassicalReligion/Historical
+ Adult
+
\ No newline at end of file
diff --git a/pvr.iptvsimple/resources/language/resource.language.en_gb/strings.po b/pvr.iptvsimple/resources/language/resource.language.en_gb/strings.po
index 583940706..47f55753d 100644
--- a/pvr.iptvsimple/resources/language/resource.language.en_gb/strings.po
+++ b/pvr.iptvsimple/resources/language/resource.language.en_gb/strings.po
@@ -28,6 +28,7 @@ msgstr ""
#label-option: General - m3uPathType
#label-option: General - epgPathType
#label-option: General - logoPathType
+#label-option: General - genresPathType
msgctxt "#30001"
msgid "Local path (include local network)"
msgstr ""
@@ -35,6 +36,7 @@ msgstr ""
#label-option: General - m3uPathType
#label-option: General - epgPathType
#label-option: General - logoPathType
+#label-option: General - genresPathType
msgctxt "#30002"
msgid "Remote path (Internet address)"
msgstr ""
@@ -57,16 +59,26 @@ msgctxt "#30012"
msgid "M3U playlist URL"
msgstr ""
+#label: General - startNum
msgctxt "#30013"
msgid "Start channel number"
msgstr ""
-#empty strings from id 30014 to 30019
+#label: General - numberByOrder
+msgctxt "#30014"
+msgid "Only number by channel order in M3U"
+msgstr ""
+
+#empty strings from id 30015 to 30018
#label-category: epgsettings
+msgctxt "#30019"
+msgid "EPG Settings"
+msgstr ""
+
#label-group: EPG Settings - EPG Settings
msgctxt "#30020"
-msgid "EPG Settings"
+msgid "EPG"
msgstr ""
#label: EPG Settings - epgPath
@@ -144,7 +156,30 @@ msgctxt "#30044"
msgid "Prefer XMLTV"
msgstr ""
-#empty strings from id 30045 to 30599
+#empty strings from id 30045 to 30549
+
+#label-category: genres
+#label-group: Genres - Genres
+msgctxt "#30050"
+msgid "Genres"
+msgstr ""
+
+#label: Genres - useEpgGenreText
+msgctxt "#30051"
+msgid "Use genre text from XMLTV when mapping genres"
+msgstr ""
+
+#label: Genres - genresPath
+msgctxt "#30052"
+msgid "Genres path"
+msgstr ""
+
+#label: Genres - genresUrl
+msgctxt "#30053"
+msgid "Genres URL"
+msgstr ""
+
+#empty strings from id 30054 to 30599
#############
# help info #
@@ -182,8 +217,12 @@ msgctxt "#30605"
msgid "The number to start numbering channels from."
msgstr ""
-#empty strings from id 30006 to 30619
+#help: General - numberByOrder
+msgctxt "#30606"
+msgid "Ignore any 'tvg-chno' tags and only number channels by the order in the M3U starting at 'Start channel number'."
+msgstr ""
+#empty strings from id 30607 to 30619
#help info - EPG Settings
@@ -214,7 +253,7 @@ msgstr ""
#help: EPG Settings - epgTimeShift
msgctxt "#30625"
-msgid "Adjust the EPG times by this value in minutes, range is from -720 mins to +720 mins (+/- 12 hours)."
+msgid "Adjust the EPG times by this value in minutes, range is from -720 mins to +840 mins (- 12 hours to +14 hours)."
msgstr ""
#help: EPG Settings - epgTSOverside
@@ -222,13 +261,13 @@ msgctxt "#30626"
msgid "Whether or not to override the time shift for all channels with `EPG time shift`. If not enabled `EPG time shift` plus the individual time shift per channel (if available) will be used."
msgstr ""
-#empty strings from id 30027 to 30639
+#empty strings from id 30627 to 30639
#help info - Channel Logos
#help-category: Channel Logos
msgctxt "#30640"
-msgid "Settings realted to Channel Logos."
+msgid "Settings related to Channel Logos."
msgstr ""
#help: Channel Logos - logoPathType
@@ -249,4 +288,33 @@ msgstr ""
#help: Channel Logos - logoFromEpg
msgctxt "#30644"
msgid "Preference on how to handle channel logos. The options are: [Ignore] - Don't use channel logos from an XMLTV file; [Prefer M3U] - Use the channel logo from the M3U if available otherwise use the XMLTV logo; [Prefer XMLTV] - Use the channel logo from the XMLTV file if available otherwise use the M3U logo."
-msgstr ""
\ No newline at end of file
+msgstr ""
+
+#empty strings from id 30645 to 30659
+
+#help info - Genres
+
+#help-category: Genres
+msgctxt "#30660"
+msgid "Settings related to genres."
+msgstr ""
+
+#help: Genres - useEpgGenreText
+msgctxt "#30661"
+msgid "If enabled, and a genre mapping file is used to get a genre type and sub type use the EPG's genre text (i.e. 'category' text) for the genre instead of the kodi default text."
+msgstr ""
+
+#help: Genres - genresPathType
+msgctxt "#30662"
+msgid "Select where to find the genres XML resource. The options are: [Local path] - A path to a genres XML file whether it be on the device or the local network; [Remote path] - A URL specifying the location of the genres XML file."
+msgstr ""
+
+#help: Genres - genresPath
+msgctxt "#30663"
+msgid "If location is [Local Path] this setting should contain a valid path."
+msgstr ""
+
+#help: Genres - genresUrl
+msgctxt "#30664"
+msgid "If location is [Remote Path] this setting should contain a valid URL."
+msgstr ""
diff --git a/pvr.iptvsimple/resources/settings.xml b/pvr.iptvsimple/resources/settings.xml
index ca628dbcb..a4c882345 100644
--- a/pvr.iptvsimple/resources/settings.xml
+++ b/pvr.iptvsimple/resources/settings.xml
@@ -54,12 +54,17 @@
1
+
+ 0
+ false
+
+
-
+ 01
@@ -110,7 +115,7 @@
-72030
- 720
+ 84014044
@@ -124,6 +129,53 @@
+
+
+
+
+ 0
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+ 0
+ special://userdata/addon_data/pvr.iptvsimple/genres/genreTextMappings/genres.xml
+
+ true
+ false
+
+
+ 0
+
+
+ 1033
+
+
+
+ 0
+
+
+ true
+
+
+ 1
+
+
+
+
+
+
diff --git a/src/client.cpp b/src/client.cpp
index 26ed2ca26..35d8ad824 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -36,16 +36,6 @@ using namespace iptvsimple;
using namespace iptvsimple::data;
using namespace iptvsimple::utilities;
-#ifdef TARGET_WINDOWS
-#define snprintf _snprintf
-#ifdef CreateDirectory
-#undef CreateDirectory
-#endif
-#ifdef DeleteFile
-#undef DeleteFile
-#endif
-#endif
-
bool m_created = false;
ADDON_STATUS m_currentStatus = ADDON_STATUS_UNKNOWN;
PVRIptvData* m_data = nullptr;
@@ -112,15 +102,12 @@ ADDON_STATUS ADDON_Create(void* hdl, void* props)
Logger::GetInstance().SetPrefix("pvr.iptvsimple");
- Logger::Log(LogLevel::LEVEL_INFO, "%s Creating the PVR IPTV Simple add-on", __FUNCTION__);
+ Logger::Log(LogLevel::LEVEL_INFO, "%s - Creating the PVR IPTV Simple add-on", __FUNCTION__);
m_currentStatus = ADDON_STATUS_UNKNOWN;
const std::string userPath = pvrprops->strUserPath;
const std::string clientPath = pvrprops->strClientPath;
- if (!XBMC->DirectoryExists(settings.GetUserPath().c_str()))
- XBMC->CreateDirectory(settings.GetUserPath().c_str());
-
settings.ReadFromAddon(userPath, clientPath);
m_data = new PVRIptvData;
diff --git a/src/iptvsimple/ChannelGroups.cpp b/src/iptvsimple/ChannelGroups.cpp
index 32355c894..1f1a9eb31 100644
--- a/src/iptvsimple/ChannelGroups.cpp
+++ b/src/iptvsimple/ChannelGroups.cpp
@@ -47,10 +47,10 @@ void ChannelGroups::GetChannelGroups(std::vector& kodiChannel
for (const auto& channelGroup : m_channelGroups)
{
- Logger::Log(LEVEL_DEBUG, "%s - Transfer channelGroup '%s', ChannelGroupIndex '%d'", __FUNCTION__, channelGroup.GetGroupName().c_str(), channelGroup.GetUniqueId());
-
if (channelGroup.IsRadio() == radio)
{
+ Logger::Log(LEVEL_DEBUG, "%s - Transfer channelGroup '%s', ChannelGroupId '%d'", __FUNCTION__, channelGroup.GetGroupName().c_str(), channelGroup.GetUniqueId());
+
PVR_CHANNEL_GROUP kodiChannelGroup = {0};
channelGroup.UpdateTo(kodiChannelGroup);
@@ -67,6 +67,14 @@ PVR_ERROR ChannelGroups::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_C
const ChannelGroup* myGroup = FindChannelGroup(group.strGroupName);
if (myGroup)
{
+ // We set a channel order here that applies to this group in kodi-pvr
+ // This allows the users to use the 'Backend Order' sort option in the left to
+ // have the same order as the backend (regardles of the channel numbering used)
+ //
+ // We don't set a channel number within this group as different channel numbers
+ // per group are not supported in M3U files
+ int channelOrder = 1;
+
for (int memberId : myGroup->GetMemberChannelIndexes())
{
if (memberId < 0 || memberId >= static_cast(m_channels.GetChannelsAmount()))
@@ -77,7 +85,10 @@ PVR_ERROR ChannelGroups::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_C
strncpy(xbmcGroupMember.strGroupName, group.strGroupName, sizeof(xbmcGroupMember.strGroupName) - 1);
xbmcGroupMember.iChannelUniqueId = channel.GetUniqueId();
- xbmcGroupMember.iChannelNumber = channel.GetChannelNumber();
+ xbmcGroupMember.iOrder = channelOrder++; // Keep the channels in list order as per the M3U
+
+ Logger::Log(LEVEL_DEBUG, "%s - Transfer channel group '%s' member '%s', ChannelId '%d', ChannelOrder: '%d'", __FUNCTION__,
+ myGroup->GetGroupName().c_str(), channel.GetChannelName().c_str(), channel.GetUniqueId(), channelOrder);
PVR->TransferChannelGroupMember(handle, &xbmcGroupMember);
}
diff --git a/src/iptvsimple/Channels.cpp b/src/iptvsimple/Channels.cpp
index 0c9ad0044..209e6df60 100644
--- a/src/iptvsimple/Channels.cpp
+++ b/src/iptvsimple/Channels.cpp
@@ -28,14 +28,16 @@
#include "utilities/FileUtils.h"
#include "utilities/Logger.h"
+#include "p8-platform/util/StringUtils.h"
+
#include
using namespace iptvsimple;
using namespace iptvsimple::data;
using namespace iptvsimple::utilities;
-Channels::Channels()
- : m_logoLocation(Settings::GetInstance().GetLogoLocation()),
+Channels::Channels()
+ : m_logoLocation(Settings::GetInstance().GetLogoLocation()),
m_currentChannelNumber(Settings::GetInstance().GetStartChannelNumber()) {}
void Channels::Clear()
@@ -52,15 +54,20 @@ int Channels::GetChannelsAmount() const
void Channels::GetChannels(std::vector& kodiChannels, bool radio) const
{
+ // We set a channel order here that applies to the 'Any channels' group in kodi-pvr
+ // This allows the users to use the 'Backend Order' sort option in the left to
+ // have the same order as the backend (regardles of the channel numbering used)
+ int channelOrder = 1;
for (const auto& channel : m_channels)
{
if (channel.IsRadio() == radio)
{
- Logger::Log(LEVEL_DEBUG, "%s - Transfer channel '%s', ChannelIndex '%d'", __FUNCTION__, channel.GetChannelName().c_str(),
- channel.GetUniqueId());
+ Logger::Log(LEVEL_DEBUG, "%s - Transfer channel '%s', ChannelId '%d', ChannelNumber: '%d'", __FUNCTION__, channel.GetChannelName().c_str(),
+ channel.GetUniqueId(), channel.GetChannelNumber());
PVR_CHANNEL kodiChannel = {0};
channel.UpdateTo(kodiChannel);
+ kodiChannel.iOrder = channelOrder++; // Keep the channels in list order as per the M3U
kodiChannels.emplace_back(kodiChannel);
}
@@ -109,22 +116,28 @@ Channel* Channels::GetChannel(int uniqueId)
return nullptr;
}
-const Channel* Channels::FindChannel(const std::string& id, const std::string& name) const
+const Channel* Channels::FindChannel(const std::string& id, const std::string& displayName) const
{
- const std::string tvgName = std::regex_replace(name, std::regex(" "), "_");
-
for (const auto& myChannel : m_channels)
{
- if (myChannel.GetTvgId() == id)
+ if (StringUtils::EqualsNoCase(myChannel.GetTvgId(), id))
return &myChannel;
+ }
- if (tvgName.empty())
- continue;
+ if (displayName.empty())
+ return nullptr;
- if (myChannel.GetTvgName() == tvgName)
+ const std::string convertedDisplayName = std::regex_replace(displayName, std::regex(" "), "_");
+ for (const auto& myChannel : m_channels)
+ {
+ if (StringUtils::EqualsNoCase(myChannel.GetTvgName(), convertedDisplayName) ||
+ StringUtils::EqualsNoCase(myChannel.GetTvgName(), displayName))
return &myChannel;
+ }
- if (myChannel.GetChannelName() == name)
+ for (const auto& myChannel : m_channels)
+ {
+ if (StringUtils::EqualsNoCase(myChannel.GetChannelName(), displayName))
return &myChannel;
}
diff --git a/src/iptvsimple/Channels.h b/src/iptvsimple/Channels.h
index fb8dd5d9e..4e068552e 100644
--- a/src/iptvsimple/Channels.h
+++ b/src/iptvsimple/Channels.h
@@ -48,7 +48,7 @@ namespace iptvsimple
void AddChannel(iptvsimple::data::Channel& channel, std::vector& groupIdList, iptvsimple::ChannelGroups& channelGroups);
iptvsimple::data::Channel* GetChannel(int uniqueId);
- const iptvsimple::data::Channel* FindChannel(const std::string& id, const std::string& name) const;
+ const iptvsimple::data::Channel* FindChannel(const std::string& id, const std::string& displayName) const;
const std::vector& GetChannelsList() const { return m_channels; }
void Clear();
void ApplyChannelLogos();
diff --git a/src/iptvsimple/Epg.cpp b/src/iptvsimple/Epg.cpp
index 7a92a7458..19afe2929 100644
--- a/src/iptvsimple/Epg.cpp
+++ b/src/iptvsimple/Epg.cpp
@@ -40,21 +40,32 @@ using namespace iptvsimple::data;
using namespace iptvsimple::utilities;
using namespace rapidxml;
-Epg::Epg(Channels& channels)
- : m_channels(channels), m_xmltvLocation(Settings::GetInstance().GetEpgLocation()), m_epgTimeShift(Settings::GetInstance().GetEpgTimeshiftSecs()),
- m_tsOverride(Settings::GetInstance().GetTsOverride()), m_lastStart(0), m_lastEnd(0) {}
+Epg::Epg(Channels& channels)
+ : m_channels(channels), m_xmltvLocation(Settings::GetInstance().GetEpgLocation()), m_epgTimeShift(Settings::GetInstance().GetEpgTimeshiftSecs()),
+ m_tsOverride(Settings::GetInstance().GetTsOverride()), m_lastStart(0), m_lastEnd(0)
+{
+ FileUtils::CopyDirectory(FileUtils::GetResourceDataPath() + GENRE_DIR, GENRE_ADDON_DATA_BASE_DIR, true);
+
+ if (!FileUtils::FileExists(DEFAULT_GENRE_TEXT_MAP_FILE))
+ {
+ MoveOldGenresXMLFileToNewLocation();
+ }
+}
void Epg::Clear()
{
m_channelEpgs.clear();
- m_genres.clear();
+ m_genreMappings.clear();
}
bool Epg::LoadEPG(time_t start, time_t end)
{
+ auto started = std::chrono::high_resolution_clock::now();
+ Logger::Log(LEVEL_DEBUG, "%s - EPG Load Start", __FUNCTION__);
+
if (m_xmltvLocation.empty())
{
- Logger::Log(LEVEL_NOTICE, "EPG file path is not configured. EPG not loaded.");
+ Logger::Log(LEVEL_NOTICE, "%s - EPG file path is not configured. EPG not loaded.", __FUNCTION__);
return false;
}
@@ -62,7 +73,8 @@ bool Epg::LoadEPG(time_t start, time_t end)
if (GetXMLTVFileWithRetries(data))
{
- char* buffer = FillBufferFromXMLTVData(data);
+ std::string decompressedData;
+ char* buffer = FillBufferFromXMLTVData(data, decompressedData);
if (!buffer)
return false;
@@ -74,14 +86,14 @@ bool Epg::LoadEPG(time_t start, time_t end)
}
catch (parse_error p)
{
- Logger::Log(LEVEL_ERROR, "Unable parse EPG XML: %s", p.what());
+ Logger::Log(LEVEL_ERROR, "%s - Unable parse EPG XML: %s", __FUNCTION__, p.what());
return false;
}
xml_node<>* rootElement = xmlDoc.first_node("tv");
if (!rootElement)
{
- Logger::Log(LEVEL_ERROR, "Invalid EPG XML: no tag found");
+ Logger::Log(LEVEL_ERROR, "%s - Invalid EPG XML: no tag found", __FUNCTION__);
return false;
}
@@ -99,11 +111,14 @@ bool Epg::LoadEPG(time_t start, time_t end)
LoadGenres();
- Logger::Log(LEVEL_NOTICE, "EPG Loaded.");
-
if (Settings::GetInstance().GetEpgLogosMode() != EpgLogosMode::IGNORE_XMLTV)
ApplyChannelsLogosFromEPG();
+ int milliseconds = std::chrono::duration_cast(
+ std::chrono::high_resolution_clock::now() - started).count();
+
+ Logger::Log(LEVEL_NOTICE, "%s - EPG Loaded - %d (ms)", __FUNCTION__, milliseconds);
+
return true;
}
@@ -114,10 +129,10 @@ bool Epg::GetXMLTVFileWithRetries(std::string& data)
while (count < 3) // max 3 tries
{
- if ((bytesRead = FileUtils::GetCachedFileContents(TVG_FILE_NAME, m_xmltvLocation, data, Settings::GetInstance().UseEPGCache())) != 0)
+ if ((bytesRead = FileUtils::GetCachedFileContents(XMLTV_CACHE_FILENAME, m_xmltvLocation, data, Settings::GetInstance().UseEPGCache())) != 0)
break;
- Logger::Log(LEVEL_ERROR, "Unable to load EPG file '%s': file is missing or empty. :%dth try.", m_xmltvLocation.c_str(), ++count);
+ Logger::Log(LEVEL_ERROR, "%s - Unable to load EPG file '%s': file is missing or empty. :%dth try.", __FUNCTION__, m_xmltvLocation.c_str(), ++count);
if (count < 3)
std::this_thread::sleep_for(std::chrono::microseconds(2 * 1000 * 1000)); // sleep 2 sec before next try.
@@ -125,38 +140,37 @@ bool Epg::GetXMLTVFileWithRetries(std::string& data)
if (bytesRead == 0)
{
- Logger::Log(LEVEL_ERROR, "Unable to load EPG file '%s': file is missing or empty. After %d tries.", m_xmltvLocation.c_str(), count);
+ Logger::Log(LEVEL_ERROR, "%s - Unable to load EPG file '%s': file is missing or empty. After %d tries.", __FUNCTION__, m_xmltvLocation.c_str(), count);
return false;
}
return true;
}
-char* Epg::FillBufferFromXMLTVData(std::string& data)
+char* Epg::FillBufferFromXMLTVData(std::string& data, std::string& decompressedData)
{
- std::string decompressed;
char* buffer = nullptr;
// gzip packed
if (data[0] == '\x1F' && data[1] == '\x8B' && data[2] == '\x08')
{
- if (!FileUtils::GzipInflate(data, decompressed))
+ if (!FileUtils::GzipInflate(data, decompressedData))
{
- Logger::Log(LEVEL_ERROR, "Invalid EPG file '%s': unable to decompress file.", m_xmltvLocation.c_str());
+ Logger::Log(LEVEL_ERROR, "%s - Invalid EPG file '%s': unable to decompress file.", __FUNCTION__, m_xmltvLocation.c_str());
return nullptr;
}
- buffer = &(decompressed[0]);
+ buffer = &(decompressedData[0]);
}
else
{
- buffer = &(data[0]);
+ buffer = &(data[0]);
}
XmltvFileFormat fileFormat = GetXMLTVFileFormat(buffer);
if (fileFormat == XmltvFileFormat::INVALID)
{
- Logger::Log(LEVEL_ERROR, "Invalid EPG file '%s': unable to parse file.", m_xmltvLocation.c_str());
+ Logger::Log(LEVEL_ERROR, "%s - Invalid EPG file '%s': unable to parse file.", __FUNCTION__, m_xmltvLocation.c_str());
return nullptr;
}
@@ -196,20 +210,27 @@ bool Epg::LoadChannelEpgs(xml_node<>* rootElement)
m_channelEpgs.clear();
- xml_node<>* channelNode = nullptr;
- for (channelNode = rootElement->first_node("channel"); channelNode; channelNode = channelNode->next_sibling("channel"))
+ for (xml_node<>* channelNode = rootElement->first_node("channel"); channelNode; channelNode = channelNode->next_sibling("channel"))
{
ChannelEpg channelEpg;
if (channelEpg.UpdateFrom(channelNode, m_channels))
+ {
+ Logger::Log(LEVEL_DEBUG, "%s - Loaded chanenl EPG with id '%s' with display names: '%s'", __FUNCTION__, channelEpg.GetId().c_str(), StringUtils::Join(channelEpg.GetNames(), EPG_STRING_TOKEN_SEPARATOR).c_str());
+
m_channelEpgs.emplace_back(channelEpg);
+ }
}
if (m_channelEpgs.size() == 0)
{
- Logger::Log(LEVEL_ERROR, "EPG channels not found.");
+ Logger::Log(LEVEL_ERROR, "%s - EPG channels not found.", __FUNCTION__);
return false;
}
+ else
+ {
+ Logger::Log(LEVEL_NOTICE, "%s - Loaded '%d' EPG channels.", __FUNCTION__, m_channelEpgs.size());
+ }
return true;
}
@@ -241,7 +262,7 @@ void Epg::LoadEpgEntries(xml_node<>* rootElement, int start, int end)
if (!GetAttributeValue(channelNode, "channel", id))
continue;
- if (!channelEpg || StringUtils::CompareNoCase(channelEpg->GetId(), id) != 0)
+ if (!channelEpg || !StringUtils::EqualsNoCase(channelEpg->GetId(), id))
{
if (!(channelEpg = FindEpgForChannel(id)))
continue;
@@ -255,6 +276,8 @@ void Epg::LoadEpgEntries(xml_node<>* rootElement, int start, int end)
channelEpg->AddEpgEntry(entry);
}
}
+
+ Logger::Log(LEVEL_NOTICE, "%s - Loaded '%d' EPG entries.", __FUNCTION__, broadcastId);
}
@@ -306,7 +329,7 @@ PVR_ERROR Epg::GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t sta
EPG_TAG tag = {0};
- epgEntry.UpdateTo(tag, iChannelUid, shift, m_genres);
+ epgEntry.UpdateTo(tag, iChannelUid, shift, m_genreMappings);
PVR->TransferEpgEntry(handle, &tag);
@@ -324,7 +347,7 @@ ChannelEpg* Epg::FindEpgForChannel(const std::string& id)
{
for (auto& myChannelEpg : m_channelEpgs)
{
- if (StringUtils::CompareNoCase(myChannelEpg.GetId(), id) == 0)
+ if (StringUtils::EqualsNoCase(myChannelEpg.GetId(), id))
return &myChannelEpg;
}
@@ -335,15 +358,28 @@ ChannelEpg* Epg::FindEpgForChannel(const Channel& channel)
{
for (auto& myChannelEpg : m_channelEpgs)
{
- if (myChannelEpg.GetId() == channel.GetTvgId())
+ if (StringUtils::EqualsNoCase(myChannelEpg.GetId(), channel.GetTvgId()))
return &myChannelEpg;
+ }
- const std::string name = std::regex_replace(myChannelEpg.GetName(), std::regex(" "), "_");
- if (name == channel.GetTvgName() || myChannelEpg.GetName() == channel.GetTvgName())
- return &myChannelEpg;
+ for (auto& myChannelEpg : m_channelEpgs)
+ {
+ for (const std::string& displayName : myChannelEpg.GetNames())
+ {
+ const std::string convertedDisplayName = std::regex_replace(displayName, std::regex(" "), "_");
+ if (StringUtils::EqualsNoCase(convertedDisplayName, channel.GetTvgName()) ||
+ StringUtils::EqualsNoCase(displayName, channel.GetTvgName()))
+ return &myChannelEpg;
+ }
+ }
- if (myChannelEpg.GetName() == channel.GetChannelName())
- return &myChannelEpg;
+ for (auto& myChannelEpg : m_channelEpgs)
+ {
+ for (const std::string& displayName : myChannelEpg.GetNames())
+ {
+ if (StringUtils::EqualsNoCase(displayName, channel.GetChannelName()))
+ return &myChannelEpg;
+ }
}
return nullptr;
@@ -377,23 +413,16 @@ void Epg::ApplyChannelsLogosFromEPG()
bool Epg::LoadGenres()
{
- // try to load genres from userdata folder
- std::string filePath = FileUtils::GetUserFilePath(GENRES_MAP_FILENAME);
- if (!XBMC->FileExists(filePath.c_str(), false))
- {
- // try to load file from addom folder
- filePath = FileUtils::GetClientFilePath(GENRES_MAP_FILENAME);
- if (!XBMC->FileExists(filePath.c_str(), false))
- return false;
- }
+ if (!FileUtils::FileExists(Settings::GetInstance().GetGenresLocation()))
+ return false;
std::string data;
- FileUtils::GetFileContents(filePath, data);
+ FileUtils::GetFileContents(Settings::GetInstance().GetGenresLocation(), data);
if (data.empty())
return false;
- m_genres.clear();
+ m_genreMappings.clear();
char* buffer = &(data[0]);
xml_document<> xmlDoc;
@@ -412,12 +441,31 @@ bool Epg::LoadGenres()
for (xml_node<>* pGenreNode = pRootElement->first_node("genre"); pGenreNode; pGenreNode = pGenreNode->next_sibling("genre"))
{
- EpgGenre genre;
+ EpgGenre genreMapping;
- if (genre.UpdateFrom(pGenreNode))
- m_genres.emplace_back(genre);
+ if (genreMapping.UpdateFrom(pGenreNode))
+ m_genreMappings.emplace_back(genreMapping);
}
xmlDoc.clear();
+
+ if (!m_genreMappings.empty())
+ Logger::Log(LEVEL_NOTICE, "%s - Loaded %d genres", __FUNCTION__, m_genreMappings.size());
+
return true;
-}
\ No newline at end of file
+}
+
+void Epg::MoveOldGenresXMLFileToNewLocation()
+{
+ //If we don't have a genres.xml file yet copy it if it exists in any of the other old locations.
+ //If not copy a placeholder file that allows the settings dialog to function.
+ if (FileUtils::FileExists(ADDON_DATA_BASE_DIR + "/" + GENRES_MAP_FILENAME))
+ FileUtils::CopyFile(ADDON_DATA_BASE_DIR + "/" + GENRES_MAP_FILENAME, DEFAULT_GENRE_TEXT_MAP_FILE);
+ else if (FileUtils::FileExists(FileUtils::GetSystemAddonPath() + "/" + GENRES_MAP_FILENAME))
+ FileUtils::CopyFile(FileUtils::GetSystemAddonPath() + "/" + GENRES_MAP_FILENAME, DEFAULT_GENRE_TEXT_MAP_FILE);
+ else
+ FileUtils::CopyFile(FileUtils::GetResourceDataPath() + "/" + GENRES_MAP_FILENAME, DEFAULT_GENRE_TEXT_MAP_FILE);
+
+ FileUtils::DeleteFile(ADDON_DATA_BASE_DIR + "/" + GENRES_MAP_FILENAME.c_str());
+ FileUtils::DeleteFile(FileUtils::GetSystemAddonPath() + "/" + GENRES_MAP_FILENAME.c_str());
+}
diff --git a/src/iptvsimple/Epg.h b/src/iptvsimple/Epg.h
index df4925845..139348735 100644
--- a/src/iptvsimple/Epg.h
+++ b/src/iptvsimple/Epg.h
@@ -24,6 +24,7 @@
#include "kodi/libXBMC_pvr.h"
#include "Channels.h"
+#include "Settings.h"
#include "data/ChannelEpg.h"
#include "data/EpgGenre.h"
@@ -34,6 +35,8 @@ namespace iptvsimple
{
static const int SECONDS_IN_DAY = 86400;
static const std::string GENRES_MAP_FILENAME = "genres.xml";
+ static const std::string GENRE_DIR = "/genres";
+ static const std::string GENRE_ADDON_DATA_BASE_DIR = ADDON_DATA_BASE_DIR + GENRE_DIR;
enum class XmltvFileFormat
{
@@ -53,10 +56,11 @@ namespace iptvsimple
private:
static const XmltvFileFormat GetXMLTVFileFormat(const char* buffer);
+ static void MoveOldGenresXMLFileToNewLocation();
bool LoadEPG(time_t iStart, time_t iEnd);
bool GetXMLTVFileWithRetries(std::string& data);
- char* FillBufferFromXMLTVData(std::string& data);
+ char* FillBufferFromXMLTVData(std::string& data, std::string& decompressedData);
bool LoadChannelEpgs(rapidxml::xml_node<>* rootElement);
void LoadEpgEntries(rapidxml::xml_node<>* rootElement, int start, int end);
bool LoadGenres();
@@ -73,6 +77,6 @@ namespace iptvsimple
iptvsimple::Channels& m_channels;
std::vector m_channelEpgs;
- std::vector m_genres;
+ std::vector m_genreMappings;
};
} //namespace iptvsimple
\ No newline at end of file
diff --git a/src/iptvsimple/PlaylistLoader.cpp b/src/iptvsimple/PlaylistLoader.cpp
index 34588f3a0..1eefb0fa0 100644
--- a/src/iptvsimple/PlaylistLoader.cpp
+++ b/src/iptvsimple/PlaylistLoader.cpp
@@ -26,9 +26,11 @@
#include "../client.h"
#include "utilities/FileUtils.h"
#include "utilities/Logger.h"
+#include "utilities/WebUtils.h"
#include "p8-platform/util/StringUtils.h"
+#include
#include
#include