From 0838cb5ea9a08a956892d45d1b516faf1d5282d9 Mon Sep 17 00:00:00 2001 From: taligentx Date: Mon, 9 Jul 2018 00:10:08 -0500 Subject: [PATCH 01/15] Force plain HTTP connections for HTTPS EPG thumbnail URLs --- Contents/Code/__init__.py | 2 +- README.md | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 1bb1db5..d198632 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -483,7 +483,7 @@ def channels(startCount=0, art=ART): # Check the EPG entry for a thumbnail if tvhEPGEntry.get('image') and tvhEPGEntry['image'].startswith('http'): - epgThumb = tvhEPGEntry['image'] + epgThumb = tvhEPGEntry['image'].replace('https://', 'http://') # Use EPG thumbnails from Tvheadend if a thumbnail is not available from the metadata providers if thumb is None and epgThumb: diff --git a/README.md b/README.md index 8ec79c9..59d8f57 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,11 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](https://tvheadend.org), including metadata from Tvheadend's EPG, [theTVDB](https://thetvdb.com), and [The Movie DB](https://www.themoviedb.org). ## Release notes +* 2018.07.09 - [LiveTVH 1.4-develop] + * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) + * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) - This pushes the pre-existing changes in the develop branch to master as a release. + * This pushes the pre-existing changes in the develop branch to master as a release. * Updated: Tvheadend channel tags support additional codecs, resolutions, and radio (audio-only) channels * Updated: Changed image filenames to match Plex channel guidelines. From cb1f3f34e7e234b14fa2deac5575dc558d743beb Mon Sep 17 00:00:00 2001 From: taligentx Date: Tue, 10 Jul 2018 23:05:07 -0500 Subject: [PATCH 02/15] Change audio bitrate for Plex Web direct streaming --- Contents/Code/__init__.py | 12 ++++++------ README.md | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index d198632..4743400 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -636,10 +636,10 @@ def channel( parts = [PartObject(key=playbackURL, streams = [ VideoStreamObject(codec=streamVideo,bitrate=8000,width=1280,height=720), - AudioStreamObject(codec=streamAudio,bitrate=256)])], + AudioStreamObject(codec=streamAudio,bitrate=192)])], video_resolution = '720', container = 'mpegts', - bitrate = 8256, + bitrate = 8192, width = 1280, height = 720, duration = 86400000, @@ -654,10 +654,10 @@ def channel( parts = [PartObject(key=playbackURL, streams = [ VideoStreamObject(codec=streamVideo,bitrate=2000,width=720,height=576), - AudioStreamObject(codec=streamAudio,bitrate=256)])], + AudioStreamObject(codec=streamAudio,bitrate=192)])], video_resolution = '576', container = 'mpegts', - bitrate = 2256, + bitrate = 2192, width = 720, height = 576, duration = 86400000, @@ -672,10 +672,10 @@ def channel( parts = [PartObject(key=playbackURL, streams = [ VideoStreamObject(codec=streamVideo,bitrate=11000,width=1920,height=1080), - AudioStreamObject(codec=streamAudio,bitrate=256)])], + AudioStreamObject(codec=streamAudio,bitrate=192)])], video_resolution = '1080', container = 'mpegts', - bitrate = 11256, + bitrate = 11192, width = 1920, height = 1080, duration = 86400000, diff --git a/README.md b/README.md index 59d8f57..af02ba3 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,14 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Release notes * 2018.07.09 - [LiveTVH 1.4-develop] + * Updated: Changed specified audio bitrate to enable audio direct streaming for Plex Web * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) * This pushes the pre-existing changes in the develop branch to master as a release. * Updated: Tvheadend channel tags support additional codecs, resolutions, and radio (audio-only) channels - * Updated: Changed image filenames to match Plex channel guidelines. + * Updated: Changed image filenames to match Plex channel guidelines + * Updated: Replaced deprecated string substitution per [#18](https://github.com/taligentx/LiveTVH.bundle/pull/18) * 2017.05.22 - [LiveTVH 1.2](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.2) * New: Paginated channel lists with configurable # of items per page - this helps with longer channel lists (a necessity for IPTV providers with thousands of channels). From edc42a4cafdb6666644d410e1b9871b3706bb0f4 Mon Sep 17 00:00:00 2001 From: taligentx Date: Thu, 12 Jul 2018 16:31:15 -0500 Subject: [PATCH 03/15] Fix recordings display when resolution is not set --- Contents/Code/__init__.py | 4 +++- README.md | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 4743400..ff02b6f 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -21,7 +21,7 @@ # /Preferences -liveTVHVersion = '1.3' +liveTVHVersion = '1.4' TITLE = 'LiveTVH' PREFIX = '/video/livetvh' THUMB = 'icon-default.png' @@ -824,6 +824,7 @@ def recordings(tvhVideoTags, tvhAudioTags, startCount=0): streamURL = '/' + tvhRecording['url'] streamVideo = None streamAudio = None + streamResolution = None thumb = None fallbackThumb = None art = R(ART) @@ -948,6 +949,7 @@ def recordings(tvhVideoTags, tvhAudioTags, startCount=0): streamURL=streamURL, streamVideo=streamVideo, streamAudio=streamAudio, + streamResolution=streamResolution, thumb=thumb, fallbackThumb=fallbackThumb, art=art, diff --git a/README.md b/README.md index af02ba3..daeb22e 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](https://tvheadend.org), including metadata from Tvheadend's EPG, [theTVDB](https://thetvdb.com), and [The Movie DB](https://www.themoviedb.org). ## Release notes -* 2018.07.09 - [LiveTVH 1.4-develop] - * Updated: Changed specified audio bitrate to enable audio direct streaming for Plex Web +* LiveTVH 1.4-develop + * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) + * Bugfix: Recordings failed to display when resolution was not set * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) * This pushes the pre-existing changes in the develop branch to master as a release. From c2ffe68a8a882982475a32aed831ccb0b6f8a006 Mon Sep 17 00:00:00 2001 From: taligentx Date: Thu, 12 Jul 2018 17:59:13 -0500 Subject: [PATCH 04/15] Add documentation on necessary Tvheadend user access rights --- README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index daeb22e..fad3cd6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # LiveTVH.bundle LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](https://tvheadend.org), including metadata from Tvheadend's EPG, [theTVDB](https://thetvdb.com), and [The Movie DB](https://www.themoviedb.org). +## Features +* Playback of Tvheadend video channels, audio channels, and recordings. +* EPG displayed as a simple list within each channel description. +* Metadata and artwork lookup from theTVDB (using EPG zap2it IDs if available) and The Movie DB. +* Direct streaming (experimental, primarily tested with Plex Web and iOS). + ## Release notes * LiveTVH 1.4-develop * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming @@ -32,12 +38,6 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * 2017.05.10 - Initial release 1.0 -## Features -* Playback of Tvheadend video channels, audio channels, and recordings. -* EPG displayed as a simple list within each channel description. -* Metadata and artwork lookup from theTVDB (using EPG zap2it IDs if available) and The Movie DB. -* Direct streaming (experimental, primarily tested with Plex Web and iOS). - ## Screenshots ![Plex Web Posters Screenshot](https://cloud.githubusercontent.com/assets/12835671/26337954/21753de4-3f42-11e7-895d-005c4da6b0a5.jpg) ![Plex Web Screenshot](https://cloud.githubusercontent.com/assets/12835671/25927053/c6212fda-35b8-11e7-98ca-ad636e62076e.jpg) @@ -48,15 +48,17 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Setup 1. [Download LiveTVH.bundle](https://github.com/taligentx/LiveTVH.bundle/releases/) and unzip to the [Plex Media Server/Plug-ins](https://support.plex.tv/hc/en-us/articles/201106098-How-do-I-find-the-Plug-Ins-folder-) directory. -2. Set the LiveTVH preferences with the Tvheadend LAN IP address/hostname (or WAN for remote access), username, and password. +2. From the Tvheadend web interface, navigate to Configuration > Users > Passwords and create a user and password. +3. Navigate to Configuration > Users > Access Entries and create a new access entry. Select "Web interface", Streaming > "Basic", and Video recorder > "Basic". + + ![Tvheadend Access Entry screenshot](https://user-images.githubusercontent.com/12835671/42663549-95fdfd76-85fb-11e8-8b02-b2022d8c6cff.png) +4. Set the LiveTVH preferences with the Tvheadend LAN IP address/hostname (or WAN for remote access), username, and password. ![Prefs Screenshot](https://cloud.githubusercontent.com/assets/12835671/26337942/0a4d9724-3f42-11e7-9654-7c8e82e4877a.jpg) -3. Watch! +5. Watch! ## Notes -* Channels will take a bit of time to load initially while metadata is fetched and speed up over time as the cache is built up (up to 30 days). 20-30 channels per page works reasonably well. - -* While Tvheadend recordings can be played, managing new recordings will need to be handled outside of Plex, or by using [Plex DVR](https://www.plex.tv/features/dvr) and [tvhProxy](https://github.com/jkaberg/tvhProxy)). +* Channels will take a bit of time to load initially while metadata is fetched and speed up over time as the cache is built up and stored for 30 days. Up to 30 channels per page works reasonably well. * Direct streaming of channels on Plex Web, iOS, and Roku requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel (Tvheadend supports editing multiple selections to make this a quick update): * Video tags: `H264`, `MPEG2`, `HEVC`, `VP8`, `VP9` @@ -68,6 +70,8 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Radio (audio-only) channels are also identified using Tvheadend channel tags - create and set a `Radio` tag in Tvheadend on the appropriate channels for audio-only playback, as well as audio tags (for example, `AAC`) for direct streaming. +* While Tvheadend recordings can be played, managing new recordings will need to be handled outside of Plex, or by using [Plex DVR](https://www.plex.tv/features/dvr) and [tvhProxy](https://github.com/jkaberg/tvhProxy)). + * Watching remotely may require Tvheadend to have a public-facing address, as some clients will attempt to directly play the Tvheadend stream instead of running through the Plex transcoder. In this case, putting Tvheadend behind a [reverse proxy with SSL](https://www.nginx.com/resources/admin-guide/reverse-proxy/) is highly recommended, as the Tvheadend username and password is sent using HTTP Basic Authentication and is not secure over plain HTTP. From 470bd71e25f7ba60e15d6c597a4e3127732e7178 Mon Sep 17 00:00:00 2001 From: taligentx Date: Thu, 12 Jul 2018 18:00:49 -0500 Subject: [PATCH 05/15] Add documentation on necessary Tvheadend user access rights --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fad3cd6..e917eaf 100644 --- a/README.md +++ b/README.md @@ -49,13 +49,14 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Setup 1. [Download LiveTVH.bundle](https://github.com/taligentx/LiveTVH.bundle/releases/) and unzip to the [Plex Media Server/Plug-ins](https://support.plex.tv/hc/en-us/articles/201106098-How-do-I-find-the-Plug-Ins-folder-) directory. 2. From the Tvheadend web interface, navigate to Configuration > Users > Passwords and create a user and password. -3. Navigate to Configuration > Users > Access Entries and create a new access entry. Select "Web interface", Streaming > "Basic", and Video recorder > "Basic". +3. Navigate to Configuration > Users > Access Entries and create a new access entry. +4. Select "Web interface", Streaming > "Basic", and Video recorder > "Basic". ![Tvheadend Access Entry screenshot](https://user-images.githubusercontent.com/12835671/42663549-95fdfd76-85fb-11e8-8b02-b2022d8c6cff.png) -4. Set the LiveTVH preferences with the Tvheadend LAN IP address/hostname (or WAN for remote access), username, and password. +5. Set the LiveTVH preferences with the Tvheadend LAN IP address/hostname (or WAN for remote access), username, and password. ![Prefs Screenshot](https://cloud.githubusercontent.com/assets/12835671/26337942/0a4d9724-3f42-11e7-9654-7c8e82e4877a.jpg) -5. Watch! +6. Watch! ## Notes * Channels will take a bit of time to load initially while metadata is fetched and speed up over time as the cache is built up and stored for 30 days. Up to 30 channels per page works reasonably well. From 9d78cca3667bb6b61e0b5d0979c0d3f1a7a6f228 Mon Sep 17 00:00:00 2001 From: taligentx Date: Tue, 17 Jul 2018 22:44:16 -0500 Subject: [PATCH 06/15] Add timeouts to fix crash if thetvdb.com is unreachable --- Contents/Code/__init__.py | 27 +++++++++++++++++---------- README.md | 9 +++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index ff02b6f..6b954d6 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -18,6 +18,7 @@ epgCacheTime = 4200 imageCacheTime = CACHE_1MONTH tvdbRetryInterval = CACHE_1MONTH +httpTimeout = 3 # /Preferences @@ -236,7 +237,7 @@ def MainMenu(): else: epgEncoding = 'latin-1' - rawEPGData = HTTP.Request(url=tvhEPGURL, headers=tvhHeaders, cacheTime=epgCacheTime, encoding=epgEncoding, values=None).content + rawEPGData = HTTP.Request(url=tvhEPGURL, headers=tvhHeaders, timeout=httpTimeout, cacheTime=epgCacheTime, encoding=epgEncoding, values=None).content rawEPGData = re.sub(r'[\x00-\x1f]', '', rawEPGData) # Strip control characters from EPG data (yep, this has actually happened) tvhEPGData = JSON.ObjectFromString(rawEPGData, encoding='utf-8', max_size=20971520) if tvhEPGData: break @@ -786,7 +787,7 @@ def stream(streamURL): testURL = '{}{}'.format(tvhAddress, streamURL) try: - responseCode = HTTP.Request(testURL, headers=tvhHeaders, values=None, cacheTime=None, timeout=2).headers + responseCode = HTTP.Request(testURL, headers=tvhHeaders, values=None, cacheTime=None, timeout=httpTimeout).headers return IndirectResponse(MovieObject, key=playbackURL) except Exception as e: @@ -997,9 +998,9 @@ def image(url=None, fallback=None): elif fallback: if tvhAddress in fallback: - imageContent = HTTP.Request(url=fallback, headers=tvhHeaders, cacheTime=imageCacheTime, values=None).content + imageContent = HTTP.Request(url=fallback, headers=tvhHeaders, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content else: - imageContent = HTTP.Request(url=fallback, cacheTime=imageCacheTime, values=None).content + imageContent = HTTP.Request(url=fallback, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content return DataObject(imageContent, 'image/jpeg') @@ -1009,7 +1010,7 @@ def image(url=None, fallback=None): for tvdbImageResult in tvdbImageData['data']: url = 'http://thetvdb.com/banners/' + str(tvdbImageResult['fileName']) try: - imageContent = HTTP.Request(url, cacheTime=imageCacheTime, values=None).content + imageContent = HTTP.Request(url, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content return DataObject(imageContent, 'image/jpeg') except Exception as e: Log.Warn('Error retrieving image: ' + str(e)) @@ -1017,7 +1018,7 @@ def image(url=None, fallback=None): elif tvhAddress in url: try: - imageContent = HTTP.Request(url=url, headers=tvhHeaders, cacheTime=imageCacheTime, values=None).content + imageContent = HTTP.Request(url=url, headers=tvhHeaders, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content return DataObject(imageContent, 'image/jpeg') except Exception as e: Log.Warn('Error retrieving image: ' + str(e)) @@ -1028,7 +1029,7 @@ def image(url=None, fallback=None): else: try: - imageContent = HTTP.Request(url, cacheTime=imageCacheTime, values=None).content + imageContent = HTTP.Request(url, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content return DataObject(imageContent, 'image/jpeg') except Exception as e: Log.Warn('Error retrieving image: ' + str(e)) @@ -1093,11 +1094,11 @@ def tvdbAuth(): tvdbHeaders = {'content-type': 'application/json'} try: - tvdbResponse = HTTP.Request(url=tvdbLoginURL, headers=tvdbHeaders, data=tvdbApiKeyJSON, cacheTime=1).content + tvdbResponse = HTTP.Request(url=tvdbLoginURL, headers=tvdbHeaders, timeout=httpTimeout, data=tvdbApiKeyJSON, cacheTime=1).content tvdbTokenData = JSON.ObjectFromString(tvdbResponse) tvdbToken = tvdbTokenData['token'] - except Ex.HTTPError as e: + except Exception as e: Log.Warn('Failed to retrieve theTVDB authorization token: ' + str(e)) tvdbToken = False @@ -1145,7 +1146,13 @@ def tvdb(title, zap2itID, zap2itMissingID=None): elif not tvdbToken: Log.Info('theTVDB authorization failed.') - return {'poster': tvdbPosterSearchURL, 'fanart': tvdbFanartSearchURL} + return { + 'poster': tvdbPosterSearchURL, + 'fanart': tvdbFanartSearchURL, + 'rating': tvdbRating, + 'siteRating': tvdbSiteRating, + 'genres': tvdbGenres, + 'zap2itMissingID': zap2itMissingID} # Search using zap2it ID if available, otherwise search by name tvdbHeaders = {'Authorization' : 'Bearer %s' % tvdbToken} diff --git a/README.md b/README.md index e917eaf..095893a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set + * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) * This pushes the pre-existing changes in the develop branch to master as a release. @@ -47,9 +48,9 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ![Plex iOS Screenshot](https://cloud.githubusercontent.com/assets/12835671/25927072/dbecdd3c-35b8-11e7-80d9-056e59088501.jpg) ## Setup -1. [Download LiveTVH.bundle](https://github.com/taligentx/LiveTVH.bundle/releases/) and unzip to the [Plex Media Server/Plug-ins](https://support.plex.tv/hc/en-us/articles/201106098-How-do-I-find-the-Plug-Ins-folder-) directory. -2. From the Tvheadend web interface, navigate to Configuration > Users > Passwords and create a user and password. -3. Navigate to Configuration > Users > Access Entries and create a new access entry. +1. [Download LiveTVH.bundle](https://github.com/taligentx/LiveTVH.bundle/releases/) and unzip to the [Plex Media Server/Plug-ins](https://support.plex.tv/hc/en-us/articles/201106098-How-do-I-find-the-Plug-Ins-folder-) directory. Alternatively, `git clone` this respository to the Plug-ins directory to keep track of the latest changes. +2. Open the Tvheadend web interface and navigate to Configuration > Users > Passwords. Create a user and password. +3. Navigate to Configuration > Users > Access Entries and create a new access entry for the user. 4. Select "Web interface", Streaming > "Basic", and Video recorder > "Basic". ![Tvheadend Access Entry screenshot](https://user-images.githubusercontent.com/12835671/42663549-95fdfd76-85fb-11e8-8b02-b2022d8c6cff.png) @@ -61,7 +62,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Notes * Channels will take a bit of time to load initially while metadata is fetched and speed up over time as the cache is built up and stored for 30 days. Up to 30 channels per page works reasonably well. -* Direct streaming of channels on Plex Web, iOS, and Roku requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel (Tvheadend supports editing multiple selections to make this a quick update): +* Direct streaming of channels on Plex Web, iOS, and Roku requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel (note that Tvheadend supports editing multiple selections at the same time): * Video tags: `H264`, `MPEG2`, `HEVC`, `VP8`, `VP9` * Audio tags: `AAC`, `AAC-LATM`, `AC3`, `EAC3`, `MP2`, `MP3`, `VORBIS` * Video and audio tags may be combined into single tags: `H264-AAC`, `H264-MP2`, etc. From d909f047e5b2d56a113783b7e73d38e9494be5b8 Mon Sep 17 00:00:00 2001 From: taligentx Date: Thu, 19 Jul 2018 11:32:45 -0500 Subject: [PATCH 07/15] Note supported versions of Tvheadend --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 095893a..71ee79f 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,15 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](https://tvheadend.org), including metadata from Tvheadend's EPG, [theTVDB](https://thetvdb.com), and [The Movie DB](https://www.themoviedb.org). ## Features -* Playback of Tvheadend video channels, audio channels, and recordings. -* EPG displayed as a simple list within each channel description. -* Metadata and artwork lookup from theTVDB (using EPG zap2it IDs if available) and The Movie DB. -* Direct streaming (experimental, primarily tested with Plex Web and iOS). +* Plays all Tvheadend video channels, audio channels, and recordings. Direct streaming is possible by setting the codec and resolution of channels using Tvheadend channel tags. +* Displays the Tvheadend EPG for channels through the Plex channel description. +* Displays metadata and artwork from theTVDB (using EPG zap2it IDs if available) and The Movie DB. +* Supports Tvheadend 4.2. The Tvheadend unstable branch is not currently supported [due to a bug](https://github.com/taligentx/LiveTVH.bundle/issues/23). ## Release notes * LiveTVH 1.4-develop * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming + * Updated: Added note on supported versions of Tvheadend * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable @@ -62,7 +63,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Notes * Channels will take a bit of time to load initially while metadata is fetched and speed up over time as the cache is built up and stored for 30 days. Up to 30 channels per page works reasonably well. -* Direct streaming of channels on Plex Web, iOS, and Roku requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel (note that Tvheadend supports editing multiple selections at the same time): +* Direct streaming of channels on Plex Web, iOS, and Roku requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel: * Video tags: `H264`, `MPEG2`, `HEVC`, `VP8`, `VP9` * Audio tags: `AAC`, `AAC-LATM`, `AC3`, `EAC3`, `MP2`, `MP3`, `VORBIS` * Video and audio tags may be combined into single tags: `H264-AAC`, `H264-MP2`, etc. From 87f1033135dd0273bc4448fc1608592d6dcc1ee0 Mon Sep 17 00:00:00 2001 From: taligentx Date: Thu, 19 Jul 2018 11:37:58 -0500 Subject: [PATCH 08/15] Note supported versions of Tvheadend --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71ee79f..7a42010 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Plays all Tvheadend video channels, audio channels, and recordings. Direct streaming is possible by setting the codec and resolution of channels using Tvheadend channel tags. * Displays the Tvheadend EPG for channels through the Plex channel description. * Displays metadata and artwork from theTVDB (using EPG zap2it IDs if available) and The Movie DB. -* Supports Tvheadend 4.2. The Tvheadend unstable branch is not currently supported [due to a bug](https://github.com/taligentx/LiveTVH.bundle/issues/23). +* Supports Tvheadend stable versions (currently 4.2). The Tvheadend unstable branch is not currently supported [due to a bug](https://github.com/taligentx/LiveTVH.bundle/issues/23). ## Release notes * LiveTVH 1.4-develop From a2bfe87748815e84e152c126be6ab2a21640bcce Mon Sep 17 00:00:00 2001 From: taligentx Date: Thu, 19 Jul 2018 12:05:51 -0500 Subject: [PATCH 09/15] Display error message if recordings data is invalid (#25) --- Contents/Code/__init__.py | 6 ++++++ Contents/Strings/en.json | 1 + README.md | 1 + 3 files changed, 8 insertions(+) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 6b954d6..97bee09 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -810,6 +810,12 @@ def recordings(tvhVideoTags, tvhAudioTags, startCount=0): except Exception as e: Log.Warn('Error retrieving Tvheadend recordings data: ' + str(e)) + # Display an error message to clients if there was an error retrieving recordings data + if tvhRecordingsData is None: + errorContainer = ObjectContainer(title1=TITLE, no_cache=True) + errorContainer.add(DirectoryObject(title=L('recordingsUnavailable'))) + return errorContainer + # Request channel data from Tvheadend tvhChannelsData = None tvhChannelsURL = str(tvhAddress) + '/api/channel/grid?start=0&limit=100000' diff --git a/Contents/Strings/en.json b/Contents/Strings/en.json index e6e94b9..fca4c99 100644 --- a/Contents/Strings/en.json +++ b/Contents/Strings/en.json @@ -1,6 +1,7 @@ { "preferences": "Preferences", "channelsUnavailable": "No channels available, verify settings.", + "recordingsUnavailable": "No recordings available, check logs for errors.", "tvhAddress": "Tvheadend address:port (with webroot if applicable):", "tvhUser": "Username:", "tvhPass": "Password:", diff --git a/README.md b/README.md index 7a42010..cbfb572 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable + * Bugfix: Plugin failed to respond when accessing recordings if Tvheadend recordings data is invalid * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) * This pushes the pre-existing changes in the develop branch to master as a release. From fe5db9bb271e8ec00f730d1d6a25b2ca15883890 Mon Sep 17 00:00:00 2001 From: taligentx Date: Fri, 20 Jul 2018 15:22:24 -0500 Subject: [PATCH 10/15] Add recordings data fallback from UTF-8 to ISO-8859-1 for invalid UTF8 characters #25 --- Contents/Code/__init__.py | 56 ++++++++++++++++++++++++++++++--------- README.md | 1 + 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 97bee09..8e594bf 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -198,17 +198,33 @@ def MainMenu(): except Exception as e: Log.Warn('Error parsing Tvheadend channel tags data: ' + str(e)) - # Request recordings from Tvheadend + # Request recordings data as UTF-8 with fallback to ISO-8859-1 tvhRecordingsData = None tvhRecordingsURL = str(tvhAddress) + '/api/dvr/entry/grid_finished' + recordingsUTF8Encoding = True - try: - tvhRecordingsData = JSON.ObjectFromURL(url=tvhRecordingsURL, headers=tvhHeaders, values=None, cacheTime=channelDataCacheTime) - if int(tvhRecordingsData['total']) == 0: - tvhRecordingsData = None + while True: + try: + if recordingsUTF8Encoding: + recordingsEncoding = 'utf-8' + else: + recordingsEncoding = 'latin-1' - except Exception as e: - Log.Warn('Error retrieving Tvheadend recordings data: ' + str(e)) + rawRecordingsData = HTTP.Request(url=tvhRecordingsURL, headers=tvhHeaders, timeout=httpTimeout, cacheTime=channelDataCacheTime, encoding=recordingsEncoding, values=None).content + rawRecordingsData = re.sub(r'[\x00-\x1f]', '', rawRecordingsData) # Strip control characters from recordings data (yep, this has actually happened) + tvhRecordingsData = JSON.ObjectFromString(rawRecordingsData, encoding='utf-8', max_size=20971520) + if tvhRecordingsData: break + + except Exception as e: + if recordingsUTF8Encoding: + Log.Warn('Unable to retrieve Tvheadend recordings data as UTF-8, falling back to ISO-8859-1: ' + str(e)) + recordingsUTF8Encoding = False + else: + Log.Warn('Error retrieving Tvheadend recordings data: ' + str(e)) + break + + if int(tvhRecordingsData['total']) == 0: + tvhRecordingsData = None # Set the number of EPG items to retrieve tvhEPGData = None @@ -801,14 +817,30 @@ def recordings(tvhVideoTags, tvhAudioTags, startCount=0): nextStartCount = startCount + int(Prefs['prefPageCount']) recordingsContainer = ObjectContainer(title1=L('recordings'), no_cache=True) - # Request recordings from Tvheadend + # Request recordings data as UTF-8 with fallback to ISO-8859-1 tvhRecordingsData = None tvhRecordingsURL = str(tvhAddress) + '/api/dvr/entry/grid_finished' + recordingsUTF8Encoding = True - try: - tvhRecordingsData = JSON.ObjectFromURL(url=tvhRecordingsURL, headers=tvhHeaders, values=None, cacheTime=channelDataCacheTime) - except Exception as e: - Log.Warn('Error retrieving Tvheadend recordings data: ' + str(e)) + while True: + try: + if recordingsUTF8Encoding: + recordingsEncoding = 'utf-8' + else: + recordingsEncoding = 'latin-1' + + rawRecordingsData = HTTP.Request(url=tvhRecordingsURL, headers=tvhHeaders, timeout=httpTimeout, cacheTime=channelDataCacheTime, encoding=recordingsEncoding, values=None).content + rawRecordingsData = re.sub(r'[\x00-\x1f]', '', rawRecordingsData) # Strip control characters from recordings data (yep, this has actually happened) + tvhRecordingsData = JSON.ObjectFromString(rawRecordingsData, encoding='utf-8', max_size=20971520) + if tvhRecordingsData: break + + except Exception as e: + if recordingsUTF8Encoding: + Log.Warn('Unable to retrieve Tvheadend recordings data as UTF-8, falling back to ISO-8859-1: ' + str(e)) + recordingsUTF8Encoding = False + else: + Log.Warn('Error retrieving Tvheadend recordings data: ' + str(e)) + break # Display an error message to clients if there was an error retrieving recordings data if tvhRecordingsData is None: diff --git a/README.md b/README.md index cbfb572..dee6909 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable * Bugfix: Plugin failed to respond when accessing recordings if Tvheadend recordings data is invalid + * Bugfix: Recordings failed to display with invalid UTF-8 characters, added fallback to display as ISO-8859-1 characters * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) * This pushes the pre-existing changes in the develop branch to master as a release. From ee4b8d9910194b3a0644f3bc531c7b7dd00fcd92 Mon Sep 17 00:00:00 2001 From: taligentx Date: Tue, 24 Jul 2018 02:07:01 -0500 Subject: [PATCH 11/15] Change HTTP auth to Digest (#23) --- Contents/Code/__init__.py | 66 ++++++++++++++++++++++----------------- README.md | 4 +-- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 8e594bf..4e4d9d9 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -1,9 +1,9 @@ # LiveTVH - Live TV streaming for Plex via Tvheadend # https://github.com/taligentx/LiveTVH -import base64 import time import re +import urllib2 # Preferences # @@ -14,8 +14,6 @@ improveTheTVDB = True # Cache times -channelDataCacheTime = 60 -epgCacheTime = 4200 imageCacheTime = CACHE_1MONTH tvdbRetryInterval = CACHE_1MONTH httpTimeout = 3 @@ -27,7 +25,7 @@ PREFIX = '/video/livetvh' THUMB = 'icon-default.png' ART = 'art-default.jpg' -tvhHeaders = None + tvhAddress = None tvhReachable = False tvdbToken = None @@ -50,21 +48,33 @@ def ValidatePrefs(): # Setup authorization and configuration data @route(PREFIX + '/setprefs') def setPrefs(): - global tvhHeaders global tvhAddress global tvhReachable global tvdbToken global tmdbBaseURL global tmdbGenreData - - # Set Tvheadend authorization and verify connectivity to Tvheadend - tvhAuth = base64.b64encode('{}:{}'.format(Prefs['tvhUser'], Prefs['tvhPass'])) - tvhHeaders = {'Authorization': 'Basic ' + str(tvhAuth)} + tvhRealm = None tvhAddress = Prefs['tvhAddress'].rstrip('/') tvhServerInfoURL = str(tvhAddress) + '/api/serverinfo' + # Gets the Tvheadend HTTP authentication realm try: - tvhInfoData = JSON.ObjectFromURL(url=tvhServerInfoURL, headers=tvhHeaders, values=None, cacheTime=1) + response = urllib2.urlopen(tvhServerInfoURL).info() + except urllib2.HTTPError as e: + tvhRealmData = re.search("realm=\"\w+", e.info().getheader('WWW-Authenticate')).group(0).split('"') + tvhRealm = tvhRealmData[1] + except Exception as e: + Log.Info('Error accessing Tvheadend: ' + str(e)) + + # Sets the Tvheadend HTTP authentication type as Digest + tvhAuth = urllib2.HTTPDigestAuthHandler() + tvhAuth.add_password(tvhRealm, tvhAddress, Prefs['tvhUser'], Prefs['tvhPass']) + tvhOpen = urllib2.build_opener(tvhAuth) + urllib2.install_opener(tvhOpen) + + # Checks for connectivity to Tvheadend + try: + tvhInfoData = JSON.ObjectFromString(urllib2.urlopen(tvhServerInfoURL).read()) Log.Info('Tvheadend version: ' + tvhInfoData['sw_version']) if tvhInfoData['api_version'] >= 15: @@ -78,12 +88,12 @@ def setPrefs(): tvhReachable = False return - # Renew theTVDB authorization token if necessary + # Renews theTVDB authorization token if necessary if Prefs['prefMetadata'] and tvdbToken: tvdbToken = None tvdbAuth() - # Retrieve themovieDB base URL for images and genre list + # Retrieves themovieDB base URL for images and genre list if Prefs['prefMetadata']: tmdbConfigURL = 'https://api.themoviedb.org/3/configuration?api_key=0fd2136e80c47d0e371ee1af87eaedde' tmdbGenreURL = 'https://api.themoviedb.org/3/genre/movie/list?api_key=0fd2136e80c47d0e371ee1af87eaedde' @@ -97,7 +107,7 @@ def setPrefs(): Log.Warn('Error accessing themovieDB: ' + str(e)) -# Build the main menu +# Builds the main menu @handler(PREFIX, TITLE) def MainMenu(): @@ -106,29 +116,29 @@ def MainMenu(): Log.Debug('Platform: ' + str(Client.Platform)) Log.Debug('OS: ' + str(Platform.OS) + ' ' + str(Platform.CPU)) - # Request channel data from Tvheadend + # Requests channel data from Tvheadend tvhChannelsData = None tvhChannelsURL = str(tvhAddress) + '/api/channel/grid?start=0&limit=100000' if tvhReachable: try: - tvhChannelsData = JSON.ObjectFromURL(url=tvhChannelsURL, headers=tvhHeaders, values=None, cacheTime=channelDataCacheTime) + tvhChannelsData = JSON.ObjectFromString(urllib2.urlopen(tvhChannelsURL).read()) except Exception as e: Log.Critical('Error retrieving Tvheadend channel data: ' + str(e)) - # Display an error message to clients if Tvheadend is malfunctional + # Displays an error message to clients if Tvheadend is malfunctional if tvhChannelsData is None: errorContainer = ObjectContainer(title1=TITLE, no_cache=True) errorContainer.add(DirectoryObject(title=L('channelsUnavailable'))) return errorContainer - # Request and set channel tags from Tvheadend + # Requests and sets channel tags from Tvheadend # Tags are used as a manual method to identify video/audio attributes for each channel tvhTagsData = None tvhTagsURL = str(tvhAddress) + '/api/channeltag/grid?start=0&limit=100000' try: - tvhTagsData = JSON.ObjectFromURL(url=tvhTagsURL, headers=tvhHeaders, values=None, cacheTime=channelDataCacheTime) + tvhTagsData = JSON.ObjectFromString(urllib2.urlopen(tvhTagsURL).read()) if debug: Log.Debug('tvhTagsData: ' + str(tvhTagsData)) except Exception as e: Log.Warn('Error retrieving Tvheadend channel tags data: ' + str(e)) @@ -210,9 +220,9 @@ def MainMenu(): else: recordingsEncoding = 'latin-1' - rawRecordingsData = HTTP.Request(url=tvhRecordingsURL, headers=tvhHeaders, timeout=httpTimeout, cacheTime=channelDataCacheTime, encoding=recordingsEncoding, values=None).content + rawRecordingsData = urllib2.urlopen(tvhRecordingsURL).read() rawRecordingsData = re.sub(r'[\x00-\x1f]', '', rawRecordingsData) # Strip control characters from recordings data (yep, this has actually happened) - tvhRecordingsData = JSON.ObjectFromString(rawRecordingsData, encoding='utf-8', max_size=20971520) + tvhRecordingsData = JSON.ObjectFromString(rawRecordingsData, encoding=recordingsEncoding, max_size=20971520) if tvhRecordingsData: break except Exception as e: @@ -253,9 +263,9 @@ def MainMenu(): else: epgEncoding = 'latin-1' - rawEPGData = HTTP.Request(url=tvhEPGURL, headers=tvhHeaders, timeout=httpTimeout, cacheTime=epgCacheTime, encoding=epgEncoding, values=None).content + rawEPGData = urllib2.urlopen(tvhEPGURL).read() rawEPGData = re.sub(r'[\x00-\x1f]', '', rawEPGData) # Strip control characters from EPG data (yep, this has actually happened) - tvhEPGData = JSON.ObjectFromString(rawEPGData, encoding='utf-8', max_size=20971520) + tvhEPGData = JSON.ObjectFromString(rawEPGData, encoding=epgEncoding, max_size=20971520) if tvhEPGData: break except Exception as e: @@ -803,7 +813,7 @@ def stream(streamURL): testURL = '{}{}'.format(tvhAddress, streamURL) try: - responseCode = HTTP.Request(testURL, headers=tvhHeaders, values=None, cacheTime=None, timeout=httpTimeout).headers + tvhResponse = urllib2.urlopen(testURL).getcode() return IndirectResponse(MovieObject, key=playbackURL) except Exception as e: @@ -829,9 +839,9 @@ def recordings(tvhVideoTags, tvhAudioTags, startCount=0): else: recordingsEncoding = 'latin-1' - rawRecordingsData = HTTP.Request(url=tvhRecordingsURL, headers=tvhHeaders, timeout=httpTimeout, cacheTime=channelDataCacheTime, encoding=recordingsEncoding, values=None).content + rawRecordingsData = urllib2.urlopen(tvhRecordingsURL).read() rawRecordingsData = re.sub(r'[\x00-\x1f]', '', rawRecordingsData) # Strip control characters from recordings data (yep, this has actually happened) - tvhRecordingsData = JSON.ObjectFromString(rawRecordingsData, encoding='utf-8', max_size=20971520) + tvhRecordingsData = JSON.ObjectFromString(rawRecordingsData, encoding=recordingsEncoding, max_size=20971520) if tvhRecordingsData: break except Exception as e: @@ -853,7 +863,7 @@ def recordings(tvhVideoTags, tvhAudioTags, startCount=0): tvhChannelsURL = str(tvhAddress) + '/api/channel/grid?start=0&limit=100000' try: - tvhChannelsData = JSON.ObjectFromURL(url=tvhChannelsURL, headers=tvhHeaders, values=None, cacheTime=channelDataCacheTime) + tvhChannelsData = JSON.ObjectFromString(urllib2.urlopen(tvhChannelsURL).read()) except Exception as e: Log.Critical('Error retrieving Tvheadend channel data: ' + str(e)) @@ -1036,7 +1046,7 @@ def image(url=None, fallback=None): elif fallback: if tvhAddress in fallback: - imageContent = HTTP.Request(url=fallback, headers=tvhHeaders, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content + imageContent = urllib2.urlopen(fallback).read() else: imageContent = HTTP.Request(url=fallback, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content @@ -1056,7 +1066,7 @@ def image(url=None, fallback=None): elif tvhAddress in url: try: - imageContent = HTTP.Request(url=url, headers=tvhHeaders, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content + imageContent = urllib2.urlopen(url).read() return DataObject(imageContent, 'image/jpeg') except Exception as e: Log.Warn('Error retrieving image: ' + str(e)) diff --git a/README.md b/README.md index dee6909..83951ea 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Plays all Tvheadend video channels, audio channels, and recordings. Direct streaming is possible by setting the codec and resolution of channels using Tvheadend channel tags. * Displays the Tvheadend EPG for channels through the Plex channel description. * Displays metadata and artwork from theTVDB (using EPG zap2it IDs if available) and The Movie DB. -* Supports Tvheadend stable versions (currently 4.2). The Tvheadend unstable branch is not currently supported [due to a bug](https://github.com/taligentx/LiveTVH.bundle/issues/23). +* Supports Tvheadend stable versions 4.2.x and unstable development versions 4.3.x. ## Release notes * LiveTVH 1.4-develop * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming - * Updated: Added note on supported versions of Tvheadend + * Updated: Tvheadend HTTP authentication type changed from Plain to Digest to support unstable Tvheadend 4.3's default authentication settings * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable From c6c2b47de71b492bc2bfbab3e0a3970eb4465333 Mon Sep 17 00:00:00 2001 From: taligentx Date: Tue, 24 Jul 2018 10:59:08 -0500 Subject: [PATCH 12/15] Support both basic and digest authentication (#26) --- Contents/Code/__init__.py | 19 ++++++++++++------- README.md | 8 +++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 4e4d9d9..108c1cc 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -54,23 +54,28 @@ def setPrefs(): global tmdbBaseURL global tmdbGenreData tvhRealm = None + tvhAuthType = None + tvhAuthHandler = urllib2.HTTPBasicAuthHandler() tvhAddress = Prefs['tvhAddress'].rstrip('/') tvhServerInfoURL = str(tvhAddress) + '/api/serverinfo' - # Gets the Tvheadend HTTP authentication realm + # Gets the Tvheadend HTTP authentication type and realm try: response = urllib2.urlopen(tvhServerInfoURL).info() except urllib2.HTTPError as e: - tvhRealmData = re.search("realm=\"\w+", e.info().getheader('WWW-Authenticate')).group(0).split('"') + tvhAuthInfo = e.info().getheader('WWW-Authenticate') + if 'Digest' in tvhAuthInfo: + tvhAuthHandler = urllib2.HTTPDigestAuthHandler() + + tvhRealmData = re.search("realm=\"\w+", tvhAuthInfo).group(0).split('"') tvhRealm = tvhRealmData[1] except Exception as e: Log.Info('Error accessing Tvheadend: ' + str(e)) - # Sets the Tvheadend HTTP authentication type as Digest - tvhAuth = urllib2.HTTPDigestAuthHandler() - tvhAuth.add_password(tvhRealm, tvhAddress, Prefs['tvhUser'], Prefs['tvhPass']) - tvhOpen = urllib2.build_opener(tvhAuth) - urllib2.install_opener(tvhOpen) + # Sets the Tvheadend HTTP authentication data + tvhAuthHandler.add_password(tvhRealm, tvhAddress, Prefs['tvhUser'], Prefs['tvhPass']) + tvhOpener = urllib2.build_opener(tvhAuthHandler) + urllib2.install_opener(tvhOpener) # Checks for connectivity to Tvheadend try: diff --git a/README.md b/README.md index 83951ea..ebf6821 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,14 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Release notes * LiveTVH 1.4-develop + * New: Support for Tvheadend HTTP authentication type Digest * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming - * Updated: Tvheadend HTTP authentication type changed from Plain to Digest to support unstable Tvheadend 4.3's default authentication settings - * Bugfix: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) + * Updated: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable - * Bugfix: Plugin failed to respond when accessing recordings if Tvheadend recordings data is invalid - * Bugfix: Recordings failed to display with invalid UTF-8 characters, added fallback to display as ISO-8859-1 characters + * Bugfix: Plugin failed to respond when accessing recordings if the Tvheadend recordings data contains invalid UTF-8 characters, added fallback to display as ISO-8859-1 characters * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) - * This pushes the pre-existing changes in the develop branch to master as a release. * Updated: Tvheadend channel tags support additional codecs, resolutions, and radio (audio-only) channels * Updated: Changed image filenames to match Plex channel guidelines * Updated: Replaced deprecated string substitution per [#18](https://github.com/taligentx/LiveTVH.bundle/pull/18) From 1eb940271006fe4503c15b8771dbc8c24b619127 Mon Sep 17 00:00:00 2001 From: taligentx Date: Tue, 24 Jul 2018 17:23:44 -0500 Subject: [PATCH 13/15] Set HTTP digest authentication as default --- Contents/Code/__init__.py | 22 ++++++++++------------ Contents/DefaultPrefs.json | 2 +- Contents/Strings/en.json | 2 +- README.md | 11 ++++++----- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 108c1cc..e3f9c5b 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -45,7 +45,7 @@ def ValidatePrefs(): return True -# Setup authorization and configuration data +## Setup authorization and configuration data @route(PREFIX + '/setprefs') def setPrefs(): global tvhAddress @@ -55,7 +55,7 @@ def setPrefs(): global tmdbGenreData tvhRealm = None tvhAuthType = None - tvhAuthHandler = urllib2.HTTPBasicAuthHandler() + tvhAuthHandler = urllib2.HTTPDigestAuthHandler() tvhAddress = Prefs['tvhAddress'].rstrip('/') tvhServerInfoURL = str(tvhAddress) + '/api/serverinfo' @@ -64,19 +64,18 @@ def setPrefs(): response = urllib2.urlopen(tvhServerInfoURL).info() except urllib2.HTTPError as e: tvhAuthInfo = e.info().getheader('WWW-Authenticate') - if 'Digest' in tvhAuthInfo: - tvhAuthHandler = urllib2.HTTPDigestAuthHandler() - tvhRealmData = re.search("realm=\"\w+", tvhAuthInfo).group(0).split('"') - tvhRealm = tvhRealmData[1] + if 'Basic' in str(tvhAuthInfo): + tvhAuthHandler = urllib2.HTTPBasicAuthHandler() + + tvhRealm = (re.search("realm=\"[^\"]*", tvhAuthInfo).group(0).split('"'))[1] + tvhAuthHandler.add_password(tvhRealm, tvhAddress, Prefs['tvhUser'], Prefs['tvhPass']) + tvhOpener = urllib2.build_opener(tvhAuthHandler) + urllib2.install_opener(tvhOpener) + except Exception as e: Log.Info('Error accessing Tvheadend: ' + str(e)) - # Sets the Tvheadend HTTP authentication data - tvhAuthHandler.add_password(tvhRealm, tvhAddress, Prefs['tvhUser'], Prefs['tvhPass']) - tvhOpener = urllib2.build_opener(tvhAuthHandler) - urllib2.install_opener(tvhOpener) - # Checks for connectivity to Tvheadend try: tvhInfoData = JSON.ObjectFromString(urllib2.urlopen(tvhServerInfoURL).read()) @@ -91,7 +90,6 @@ def setPrefs(): except Exception as e: Log.Critical('Error accessing Tvheadend: ' + str(e)) tvhReachable = False - return # Renews theTVDB authorization token if necessary if Prefs['prefMetadata'] and tvdbToken: diff --git a/Contents/DefaultPrefs.json b/Contents/DefaultPrefs.json index 80cb4f9..3cdbfad 100644 --- a/Contents/DefaultPrefs.json +++ b/Contents/DefaultPrefs.json @@ -58,6 +58,6 @@ "id": "prefDirectStream", "label": "prefDirectStream", "type": "bool", - "default": "false" + "default": "true" } ] diff --git a/Contents/Strings/en.json b/Contents/Strings/en.json index fca4c99..cc02c38 100644 --- a/Contents/Strings/en.json +++ b/Contents/Strings/en.json @@ -11,7 +11,7 @@ "prefPageCount": "Number of channels to display per page:", "prefEPGCount": "Number of hours/entries of upcoming shows to display:", "prefMetadata": "Display artwork and metadata from theTVDB and The Movie DB", - "prefDirectStream": "Enable direct streaming (experimental)", + "prefDirectStream": "Enable direct streaming", "next": "Next...", "recordings": "Recordings" } \ No newline at end of file diff --git a/README.md b/README.md index ebf6821..a02ce6e 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,15 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](https://tvheadend.org), including metadata from Tvheadend's EPG, [theTVDB](https://thetvdb.com), and [The Movie DB](https://www.themoviedb.org). ## Features -* Plays all Tvheadend video channels, audio channels, and recordings. Direct streaming is possible by setting the codec and resolution of channels using Tvheadend channel tags. -* Displays the Tvheadend EPG for channels through the Plex channel description. +* Plays all Tvheadend video channels, audio channels, and recordings, including IPTV and ATSC/DVB sources. +* Direct streaming when the codec and resolution of channels are set using Tvheadend channel tags. +* Provides the Tvheadend EPG for channels in the Plex channel description. * Displays metadata and artwork from theTVDB (using EPG zap2it IDs if available) and The Movie DB. * Supports Tvheadend stable versions 4.2.x and unstable development versions 4.3.x. ## Release notes * LiveTVH 1.4-develop - * New: Support for Tvheadend HTTP authentication type Digest + * New: Support both plain and digest authentication for Tvheadend HTTP authentication * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming * Updated: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set @@ -63,10 +64,10 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Notes * Channels will take a bit of time to load initially while metadata is fetched and speed up over time as the cache is built up and stored for 30 days. Up to 30 channels per page works reasonably well. -* Direct streaming of channels on Plex Web, iOS, and Roku requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel: +* Direct streaming of channels on Plex Web, iOS, Roku, and Android requires identifying the channel's codecs and resolution using Tvheadend channel tags. Create and set channel tags in Tvheadend as appropriate for each channel: * Video tags: `H264`, `MPEG2`, `HEVC`, `VP8`, `VP9` * Audio tags: `AAC`, `AAC-LATM`, `AC3`, `EAC3`, `MP2`, `MP3`, `VORBIS` - * Video and audio tags may be combined into single tags: `H264-AAC`, `H264-MP2`, etc. + * Video and audio tags may be combined into single tags: `MPEG2-AC3`, `H264-AAC`, etc. * Video resolution tags: `HDTV`, `720p`, `SDTV` ![Tvheadend Channel Tags Screenshot](https://cloud.githubusercontent.com/assets/12835671/26338051/e0cb75dc-3f42-11e7-85a0-7af80e425a21.png) From 823d6414c63d6837128a1e14b9016dcfc6f0641c Mon Sep 17 00:00:00 2001 From: taligentx Date: Tue, 24 Jul 2018 21:17:00 -0500 Subject: [PATCH 14/15] Add HTTPS artwork fallback to unverified SSL for Plex framework SSL handshake bug --- Contents/Code/__init__.py | 30 +++++++++++++++++++++++++----- README.md | 20 ++++++++++---------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index e3f9c5b..76d13b1 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -4,6 +4,7 @@ import time import re import urllib2 +import ssl # Preferences # @@ -31,6 +32,9 @@ tvdbToken = None tmdbBaseURL = None tmdbGenreData = None +unverifiedSSL = ssl.create_default_context() +unverifiedSSL.check_hostname = False +unverifiedSSL.verify_mode = ssl.CERT_NONE debug = True @@ -45,7 +49,7 @@ def ValidatePrefs(): return True -## Setup authorization and configuration data +# Setup authorization and configuration data @route(PREFIX + '/setprefs') def setPrefs(): global tvhAddress @@ -513,7 +517,7 @@ def channels(startCount=0, art=ART): # Check the EPG entry for a thumbnail if tvhEPGEntry.get('image') and tvhEPGEntry['image'].startswith('http'): - epgThumb = tvhEPGEntry['image'].replace('https://', 'http://') + epgThumb = tvhEPGEntry['image'] # Use EPG thumbnails from Tvheadend if a thumbnail is not available from the metadata providers if thumb is None and epgThumb: @@ -1044,6 +1048,7 @@ def image(url=None, fallback=None): except Ex.HTTPError as e: if e.code == 404: + Log.Info('Missing artwork in theTVDB, fallback: ' + str(fallback)) if fallback == R(ART): return Redirect(R(ART)) @@ -1051,7 +1056,16 @@ def image(url=None, fallback=None): if tvhAddress in fallback: imageContent = urllib2.urlopen(fallback).read() else: - imageContent = HTTP.Request(url=fallback, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content + try: + imageContent = HTTP.Request(url=fallback, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content + except Exception as e: + try: + if 'https' in fallback: + imageContent = urllib2.urlopen(fallback, context=unverifiedSSL).read() + Log.Info('Falling back to unverified SSL: ' + fallback) + + except Exception as e: + Log.Warn('Error retrieving fallback image: ' + str(e)) return DataObject(imageContent, 'image/jpeg') @@ -1083,8 +1097,14 @@ def image(url=None, fallback=None): imageContent = HTTP.Request(url, timeout=httpTimeout, cacheTime=imageCacheTime, values=None).content return DataObject(imageContent, 'image/jpeg') except Exception as e: - Log.Warn('Error retrieving image: ' + str(e)) - return None + try: + if 'https' in url: + imageContent = urllib2.urlopen(url, context=unverifiedSSL).read() + Log.Info('Falling back to unverified SSL: ' + url) + return DataObject(imageContent, 'image/jpeg') + except Exception as e: + Log.Warn('Error retrieving image: ' + str(e)) + return None # Search for metadata diff --git a/README.md b/README.md index a02ce6e..d6a01fd 100644 --- a/README.md +++ b/README.md @@ -11,25 +11,25 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h ## Release notes * LiveTVH 1.4-develop * New: Support both plain and digest authentication for Tvheadend HTTP authentication - * Updated: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming - * Updated: Force plain HTTP connections for HTTPS EPG thumbnail URLs - required due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) + * Changed: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming + * Changed: Channel artwork via HTTPS now falls back to SSL without authentication due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable - * Bugfix: Plugin failed to respond when accessing recordings if the Tvheadend recordings data contains invalid UTF-8 characters, added fallback to display as ISO-8859-1 characters + * Bugfix: Plugin failed to respond when accessing recordings if the Tvheadend recordings data contains invalid UTF-8 characters, added fallback to ISO-8859-1 characters * 2018.07.08 - [LiveTVH 1.3](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.3) - * Updated: Tvheadend channel tags support additional codecs, resolutions, and radio (audio-only) channels - * Updated: Changed image filenames to match Plex channel guidelines - * Updated: Replaced deprecated string substitution per [#18](https://github.com/taligentx/LiveTVH.bundle/pull/18) + * Changed: Tvheadend channel tags support additional codecs, resolutions, and radio (audio-only) channels + * Changed: Changed image filenames to match Plex channel guidelines + * Changed: Replaced deprecated string substitution per [#18](https://github.com/taligentx/LiveTVH.bundle/pull/18) * 2017.05.22 - [LiveTVH 1.2](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.2) * New: Paginated channel lists with configurable # of items per page - this helps with longer channel lists (a necessity for IPTV providers with thousands of channels). * New: Tvheadend recordings for playback - located at the end of the first page of the channel list (a display bug with several Plex clients prevents placing it at the beginning of the list). * New: Codec identification using Tvheadend channel tags (experimental). This can enable direct streaming for H264-AAC streams on some clients (see setup notes below). - * Updated: EPG parser to improve support for IPTV sources, including using images for a show if specified in the EPG (if other metadata providers are not available or are missing artwork). - * Updated: EPG item limit to 20k items/20MB (again, for IPTV sources). - * Updated: Plex clients will now display channel thumbnails as video clip objects (widescreen thumbnails) if metadata providers are disabled. - * Updated: Code housekeeping (partially PEP8-conformant) + * Changed: EPG parser to improve support for IPTV sources, including using images for a show if specified in the EPG (if other metadata providers are not available or are missing artwork). + * Changed: EPG item limit to 20k items/20MB (again, for IPTV sources). + * Changed: Plex clients will now display channel thumbnails as video clip objects (widescreen thumbnails) if metadata providers are disabled. + * Changed: Code housekeeping (partially PEP8-conformant) * Bugfix: transcoding quality options not visible during playback * Bugfix: episode names from EPG were not set on Plex for Android From 337f47f25a66d6456cdf3e9f3c50f4d322382a3e Mon Sep 17 00:00:00 2001 From: taligentx Date: Wed, 25 Jul 2018 13:55:53 -0500 Subject: [PATCH 15/15] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6a01fd..aea2c49 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Supports Tvheadend stable versions 4.2.x and unstable development versions 4.3.x. ## Release notes -* LiveTVH 1.4-develop +* 2018.07.25 - [LiveTVH 1.4](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.4) * New: Support both plain and digest authentication for Tvheadend HTTP authentication - * Changed: Plex Web no longer direct streams 256kbps audio, changed specified audio bitrate to re-enable audio direct streaming - * Changed: Channel artwork via HTTPS now falls back to SSL without authentication due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) + * New: Channel artwork via HTTPS now falls back to SSL without authentication if necessary due to a [Plex issue](https://forums.plex.tv/t/https-broken/216635/8) + * Changed: Plex Web no longer supports direct streaming 256kbps audio, lowered specified audio bitrate for audio direct streaming * Bugfix: Recordings failed to display when resolution was not set * Bugfix: Plugin failed to respond if theTVDB metadata is enabled and thetvdb.com is unreachable * Bugfix: Plugin failed to respond when accessing recordings if the Tvheadend recordings data contains invalid UTF-8 characters, added fallback to ISO-8859-1 characters @@ -23,7 +23,7 @@ LiveTVH provides live TV streaming for [Plex](https://plex.tv) via [Tvheadend](h * Changed: Replaced deprecated string substitution per [#18](https://github.com/taligentx/LiveTVH.bundle/pull/18) * 2017.05.22 - [LiveTVH 1.2](https://github.com/taligentx/LiveTVH.bundle/releases/tag/v1.2) - * New: Paginated channel lists with configurable # of items per page - this helps with longer channel lists (a necessity for IPTV providers with thousands of channels). + * New: Paginated channel lists with a configurable number of items per page - this helps with longer channel lists (a necessity for IPTV providers with thousands of channels). * New: Tvheadend recordings for playback - located at the end of the first page of the channel list (a display bug with several Plex clients prevents placing it at the beginning of the list). * New: Codec identification using Tvheadend channel tags (experimental). This can enable direct streaming for H264-AAC streams on some clients (see setup notes below). * Changed: EPG parser to improve support for IPTV sources, including using images for a show if specified in the EPG (if other metadata providers are not available or are missing artwork).