3DSBricker / dut-iptv

Github repo containing Kodi addons for CanalDigitaal IPTV, F1 TV, KPN/Telfort/XS4All iTV, NLZiet, Telenet, T-Mobile TV, Videoland and Ziggo Go
https://quasarkodearring.github.io/
3 stars 2 forks source link

[temp] FIX for Videoland sign in #2

Closed Nigel1992 closed 1 year ago

Nigel1992 commented 1 year ago

Not working anymore...


I was unable to sign-in to Videoland. It apparently has something to do with the cookies. To temp fix it, just download the following fixed version of Videoland: plugin.video.videoland.zip

If you are not familiar with Python, dont bother reading the below info.

So I fixed it by adding the gmid + ucid + hasGmid cookies :) Working example of api.py below [using static/contant cookies] [might work for others?]

import base64, json, os, random, re, string, time, xbmc

from collections import OrderedDict
from resources.lib.base.l1.constants import ADDON_ID, ADDON_PROFILE
from resources.lib.base.l2 import settings
from resources.lib.base.l2.log import log
from resources.lib.base.l3.language import _
from resources.lib.base.l3.util import check_key, get_credentials, encode32, is_file_older_than_x_days, is_file_older_than_x_minutes, load_file, load_profile, load_prefs, remove_dir, remove_file, save_profile, save_prefs, set_credentials, write_file
from resources.lib.base.l4.exceptions import Error
from resources.lib.base.l4.
session import Session
from resources.lib.base.l5.api import api_download, api_get_channels
from resources.lib.constants import CONST_BASE_HEADERS, CONST_URLS, CONST_IMAGES
from urllib.parse import parse_qs, urlparse, quote_plus

#Included from base.l7.plugin
#api_clean_after_playback
#api_get_info

#Included from base.l8.menu
#api_add_to_watchlist
#api_get_profiles
#api_list_watchlist
#api_login
#api_play_url
#api_remove_from_watchlist
#api_search
#api_set_profile
#api_vod_download
#api_vod_season
#api_vod_seasons
#api_watchlist_listing

def api_add_to_watchlist(id, series='', season='', program_type='', type='watchlist'):
    if not api_get_session():
        return None

    if series and len(str(series)) > 0:
        id = series

    if type == 'watchlist':
        watchlist_url = '{base_url}/api/v3/watchlist/{id}'.format(base_url=CONST_URLS['base'], id=id)
    else:
        return False

    headers = api_get_headers(personal=False)

    download = api_download(url=watchlist_url, type='post', headers=headers, data=None, json_data=False, return_json=False)
    code = download['code']

    if not code or not code == 200:
        return False

    return True

def api_clean_after_playback(stoptime):
    profile_settings = load_profile(profile_id=1)

    if len(str(profile_settings['ticket_id'])) > 0:
        offset = "00:00:00"

        if stoptime > 0:
            m, s = divmod(stoptime, 60)
            h, m = divmod(m, 60)
            offset = '{:02d}:{:02d}:{:02d}'.format(h, m, s)

        session_post_data = {
            'action': "stop",
            'buffer_state': 0,
            'buffer_total': 1,
            'offset': offset,
            'token': profile_settings['token']
        }

        headers = api_get_headers(personal=False)

        stop_url = '{base_url}/api/v3/heartbeat/{ticket_id}?action=stop&offset={offset}'.format(base_url=CONST_URLS['base'], ticket_id=profile_settings['ticket_id'], offset=offset)

        download = api_download(url=stop_url, type='post', headers=headers, data=session_post_data, json_data=True, return_json=True)

        session_post_data['action'] = 'exit'

        exit_url = '{base_url}/api/v3/heartbeat/{ticket_id}?action=exit&offset={offset}'.format(base_url=CONST_URLS['base'], ticket_id=profile_settings['ticket_id'], offset=offset)

        download = api_download(url=exit_url, type='post', headers=headers, data=session_post_data, json_data=True, return_json=True)

def api_get_headers(personal=False):
    headers = { 'videoland-platform': 'videoland' }

    if personal == True:
        pass

    return headers

def api_get_info(id, channel=''):
    #profile_settings = load_profile(profile_id=1)

    info = {}

    return info

def api_get_session(force=0, return_data=False):
    force = int(force)

    profile_url = '{base_url}/api/v3/profiles'.format(base_url=CONST_URLS['base'])

    headers = api_get_headers(personal=False)
    headers["Referer"] = "{base_url}/profielkeuze".format(base_url=CONST_URLS['base'])

    download = api_download(url=profile_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
    data = download['data']
    code = download['code']

    if not code or not code == 200 or not data or not check_key(data[0], 'id'):
        login_result = api_login()

        if not login_result['result']:
            if return_data == True:
                return {'result': False, 'data': login_result['data'], 'code': login_result['code']}

            return False
        else:
            download = api_download(url=profile_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
            data = download['data']
            code = download['code']

    profile_settings = load_profile(profile_id=1)
    profile_settings['last_login_success'] = 1
    profile_settings['last_login_time'] = int(time.time())
    save_profile(profile_id=1, profile=profile_settings)

    if return_data == True:
        return {'result': True, 'data': data, 'code': code}

    return True

def api_get_profiles():
    profiles = api_get_session(force=0, return_data=True)
    return_profiles = {}

    if profiles['result'] == True:
        for result in profiles['data']:
            if result['is_active'] == True:
                return_profiles[result['id']] = {}
                return_profiles[result['id']]['id'] = result['id']
                return_profiles[result['id']]['name'] = result['profilename']

    return return_profiles

def api_list_watchlist(type='watchlist'):
    if not api_get_session():
        return None

    profile_settings = load_profile(profile_id=1)

    headers = api_get_headers(personal=False)

    if type == 'continuewatch':
        watchlist_url = '{base_url}/api/v3/progress/{id}'.format(base_url=CONST_URLS['base'], id=profile_settings['profile_id'])
    elif type == 'watchlist':
        watchlist_url = '{base_url}/api/v3/watchlist/{id}'.format(base_url=CONST_URLS['base'], id=profile_settings['profile_id'])

    download = api_download(url=watchlist_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
    data = download['data']
    code = download['code']

    if code and code == 200 and data and check_key(data, 'count'):
        return data

    return None

def api_login():
    creds = get_credentials()
    username = creds['username']
    password = creds['password']

    remove_file(file='stream_cookies', ext=False)

    profile_settings = load_profile(profile_id=1)
    profile_settings['vlid'] = ''
    profile_settings['profile_id'] = ''

    save_profile(profile_id=1, profile=profile_settings)

    headers = {
        "Origin": CONST_URLS['gigya'],
        "Referer": "{gigya_url}/".format(gigya_url=CONST_URLS['gigya']),
    "Cookie": "gmid=gmid.ver4.AcbH8QbssA.v4cJjgoAsSCf9Zdj_avWgcJ9HV2e6AWBNGkcWUjHS0xdUzsDk76vyzDMvFQ23hNh.bUx-tQpqtFBZFdJpjLC6K7xRVnLBDdtOhQEDNyidI_qDKlktYOciFkL_3T3OWYqaHNtNvQXbBcZ9bZppDcXd3g.sc3; ucid=pwJ04qPJUZvOT0Y2bmqTmQ; hasGmid=ver4"
}

    session_post_data = {
        'loginID': username,
        'password': password,
        'sessionExpiration': '0',
        'targetEnv': 'jssdk',
        'include': 'profile,data',
        'includeUserInfo': 'true',
        'lang': 'en',
        'APIKey': '3_t2Z1dFrbWR-IjcC-Bod1kei6W91UKmeiu3dETVG5iKaY4ILBRzVsmgRHWWo0fqqd',
        'sdk': 'js_latest',
        'authMode': 'cookie',
        'pageURL': "{base_url}/".format(base_url=CONST_URLS['base']),
        'format': 'json',
    }

    login_url = '{gigya_url}/accounts.login'.format(gigya_url=CONST_URLS['gigya'])

    download = api_download(url=login_url, type='post', headers=headers, data=session_post_data, json_data=False, return_json=True)
    data = download['data']
    code = download['code']

    if not code or not code == 200 or not data or not check_key(data, 'signatureTimestamp') or not check_key(data, 'UID') or not check_key(data, 'UIDSignature'):
        return { 'code': code, 'data': data, 'result': False }

    hash_url = '{base_url}/api/v3/login/hash'.format(base_url=CONST_URLS['base'])

    headers = api_get_headers(personal=False)
    headers["Referer"] = "{base_url}/inloggen".format(base_url=CONST_URLS['base'])
    headers["Origin"] = CONST_URLS['base']

    session_post_data = {
        'UID': data['UID'],
        'UIDSignature': data['UIDSignature'],
        'signatureTimestamp': data['signatureTimestamp'],
    }

    download = api_download(url=hash_url, type='post', headers=headers, data=session_post_data, json_data=True, return_json=True)
    data = download['data']
    code = download['code']

    if not code or not code == 200 or not data or not check_key(data, 'vlHash'):
        return { 'code': code, 'data': data, 'result': False }

    profile_settings['vlid'] = data['vlHash']

    cookies = load_file(file='stream_cookies', isJSON=True)

    if not cookies:
        cookies = {}

    cookies['vlId'] = data['vlHash']

    write_file(file='stream_cookies', data=cookies, isJSON=True)

    profile_url = '{base_url}/api/v3/profiles'.format(base_url=CONST_URLS['base'])

    headers = api_get_headers(personal=False)
    headers["Referer"] = "{base_url}/profielkeuze".format(base_url=CONST_URLS['base'])

    download = api_download(url=profile_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
    data = download['data']
    code = download['code']

    if not code or not code == 200 or not data or not check_key(data[0], 'id'):
        return { 'code': code, 'data': data, 'result': False }

    save_profile(profile_id=1, profile=profile_settings)

    api_set_profile()

    return { 'code': code, 'data': data, 'result': True }

def api_play_url(type, channel=None, id=None, video_data=None, from_beginning=0, pvr=0, change_audio=0):
    playdata = {'path': '', 'license': '', 'info': '', 'properties': {}}

    if not api_get_session():
        return playdata

    from_beginning = int(from_beginning)
    pvr = int(pvr)
    change_audio = int(change_audio)

    profile_settings = load_profile(profile_id=1)

    info = {}
    properties = {}

    if not type or not len(str(type)) > 0:
        return playdata

    headers = api_get_headers(personal=False)

    if type == 'channel':
        play_url_path = id

        download = api_download(url=play_url_path, type='get', headers=headers, data=None, json_data=False, return_json=True)
        data = download['data']
        code = download['code']

        if not code or not code == 200 or not data or not check_key(data, 'manifest') or not check_key(data, 'token'):
            return playdata

        path = data['manifest']

        if check_key(data, 'licenseUrl'):
            license = data['licenseUrl']

        profile_settings['ticket_id'] = ''
        profile_settings['token'] = data['token']
        save_profile(profile_id=1, profile=profile_settings)

        mpd = ''

        playdata = {'path': path, 'mpd': mpd, 'license': license, 'info': info, 'properties': properties}

        return playdata

    if id.startswith('E'):
        typestr = 'episode'
        id = id[1:]
        id_ar = id.split('###')
        series = id_ar[0]
        season = id_ar[1]
        id = id_ar[2]
        info_url = '{base_url}/api/v3/episodes/{series}/{season}'.format(base_url=CONST_URLS['base'], series=series, season=season)
    elif id.startswith('M'):
        typestr = 'movie'
        id = id[1:]
        info_url = '{base_url}/api/v3/movies/{id}'.format(base_url=CONST_URLS['base'], id=id)
    else:
        return playdata

    download = api_download(url=info_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
    data = download['data']
    code = download['code']

    if code and code == 200 and data:
        if typestr == 'episode':
            if check_key(data, 'details') and check_key(data['details'], 'E' + str(id)):
                info = data['details']['E' + str(id)]
        else:
            info = data

    play_url_path = '{base_url}/api/v3/stream/{id}/widevine?edition=&profile_id={profile_id}'.format(base_url=CONST_URLS['base'], id=id, profile_id=profile_settings['profile_id'])

    download = api_download(url=play_url_path, type='get', headers=headers, data=None, json_data=False, return_json=True)
    data = download['data']
    code = download['code']

    if not code or not code == 200 or not data or not check_key(data, 'id') or not check_key(data, 'ticket_id') or not check_key(data, 'stream') or not check_key(data['stream'], 'dash'):
        return playdata

    path = data['stream']['dash']

    if check_key(data, 'drm'):
        license = data['drm']

    profile_settings['ticket_id'] = data['ticket_id']
    profile_settings['token'] = data['stream']['token']
    save_profile(profile_id=1, profile=profile_settings)

    mpd = ''

    if change_audio == 1:
        download = api_download(url=path, type='get', headers=headers, data=None, json_data=False, return_json=False)
        data = download['data']
        code = download['code']

        if code and code == 200:
            mpd = data

    playdata = {'path': path, 'mpd': mpd, 'license': license, 'info': info, 'properties': properties}

    return playdata

def api_remove_from_watchlist(id, type='watchlist'):
    if not api_get_session():
        return None

    headers = api_get_headers(personal=False)

    if type == 'continuewatch':
        remove_url = '{base_url}/api/v3/progress/{id}'.format(base_url=CONST_URLS['base'], id=id)
    elif type == 'watchlist':
        remove_url = '{base_url}/api/v3/watchlist/{id}'.format(base_url=CONST_URLS['base'], id=id)

    download = api_download(url=remove_url, type='delete', headers=headers, data=None, json_data=False, return_json=False)
    code = download['code']

    if not code or (not code == 200 and not code == 204):
        return False

    return True

def api_search(query):
    return None

def api_set_profile(id=''):
    profile_settings = load_profile(profile_id=1)
    profiles = api_get_session(force=0, return_data=True)

    name = ''
    owner_id = ''
    owner_name = ''
    saved_id = ''
    saved_name = ''

    if not profiles or profiles['result'] == False:
        return False

    for result in profiles['data']:
        if result['is_account_owner'] == True:
            owner_id = result['id']
            owner_name = result['profilename']

        if result['is_active'] == True and result['id'] == id:
            name = result['profilename']

        if check_key(profile_settings, 'profile_id'):
            if result['is_active'] == True and result['id'] == profile_settings['profile_id']:
                saved_id = result['id']
                saved_name = result['profilename']

    if len(str(name)) == 0:
        if len(str(saved_name)) > 0:
            id = saved_id
            name = saved_name
        else:
            id = owner_id
            name = owner_name

    switch_url = '{base_url}/api/v3/profiles/{id}/switch'.format(base_url=CONST_URLS['base'], id=id)

    headers = api_get_headers(personal=False)
    headers["Referer"] = "{base_url}/profielkeuze".format(base_url=CONST_URLS['base'])

    session_post_data = {
        'customer_id': id
    }

    download = api_download(url=switch_url, type='post', headers=headers, data=session_post_data, json_data=False, return_json=False)
    code = download['code']

    if not code or not code == 204:
        return False

    profile_settings = load_profile(profile_id=1)
    profile_settings['profile_name'] = name
    profile_settings['profile_id'] = id
    save_profile(profile_id=1, profile=profile_settings)

    return True

def api_vod_download():
    return None

def api_vod_season(series, id, use_cache=True):
    type = "vod_season_{id}".format(id=id)
    type = encode32(type)

    file = os.path.join("cache", "{type}.json".format(type=type))

    id_ar = id.split('###')
    series = id_ar[0]
    seasonstr = id_ar[1]
    cache = 0

    if not is_file_older_than_x_days(file=os.path.join(ADDON_PROFILE, file), days=0.5) and use_cache == True:
        data = load_file(file=file, isJSON=True)
        cache = 1
    else:
        headers = api_get_headers(personal=False)

        seasons_url = '{base_url}/api/v3/series/{series}/seasons/{season}'.format(base_url=CONST_URLS['base'], series=series, season=seasonstr)

        download = api_download(url=seasons_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
        data = download['data']
        code = download['code']

        if code and code == 200 and data and check_key(data, 'title'):
            write_file(file=file, data=data, isJSON=True)

    return {'data': data, 'cache': cache}

def api_vod_seasons(type, id, use_cache=True):
    type = "vod_seasons_{id}".format(id=id)
    type = encode32(type)

    file = os.path.join("cache", "{type}.json".format(type=type))

    id = id[1:]
    cache = 0

    if not is_file_older_than_x_days(file=os.path.join(ADDON_PROFILE, file), days=0.5) and use_cache == True:
        data = load_file(file=file, isJSON=True)
        cache = 1
    else:
        headers = api_get_headers(personal=False)

        seasons_url = '{base_url}/api/v3/series/{series}'.format(base_url=CONST_URLS['base'], series=id)

        download = api_download(url=seasons_url, type='get', headers=headers, data=None, json_data=False, return_json=True)
        data = download['data']
        code = download['code']

        if code and code == 200 and data and check_key(data, 'title'):
            write_file(file=file, data=data, isJSON=True)

    return {'data': data, 'cache': cache}

def api_vod_subscription():
    return None

def api_watchlist_listing():
    return None

NOTE: I am not a programmer, but I am familiar with HTTP. To "hopefully" fix this permanently, you'll need to parse the cookies.

These cookies can be parsed from response headers after first making a GET request to:

https://accounts.eu1.gigya.com/accounts.webSdkBootstrap?apiKey=3_t2Z1dFrbWR-IjcC-Bod1kei6W91UKmeiu3dETVG5iKaY4ILBRzVsmgRHWWo0fqqd&pageURL=https%3A%2F%2Fwww.videoland.com%2F&sdk=js_latest&sdkBuild=13455&format=json

With the following headers.

Host: accounts.eu1.gigya.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: */*
Accept-Language: nl,en-US;q=0.7,en;q=0.3
Origin: https://cdns.eu1.gigya.com
DNT: 1
Connection: keep-alive
Referer: https://cdns.eu1.gigya.com/
Cookie: gig3pctest=true
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Accept-Encoding: gzip, deflate

The response should contain the required cookies for login.

set-cookie: gmid=gmid.ver4.AcbHF_bQLg.qEdRRBNoJRoIWKw42-12Ms4pmka-Q0EV5eFZEsntAROdB0_gTfTEJN87Oy5S9CrH.5zK-lVztBN1Arvn7imJRW1aJbaitQKAQzYZ9JAeCwevj3vwvptLm47W3Z-58FX_z7_glMOCdmS_A85rnGUqKSA.sc3; expires=Sat, 18 Nov 2023 15:35:48 GMT; domain=.gigya.com; path=/; secure; samesite=none; httponly
set-cookie: ucid=Kx4yQiSc1ImzRcpmS59XKw; expires=Sat, 18 Nov 2023 15:35:48 GMT; domain=.gigya.com; path=/; secure; samesite=none
set-cookie: hasGmid=ver4; expires=Thu, 18 May 2023 14:35:48 GMT; domain=.gigya.com; path=/; secure; samesite=none

If someone is familair with Python, please edit the api.py so it parses the required cookies :)