Skip to content

Instantly share code, notes, and snippets.

@jaylinski
Last active November 9, 2025 01:13
Show Gist options
  • Save jaylinski/b89aaddc724961ce6f07582adeb450d6 to your computer and use it in GitHub Desktop.
Save jaylinski/b89aaddc724961ce6f07582adeb450d6 to your computer and use it in GitHub Desktop.
Kodi playlist containing free and public TV streams from DE/AT/CH with DRM support.
#KODIPROP:inputstream=inputstream.adaptive
#KODIPROP:inputstream.adaptive.manifest_type=mpd
#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha
#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=<token>||R{SSM}|
https://orf1.mdn.ors.at/out/u/orf1/drmqxa/manifest.mpd
@jaylinski
Copy link
Author

@jaylinski
Copy link
Author

Endpoint for key-server userToken: https://api-stage-tvthek.orf.at/livestream/_token

@nguoivanchuyen
Copy link

hi jaylinski
you can help me how to get key-server usertoken channel Kplus, this key server drm auto change 5-10 minute
#KODIPROP:inputstream=inputstream.adaptive
#KODIPROP:inputstream.adaptive.manifest_type=mpd
#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha
#KODIPROP:inputstream.adaptive.license_key=https://kplus.live.ott.irdeto.com/Widevine/GetLicense?CrmId=kplus&AccountId=kplus&ContentId=400000015&SessionId=14C0F1BB9A14154D&Ticket=F75ECCDF30FDD78A
https://sglive.akamaized.net/bpk-tv/prod_kplus_sport2hd/default/i.mpd
i can't play
i hope you wil help me. Thank you

@jaylinski
Copy link
Author

@luanfxphd I solved this issue by updating my playlist every 10minutes with an automated script.

@nguoivanchuyen
Copy link

oh, can you guide me or send me the automated script? Or you can teamview my computer sniff how to get license server DRM, after insert key DRM watch live channel anytime with link live https://sglive.akamaized.net/bpk-tv/prod_kplus_sport2hd/default/i.mpd.
thank!

@dishactive
Copy link

xin chao
contact me on zalo 0962560715
i can help you to get key

this key: https://license-sgn.solocoo.tv/wv?c=cpi%3A2%3Alz1VpqEARK5j06IAoDvcLZ9st5IBQNQC2wXJh35oTzc%3AMTp7YzoiUW5xQVFzaG1jMHV1dnNSWGtLcUZMZ0FBQUFFIixsOiJ2aV9WTiIsbWY6MTYsb2Y6MSx0czoxNjM4NzY1OTgwNjMxLHU6Ijc5YmY2MjM1LWNjYWYtYzgwMi0xZDJkLTE1OWNlOTFlODJiNiIsZGU6MyxsZjowfQ

