diff --git a/CHANGELOG.md b/CHANGELOG.md index 3299da6..0ebdb91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,21 @@ - Start using pip for third-party plugins - Rename `plex_accessible_ip`/`plex_accessible_port` to `advertise_ip`/`advertise_port`. Add `bind_ip`/`bind_port` options (https://github.com/tgorgdotcom/locast2plex/pull/98) - Hopefully phase out FCC channel checking (when locast reports proper channel numbers) - - Some kind of web based UI to modify config - - A way to daemonize the script for those running outside of docker - Install script for those not using docker - Enable multiplatform Docker image - - Implement proper logging - Wrap HTTP requests around error handling that existed in do_tuner() previously - Documentation added for Kodi, Emby/Jellyfin + - Implement proper logging + - Some kind of web based UI to modify config - Look into pull requests suggestions for ip addressing + - A way to daemonize the script for those running outside of docker ## 1.0.0 (unreleased) - Most bugs squashed +## 0.6.3 + - Add error handling for when a channel in the EPG exists that does not exist in the channel list + ## 0.6.2 - Fix an issue where logins fail when passwords with a '%' are used diff --git a/lib/epg2xml.py b/lib/epg2xml.py index 7a4127f..748bc19 100644 --- a/lib/epg2xml.py +++ b/lib/epg2xml.py @@ -93,6 +93,32 @@ def generate_epg_file(config, location): done_channels = True for channel_item in channel_info: sid = str(channel_item['id']) + if sid in dma_channels.keys(): + channel_number = str(dma_channels[sid]['channel']) + channel_realname = str(dma_channels[sid]['friendlyName']) + channel_callsign = str(dma_channels[sid]['callSign']) + + if 'logo226Url' in channel_item.keys(): + channel_logo = channel_item['logo226Url'] + + elif 'logoUrl' in channel_item.keys(): + channel_logo = channel_item['logoUrl'] + + c_out = sub_el(out, 'channel', id=sid) + sub_el(c_out, 'display-name', text='%s %s' % (channel_number, channel_callsign)) + sub_el(c_out, 'display-name', text='%s %s %s' % (channel_number, channel_callsign, sid)) + sub_el(c_out, 'display-name', text=channel_number) + sub_el(c_out, 'display-name', text='%s %s fcc' % (channel_number, channel_callsign)) + sub_el(c_out, 'display-name', text=channel_callsign) + sub_el(c_out, 'display-name', text=channel_realname) + + if channel_logo != None: + sub_el(c_out, 'icon', src=channel_logo) + + # Now list Program informations + for channel_item in channel_info: + sid = str(channel_item['id']) + if sid in dma_channels.keys(): channel_number = str(dma_channels[sid]['channel']) channel_realname = str(dma_channels[sid]['friendlyName']) channel_callsign = str(dma_channels[sid]['callSign']) @@ -103,84 +129,60 @@ def generate_epg_file(config, location): elif 'logoUrl' in channel_item.keys(): channel_logo = channel_item['logoUrl'] - c_out = sub_el(out, 'channel', id=sid) - sub_el(c_out, 'display-name', text='%s %s' % (channel_number, channel_callsign)) - sub_el(c_out, 'display-name', text='%s %s %s' % (channel_number, channel_callsign, sid)) - sub_el(c_out, 'display-name', text=channel_number) - sub_el(c_out, 'display-name', text='%s %s fcc' % (channel_number, channel_callsign)) - sub_el(c_out, 'display-name', text=channel_callsign) - sub_el(c_out, 'display-name', text=channel_realname) + for event in channel_item['listings']: - if channel_logo != None: - sub_el(c_out, 'icon', src=channel_logo) + tm_start = tm_parse(event['startTime']) # this is returned from locast in UTC + tm_duration = event['duration'] * 1000 + tm_end = tm_parse(event['startTime'] + tm_duration) - # Now list Program informations - for channel_item in channel_info: - sid = str(channel_item['id']) - channel_number = str(dma_channels[sid]['channel']) - channel_realname = str(dma_channels[sid]['friendlyName']) - channel_callsign = str(dma_channels[sid]['callSign']) - - if 'logo226Url' in channel_item.keys(): - channel_logo = channel_item['logo226Url'] - - elif 'logoUrl' in channel_item.keys(): - channel_logo = channel_item['logoUrl'] - - for event in channel_item['listings']: - - tm_start = tm_parse(event['startTime']) # this is returned from locast in UTC - tm_duration = event['duration'] * 1000 - tm_end = tm_parse(event['startTime'] + tm_duration) - - event_genres = [] - if 'genres' in event.keys(): - event_genres = event['genres'].split(",") - - # note we're returning everything as UTC, as the clients handle converting to correct timezone - prog_out = sub_el(out, 'programme', start=tm_start, stop=tm_end, channel=sid) - - if event['title']: - sub_el(prog_out, 'title', lang='en', text=event['title']) - - if 'movie' in event_genres and event['releaseYear']: - sub_el(prog_out, 'sub-title', lang='en', text='Movie: ' + event['releaseYear']) - elif 'episodeTitle' in event.keys(): - sub_el(prog_out, 'sub-title', lang='en', text=event['episodeTitle']) - - if 'description' not in event.keys(): - event['description'] = "Unavailable" - elif event['description'] is None: - event['description'] = "Unavailable" - sub_el(prog_out, 'desc', lang='en', text=event['description']) - - sub_el(prog_out, 'length', units='minutes', text=str(event['duration'])) - - for f in event_genres: - sub_el(prog_out, 'category', lang='en', text=f.strip()) - sub_el(prog_out, 'genre', lang='en', text=f.strip()) - - if event["preferredImage"] is not None: - sub_el(prog_out, 'icon', src=event["preferredImage"]) - - if 'rating' not in event.keys(): - event['rating'] = "N/A" - r = ET.SubElement(prog_out, 'rating') - sub_el(r, 'value', text=event['rating']) - - if 'seasonNumber' in event.keys() and 'episodeNumber' in event.keys(): - s_ = int(str(event['seasonNumber']), 10) - e_ = int(str(event['episodeNumber']), 10) - sub_el(prog_out, 'episode-num', system='common', - text='S%02dE%02d' % (s_, e_)) - sub_el(prog_out, 'episode-num', system='xmltv_ns', - text='%d.%d.0' % (int(s_)-1, int(e_)-1)) - sub_el(prog_out, 'episode-num', system='SxxExx', - text='S%02dE%02d' % (s_, e_)) - - if 'isNew' in event.keys(): - if event['isNew']: - sub_el(prog_out, 'new') + event_genres = [] + if 'genres' in event.keys(): + event_genres = event['genres'].split(",") + + # note we're returning everything as UTC, as the clients handle converting to correct timezone + prog_out = sub_el(out, 'programme', start=tm_start, stop=tm_end, channel=sid) + + if event['title']: + sub_el(prog_out, 'title', lang='en', text=event['title']) + + if 'movie' in event_genres and event['releaseYear']: + sub_el(prog_out, 'sub-title', lang='en', text='Movie: ' + event['releaseYear']) + elif 'episodeTitle' in event.keys(): + sub_el(prog_out, 'sub-title', lang='en', text=event['episodeTitle']) + + if 'description' not in event.keys(): + event['description'] = "Unavailable" + elif event['description'] is None: + event['description'] = "Unavailable" + sub_el(prog_out, 'desc', lang='en', text=event['description']) + + sub_el(prog_out, 'length', units='minutes', text=str(event['duration'])) + + for f in event_genres: + sub_el(prog_out, 'category', lang='en', text=f.strip()) + sub_el(prog_out, 'genre', lang='en', text=f.strip()) + + if event["preferredImage"] is not None: + sub_el(prog_out, 'icon', src=event["preferredImage"]) + + if 'rating' not in event.keys(): + event['rating'] = "N/A" + r = ET.SubElement(prog_out, 'rating') + sub_el(r, 'value', text=event['rating']) + + if 'seasonNumber' in event.keys() and 'episodeNumber' in event.keys(): + s_ = int(str(event['seasonNumber']), 10) + e_ = int(str(event['episodeNumber']), 10) + sub_el(prog_out, 'episode-num', system='common', + text='S%02dE%02d' % (s_, e_)) + sub_el(prog_out, 'episode-num', system='xmltv_ns', + text='%d.%d.0' % (int(s_)-1, int(e_)-1)) + sub_el(prog_out, 'episode-num', system='SxxExx', + text='S%02dE%02d' % (s_, e_)) + + if 'isNew' in event.keys(): + if event['isNew']: + sub_el(prog_out, 'new') xml_lock = FileLock(out_lock_path) with xml_lock: diff --git a/lib/tuner_interface.py b/lib/tuner_interface.py index 230a6fd..8a9ada5 100644 --- a/lib/tuner_interface.py +++ b/lib/tuner_interface.py @@ -27,6 +27,7 @@ class PlexHttpHandler(BaseHTTPRequestHandler): def do_GET(self): + base_url = self.config['main']['plex_accessible_ip'] + ':' + self.config['main']['plex_accessible_port'] contentPath = self.path