matthuisman / slyguy.addons

Github mirror of SlyGuy add-ons
https://www.matthuisman.nz/2020/02/slyguy-kodi-repository.html
282 stars 77 forks source link

Channel 9 au #825

Closed Tinonee111 closed 1 month ago

Tinonee111 commented 1 month ago

Hi mate Could you have a look at channel 9 please Been down for a few days now Kind. Regards

matthuisman commented 1 month ago

channel 9 keep killing the user used. i suggest using the kodi 9now add-on if you want reliable streams

also, if your using tivimate, you should be using the Kodi playlist. as the streams are currently working but need specific user agent which the kodi style playlist adds

Tinonee111 commented 1 month ago

Sorry I should have stated it’s the kodi add on that’s not working atm

On Wed, 31 Jul 2024 at 1:31 PM, Matt Huisman @.***> wrote:

channel 9 keep killing the user used. i suggest using the kodi 9now add-on if you want reliable streams

also, if your using tivimate, you should be using the Kodi playlist. as the streams are currently working but need specific user agent which the kodi style playlist adds

— Reply to this email directly, view it on GitHub https://github.com/matthuisman/slyguy.addons/issues/825#issuecomment-2259570545, or unsubscribe https://github.com/notifications/unsubscribe-auth/BKHBQW5SVQWCAC6TFM56XQLZPBLBFAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJZGU3TANJUGU . You are receiving this because you authored the thread.Message ID: @.***>

matthuisman commented 1 month ago

make sure your using the latest version. It will make you login now using a 9now login (which are free)

matthuisman commented 1 month ago

if you are on latest, then please provide a full kodi debug log showing the error

Tinonee111 commented 1 month ago

Thanks, I was tricked by the new setup requiring a login All good now, thank you for the app and your assistance

On Wed, Jul 31, 2024 at 1:36 PM Matt Huisman @.***> wrote:

if you are on latest, then please provide a full kodi debug log showing the error

— Reply to this email directly, view it on GitHub https://github.com/matthuisman/slyguy.addons/issues/825#issuecomment-2259575121, or unsubscribe https://github.com/notifications/unsubscribe-auth/BKHBQWZ2JBBQOWTDXYWPEHTZPBLVLAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJZGU3TKMJSGE . You are receiving this because you authored the thread.Message ID: @.***>

tresby commented 1 month ago

I'm using the raw-tv m3u8 with Threadfin(Steve) with ffmpeg 7 as a buffer and the streams are working fine. Would it be better for me to switch to the Kodi m3u8?

matthuisman commented 1 month ago

If its working, just keep as is. I'm not sure if that program supports the Kodi style

On Wed, 31 Jul 2024, 21:07 tresby, @.***> wrote:

I'm using the raw-tv m3u8 with Threadfin(Steve) with ffmpeg 7 as a buffer and the streams are working fine. Would it be better for me to switch to the Kodi m3u8?

— Reply to this email directly, view it on GitHub https://github.com/matthuisman/slyguy.addons/issues/825#issuecomment-2260026019, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPQAKOCMDCRBF2MRRSRQHTZPCSOXAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGAZDMMBRHE . You are receiving this because you modified the open/close state.Message ID: @.***>

tresby commented 1 month ago

If its working, just keep as is. I'm not sure if that program supports the Kodi style On Wed, 31 Jul 2024, 21:07 tresby, @.> wrote: I'm using the raw-tv m3u8 with Threadfin(Steve) with ffmpeg 7 as a buffer and the streams are working fine. Would it be better for me to switch to the Kodi m3u8? — Reply to this email directly, view it on GitHub <#825 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPQAKOCMDCRBF2MRRSRQHTZPCSOXAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGAZDMMBRHE . You are receiving this because you modified the open/close state.Message ID: @.>

Just went to try it again and its gone bad again.

Was messing around with Kodi.proxy last night and I can get up to getting an m3u8 with the plugin:// links, but I can only individually play the channels from replay using the tvs.sh.

Would there be an easy way/script to have on a cron job to using the plugin:// links and tvs.sh to get the http link in a new m3u8? If that makes sense.

Don't run tvh so can't use the pipe:// method unfortunately.

matthuisman commented 1 month ago

Another user on twitter made a flask app to redirect to the new url.

On Wed, 31 Jul 2024, 21:29 tresby, @.***> wrote:

If its working, just keep as is. I'm not sure if that program supports the Kodi style … <#m-8210583645172429004> On Wed, 31 Jul 2024, 21:07 tresby, @.> wrote: I'm using the raw-tv m3u8 with Threadfin(Steve) with ffmpeg 7 as a buffer and the streams are working fine. Would it be better for me to switch to the Kodi m3u8? — Reply to this email directly, view it on GitHub <#825 (comment) https://github.com/matthuisman/slyguy.addons/issues/825#issuecomment-2260026019>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPQAKOCMDCRBF2MRRSRQHTZPCSOXAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGAZDMMBRHE https://github.com/notifications/unsubscribe-auth/ABPQAKOCMDCRBF2MRRSRQHTZPCSOXAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGAZDMMBRHE . You are receiving this because you modified the open/close state.Message ID: @.>

Just went to try it again and its gone bad again.

Was messing around with Kodi.proxy last night and I can get up to getting an m3u8 with the plugin:// links, but I can only individually play the channels from replay using the tvs.sh.

Would there be an easy way/script to have on a cron job to using the plugin:// links and tvs.sh to get the http link in a new m3u8? If that makes sense.

Don't run tvh so can't use the pipe:// method unfortunately.

— Reply to this email directly, view it on GitHub https://github.com/matthuisman/slyguy.addons/issues/825#issuecomment-2260076322, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPQAKJ5EPNYUH773WEAUDLZPCU6PAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGA3TMMZSGI . You are receiving this because you modified the open/close state.Message ID: @.***>

tresby commented 1 month ago

Another user on twitter made a flask app to redirect to the new url. On Wed, 31 Jul 2024, 21:29 tresby, @.> wrote: If its working, just keep as is. I'm not sure if that program supports the Kodi style … <#m-8210583645172429004> On Wed, 31 Jul 2024, 21:07 tresby, @.> wrote: I'm using the raw-tv m3u8 with Threadfin(Steve) with ffmpeg 7 as a buffer and the streams are working fine. Would it be better for me to switch to the Kodi m3u8? — Reply to this email directly, view it on GitHub <#825 (comment) <#825 (comment)>>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPQAKOCMDCRBF2MRRSRQHTZPCSOXAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGAZDMMBRHE https://github.com/notifications/unsubscribe-auth/ABPQAKOCMDCRBF2MRRSRQHTZPCSOXAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGAZDMMBRHE . You are receiving this because you modified the open/close state.Message ID: @.> Just went to try it again and its gone bad again. Was messing around with Kodi.proxy last night and I can get up to getting an m3u8 with the plugin:// links, but I can only individually play the channels from replay using the tvs.sh. Would there be an easy way/script to have on a cron job to using the plugin:// links and tvs.sh to get the http link in a new m3u8? If that makes sense. Don't run tvh so can't use the pipe:// method unfortunately. — Reply to this email directly, view it on GitHub <#825 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPQAKJ5EPNYUH773WEAUDLZPCU6PAVCNFSM6AAAAABLXSNJR6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRQGA3TMMZSGI . You are receiving this because you modified the open/close state.Message ID: @.>

Oh awesome, did you happen to have the link handy? Trying to have a look for it on twitter now but can't seem to find it atm.

matthuisman commented 1 month ago

He posted it in a DM. Its basically doing what the 9now addon does

import os
import json
import time
import requests
import logging
from flask import Flask, Response, redirect
from urllib.parse import urlencode, parse_qsl, urlparse, parse_qs
import threading

# Import constants from the 9Now addon
from constants import *

app = Flask(__name__)

# Set up file logging
log_file_path = '/var/www/9now/9now_api.log'
file_handler = logging.FileHandler(log_file_path)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)

REFRESH_TOKEN_PATH = '/var/www/kodi.proxy/userdata/addon_data/slyguy.9now/refresh_token.json'

class Settings:
    REGION = 'nsw'
    CHANNEL_REFERENCE = 'live-ch9-syd-ssai'  # Reference for Channel 9 Sydney

settings = Settings()

def get_refresh_token():
    with open(REFRESH_TOKEN_PATH, 'r') as f:
        data = json.load(f)
        return data.get('refresh_token')

class API:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update(HEADERS)
        self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        self.session.headers.update({'User-Agent': self.user_agent})
        self._access_token = None
        self._token_expires = 0
        self.cached_url = None
        self.url_expiry = 0
        self.url_cache_lock = threading.Lock()

    def _refresh_token(self):
        if self._token_expires > time.time():
            return

        refresh_token = get_refresh_token()
        params = {
            'refresh_token': refresh_token,
            'client_id': '9nowdevice',
            'response_types': 'id_token',
        }
        try:
            headers = self.session.headers.copy()
            headers.update({'User-Agent': self.user_agent})
            response = self.session.post(AUTH_URL.format('/refresh-token'), params=params, timeout=30, verify=True)
            response.raise_for_status()
            data = response.json()

            if 'error' in data:
                raise Exception(data['error'])

            self._access_token = data.get('accessToken') or data.get('access_token')
            expires_in = data.get('expiresIn') or data.get('expires_in')
            self._token_expires = int(time.time()) + int(expires_in) - 30
            self.session.headers.update({'Authorization': f'Bearer {self._access_token}'})
        except requests.exceptions.RequestException as e:
            raise

    def channels(self, region='nsw'):
        self._refresh_token()
        params = {
            'device': 'web',
            'streamParams': 'web,chrome,windows',
            'region': region,
            'offset': 0,
        }
        try:
            headers = self.session.headers.copy()
            headers.update({'User-Agent': self.user_agent})
            response = self.session.get(LIVESTREAM_URL, params=params, timeout=30,verify=True)
            response.raise_for_status()
            data = response.json()
            return data['data']['getLivestream']
        except requests.RequestException as e:
            raise

    def _channels(self):
        region = settings.REGION
        data = self.channels(region)
        data['channels'].extend([row for row in data['events'] if row['type'] == 'live-event' and row['nextEvent']['name']])
        data['events'] = [row for row in data['events'] if row not in data['channels']]
        return data

    def get_expiry_from_url(self, url):
        parsed_url = urlparse(url)
        query_params = parse_qs(parsed_url.query)
        exp_param = query_params.get('exp', [None])[0]
        return int(exp_param) if exp_param else None

    def get_channel9_url(self):
        with self.url_cache_lock:
            current_time = time.time()
            if self.cached_url and current_time < self.url_expiry - 60:  # Refresh 60 seconds before expiry
                return self.cached_url

            try:
                data = self._channels()
                url = None
                for row in data['channels']:
                    if row['referenceId'] == settings.CHANNEL_REFERENCE:
                        url = row['stream']['url']
                        break

                if not url:
                    return None

                # fix encoded query
                if '?' in url:
                    url = url.split('?')[0] + '?' + urlencode(parse_qsl(url.split('?')[1]))

                self.cached_url = url
                self.url_expiry = self.get_expiry_from_url(url) or (current_time + 3600)  # Default to 1 hour if no expiry found
                app.logger.info(f"Updated cached URL (expires at {time.ctime(self.url_expiry)}): {url}")
                return url

            except Exception as e:
                app.logger.error(f"Error fetching Channel 9 URL: {str(e)}")
                return None

api = API()

def update_url_periodically():
    while True:
        api.get_channel9_url()  # This will update the cache if necessary
        time.sleep(60)  # Check every minute

@app
.route('/9')
def get_channel9_live():
    url = api.get_channel9_url()
    if url:
        app.logger.info(f"Serving URL (expires at {time.ctime(api.url_expiry)}): {url}")
        return redirect(url)
    else:
        return Response("Unable to fetch Channel 9 URL", status=500, mimetype='text/plain')

if __name__ == '__main__':
    # Start the background thread to update the URL periodically
    threading.Thread(target=update_url_periodically, daemon=True).start()
    app.run(host='0.0.0.0', port=9001)
paul19920801 commented 1 month ago

He posted it in a DM. Its basically doing what the 9now addon does

import os
import json
import time
import requests
import logging
from flask import Flask, Response, redirect
from urllib.parse import urlencode, parse_qsl, urlparse, parse_qs
import threading

# Import constants from the 9Now addon
from constants import *

app = Flask(__name__)

# Set up file logging
log_file_path = '/var/www/9now/9now_api.log'
file_handler = logging.FileHandler(log_file_path)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)

REFRESH_TOKEN_PATH = '/var/www/kodi.proxy/userdata/addon_data/slyguy.9now/refresh_token.json'

class Settings:
    REGION = 'nsw'
    CHANNEL_REFERENCE = 'live-ch9-syd-ssai'  # Reference for Channel 9 Sydney

settings = Settings()

def get_refresh_token():
    with open(REFRESH_TOKEN_PATH, 'r') as f:
        data = json.load(f)
        return data.get('refresh_token')

class API:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update(HEADERS)
        self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        self.session.headers.update({'User-Agent': self.user_agent})
        self._access_token = None
        self._token_expires = 0
        self.cached_url = None
        self.url_expiry = 0
        self.url_cache_lock = threading.Lock()

    def _refresh_token(self):
        if self._token_expires > time.time():
            return

        refresh_token = get_refresh_token()
        params = {
            'refresh_token': refresh_token,
            'client_id': '9nowdevice',
            'response_types': 'id_token',
        }
        try:
            headers = self.session.headers.copy()
            headers.update({'User-Agent': self.user_agent})
            response = self.session.post(AUTH_URL.format('/refresh-token'), params=params, timeout=30, verify=True)
            response.raise_for_status()
            data = response.json()

            if 'error' in data:
                raise Exception(data['error'])

            self._access_token = data.get('accessToken') or data.get('access_token')
            expires_in = data.get('expiresIn') or data.get('expires_in')
            self._token_expires = int(time.time()) + int(expires_in) - 30
            self.session.headers.update({'Authorization': f'Bearer {self._access_token}'})
        except requests.exceptions.RequestException as e:
            raise

    def channels(self, region='nsw'):
        self._refresh_token()
        params = {
            'device': 'web',
            'streamParams': 'web,chrome,windows',
            'region': region,
            'offset': 0,
        }
        try:
            headers = self.session.headers.copy()
            headers.update({'User-Agent': self.user_agent})
            response = self.session.get(LIVESTREAM_URL, params=params, timeout=30,verify=True)
            response.raise_for_status()
            data = response.json()
            return data['data']['getLivestream']
        except requests.RequestException as e:
            raise

    def _channels(self):
        region = settings.REGION
        data = self.channels(region)
        data['channels'].extend([row for row in data['events'] if row['type'] == 'live-event' and row['nextEvent']['name']])
        data['events'] = [row for row in data['events'] if row not in data['channels']]
        return data

    def get_expiry_from_url(self, url):
        parsed_url = urlparse(url)
        query_params = parse_qs(parsed_url.query)
        exp_param = query_params.get('exp', [None])[0]
        return int(exp_param) if exp_param else None

    def get_channel9_url(self):
        with self.url_cache_lock:
            current_time = time.time()
            if self.cached_url and current_time < self.url_expiry - 60:  # Refresh 60 seconds before expiry
                return self.cached_url

            try:
                data = self._channels()
                url = None
                for row in data['channels']:
                    if row['referenceId'] == settings.CHANNEL_REFERENCE:
                        url = row['stream']['url']
                        break

                if not url:
                    return None

                # fix encoded query
                if '?' in url:
                    url = url.split('?')[0] + '?' + urlencode(parse_qsl(url.split('?')[1]))

                self.cached_url = url
                self.url_expiry = self.get_expiry_from_url(url) or (current_time + 3600)  # Default to 1 hour if no expiry found
                app.logger.info(f"Updated cached URL (expires at {time.ctime(self.url_expiry)}): {url}")
                return url

            except Exception as e:
                app.logger.error(f"Error fetching Channel 9 URL: {str(e)}")
                return None

api = API()

def update_url_periodically():
    while True:
        api.get_channel9_url()  # This will update the cache if necessary
        time.sleep(60)  # Check every minute

@app
.route('/9')
def get_channel9_live():
    url = api.get_channel9_url()
    if url:
        app.logger.info(f"Serving URL (expires at {time.ctime(api.url_expiry)}): {url}")
        return redirect(url)
    else:
        return Response("Unable to fetch Channel 9 URL", status=500, mimetype='text/plain')

if __name__ == '__main__':
    # Start the background thread to update the URL periodically
    threading.Thread(target=update_url_periodically, daemon=True).start()
    app.run(host='0.0.0.0', port=9001)

was brainstorming on ChatGPT with this code, and here are some potential ways.. Obviously the token would need to be done on the hosts machine.

Instead of relying on a dynamically changing URL, you can use a proxy server or create an intermediate static endpoint that always points to the most current URL.

Steps:

Create a Static Proxy Server

You can set up a small proxy server (e.g., using Nginx or Apache) to serve a static URL that always redirects to the latest Channel 9 URL.

Set up NextPVR to use the static URL endpoint. For example:

URL for Channel 9: http://yourserver:9001/current_url?channel=channel9
URL for 9Gem: http://yourserver:9001/current_url?channel=9gem
URL for 9Go: http://yourserver:9001/current_url?channel=9go
URL for 9Life: http://yourserver:9001/current_url?channel=9life

Videohelp.com is also another excellent site to assist with issues like these. 
nathcoad commented 1 month ago

Thanks all for this code. I have tried running it and get as far as a m3u8 file, which works for about 2 seconds if I open it in VLC then freezes. It seems like the http://localhost:9001/9 url needs to somehow be loaded continually rather than just once? Am I missing something?