{"url":"https://vstv.broadpeak-aas.com/bpk-tv/prod_kplus_lifehd/default/i.mpd","mediaType":"DASH","drm":{"system":"Widevine","licenseUrl":"https://license-sgn.solocoo.tv/wv?c=cpi%3A2%3AuZkFXxlVmhPpDGGenzU34MEu7GYtgNXItmqL0iMhQ8g%3AMTp7YzoiUW5xQVFzaG1jMHV1dnNSWGtLcUZMZ0FBQUFJIixsOiJ2aV9WTiIsbWY6MTYsb2Y6MSx0czoxNjM4NzcxMzA4NDc5LHU6Ijc5YmY2MjM1LWNjYWYtYzgwMi0xZDJkLTE1OWNlOTFlODJiNiIsZGU6MyxsZjowfQ","cert":"CsECCAMSEBcFuRfMEgSGiwYzOi93KowYgrSCkgUijgIwggEKAoIBAQCZ7Vs7Mn2rXiTvw7YqlbWYUgrVvMs3UD4GRbgU2Ha430BRBEGtjOOtsRu4jE5yWl5KngeVKR1YWEAjp-GvDjipEnk5MAhhC28VjIeMfiG_-_7qd-EBnh5XgeikX0YmPRTmDoBYqGB63OBPrIRXsTeo1nzN6zNwXZg6IftO7L1KEMpHSQykfqpdQ4IY3brxyt4zkvE9b_tkQv0x4b9AsMYE0cS6TJUgpL-X7r1gkpr87vVbuvVk4tDnbNfFXHOggrmWEguDWe3OJHBwgmgNb2fG2CxKxfMTRJCnTuw3r0svAQxZ6ChD4lgvC2ufXbD8Xm7fZPvTCLRxG88SUAGcn1oJAgMBAAE6FGxpY2Vuc2Uud2lkZXZpbmUuY29tEoADrjRzFLWoNSl_JxOI-3u4y1J30kmCPN3R2jC5MzlRHrPMveoEuUS5J8EhNG79verJ1BORfm7BdqEEOEYKUDvBlSubpOTOD8S_wgqYCKqvS_zRnB3PzfV0zKwo0bQQQWz53ogEMBy9szTK_NDUCXhCOmQuVGE98K_PlspKkknYVeQrOnA-8XZ_apvTbWv4K-drvwy6T95Z0qvMdv62Qke4XEMfvKUiZrYZ_DaXlUP8qcu9u_r6DhpV51Wjx7zmVflkb1gquc9wqgi5efhn9joLK3_bNixbxOzVVdhbyqnFk8ODyFfUnaq3fkC3hR3f0kmYgI41sljnXXjqwMoW9wRzBMINk-3k6P8cbxfmJD4_Paj8FwmHDsRfuoI6Jj8M76H3CTsZCZKDJjM3BQQ6Kb2m-bQ0LMjfVDyxoRgvfF__M_EEkPrKWyU2C3YBXpxaBquO4C8A0ujVmGEEqsxN1HX9lu6c5OMm8huDxwWFd7OHMs3avGpr7RP7DUnTikXrh6X0"},"statsBlob":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0di5zb2xvY29vLnN0YXRzYmxvYiI6eyJhdCI6InMiLCJhaSI6IjYwMDIiLCJzcCI6MCwicyI6IjI3YTFlZTgyLTRiYTktNGFkMC04YmJlLTVlNTg4N2RkNWJhOSIsInByIjoiaHR0cHMiLCJtIjowfSwibmJmIjoxNjM4NzcxMzA4LCJleHAiOjE2Mzg5NDQxMDgsImlhdCI6MTYzODc3MTMwOCwiYXVkIjoiY3BpIn0.oUGA1aqkCQUff4qpBnLLTm2H3EN_1vbL3HubjXGA5CU","statsSessionId":"27a1ee82-4ba9-4ad0-8bbe-5e5887dd5ba9","statsAssetType":"s","restrictions":{"blackout":{"intervals":[],"refresh":21600}},"externalId":"id-6002"}

@coloms90
Copy link

Hello, Can you guide me or send me the automated script?
To get license server DRM, after insert key DRM watch live channel anytime with link live.
Here is my Kodi build:
#EXTINF:-1 tvg-logo="https://i.ibb.co/LXqDQVDY/DShow8.png" group-title="VARIEDADES",DShow
#KODIPROP:inputstreamaddon=inputstream.adaptive
#KODIPROP:inputstream.adaptive.drm_legacy=com.widevine.alpha|https://cpix.tbxdrm.com/v1/license/dtvgo/widevine?contentId=Live_1276&userToken=|Content-Type=application/octet-stream|R{SSM}|
https://1276-aws-da-mt.vrioott.com/v1/dash/f761a4100cd1d44ec919aa9543945ae7f63d684e/live_1276/e8168ef8be114ac6944cac497b69d7d3/manifest.mpd

@jaylinski
Copy link
Author

jaylinski commented Oct 16, 2025

@coloms90

package.json

{
  "name": "kodi-pvr-drm-bot",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "dependencies": {
    "@octokit/core": "^7.0.2"
  },
  "scripts": {
    "start": "node index.js"
  }
}

index.js

import { Octokit } from '@octokit/core';

const interval = 30 * 60 * 1000; // 30 minutes
const gistOrg = '<your gist org>';
const gistId = '<your gist id>';
const octokit = new Octokit();
const octokitAuthenticated = new Octokit({ auth: '<your github auth token>' });
const tokenEndpoint = '<your token endpoint>';

async function updateGist() {
  const tokenResponse = await octokit.request(`GET ${tokenEndpoint}`);
  const token = tokenResponse.data.base64;

  const playlistResponse = await octokit.request('GET https://gist.githubusercontent.com/{org}/{id}/raw/kodi_playlist', { org: gistOrg, id: gistId });
  const playlist = playlistResponse.data;
  const playlistPatched = playlist.replace(/&userToken=(.*)/gi, `&userToken=${token}|Content-Type=application/octet-stream|R{SSM}|`);

  const gistPatchResponse = await octokitAuthenticated.request('PATCH /gists/{id}', {
    id: gistId,
    files: {
      kodi_playlist: {
        content: playlistPatched,
      },
    },
  });

  if (gistPatchResponse.status !== 200) {
    throw new Error(`Error ${gistPatchResponse.status}`);
  } else {
    console.log(`Successfully patched gist with token '${token}'`);
  }
}

setInterval(updateGist, interval);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment