NRK subtitle download

Download subtitles from tv.nrk.no

  1. // ==UserScript==
  2. // @name NRK subtitle download
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Download subtitles from tv.nrk.no
  6. // @author cvzi
  7. // @copyright 2021, cuzi (https://openuserjs.org/users/cuzi)
  8. // @license MIT
  9. // @match https://tv.nrk.no/*
  10. // @icon https://tv.nrk.no/apple-touch-icon.png
  11. // @grant GM.xmlHttpRequest
  12. // @grant GM.registerMenuCommand
  13. // ==/UserScript==
  14.  
  15. /* globals GM */
  16.  
  17. (function () {
  18. 'use strict'
  19. const PROGRAM_URL = 'https://psapi.nrk.no/programs/$pid'
  20.  
  21. function container () {
  22. let div = document.getElementById('videosrcs84519')
  23. if (!div) {
  24. div = document.createElement('div')
  25. div.id = 'videosrcs84519'
  26. document.body.appendChild(div)
  27. document.head.appendChild(document.createElement('style')).innerHTML = `
  28. #videosrcs84519 {
  29. position:fixed;
  30. z-index:99999;
  31. top:0px;
  32. left:0px;
  33. padding:5px;
  34. margin:0px;
  35. background:white;
  36. color:black;
  37. border:2px solid black;
  38. font-family:monospace;
  39. font-size:14px;
  40. }
  41.  
  42. #videosrcs84519 a:link {color:blue; text-decoration:underline;font-size:14px;font-family:monospace;}
  43. #videosrcs84519 a:visited {color:#660973; text-decoration:underline;font-size:14px;font-family:monospace;}
  44. `
  45. }
  46. div.innerHTML = ''
  47. return div
  48. }
  49. function link (url) {
  50. const a = document.createElement('a')
  51. a.href = url
  52. a.target = '_blank'
  53. let text = url.toString()
  54. a.title = text
  55. if (text.length > 160) {
  56. text = text.substring(0, 78) + '...' + text.substring(text.length - 78)
  57. }
  58. a.appendChild(document.createTextNode(text))
  59. return a
  60. }
  61.  
  62. const lst = new Set()
  63. const m3u8s = new Set()
  64. function m3u8 (url) {
  65. if (!url.endsWith('m3u8') || m3u8s.has(url)) {
  66. return
  67. }
  68. m3u8s.add(url)
  69. GM.xmlHttpRequest({
  70. method: 'GET',
  71. url: url,
  72. onload: function (response) {
  73. response.responseText.split('\n').forEach(function (line) {
  74. if (line.trim().startsWith('#')) {
  75. return
  76. }
  77. if (line.startsWith('http')) {
  78. lst.add(line)
  79. } else {
  80. const parts = url.split('/')
  81. parts.pop()
  82. parts.push(line)
  83. lst.add(parts.join('/'))
  84. }
  85. })
  86. listUrls()
  87. }
  88. })
  89. }
  90.  
  91. function findProgramId () {
  92. if (document.querySelector('meta[property="nrk:program-id"]')) {
  93. return document.querySelector('meta[property="nrk:program-id"]').content
  94. } else if (document.querySelector('[data-program-id]')) {
  95. return document.querySelector('[data-program-id]').dataset.programId
  96. } else {
  97. let m = document.documentElement.innerHTML.match(/"prfId":"(\w+?)"/)
  98. if (m) {
  99. return m[1]
  100. } else {
  101. m = document.documentElement.innerHTML.match(/prf:(\w+?)/)
  102. if (m) {
  103. return m[1]
  104. }
  105. }
  106. }
  107. return null
  108. }
  109.  
  110. let iv = null
  111. function listUrls () {
  112. const programId = findProgramId()
  113. if (programId) {
  114. GM.xmlHttpRequest({
  115. method: 'GET',
  116. url: PROGRAM_URL.replace('$pid', programId),
  117. onload: function (response) {
  118. const data = JSON.parse(response.responseText)
  119. data.mediaAssetsOnDemand.forEach(function (episode) {
  120. lst.add(decodeURIComponent(episode.webVttSubtitles))
  121. m3u8(decodeURIComponent(episode.webVttSubtitles))
  122. lst.add(decodeURIComponent(episode.timedTextSubtitlesUrl))
  123. m3u8(decodeURIComponent(episode.timedTextSubtitlesUrl))
  124.  
  125. const c = container()
  126. lst.forEach(function (src) {
  127. c.appendChild(link(src))
  128. c.appendChild(document.createElement('br'))
  129. })
  130. let button = c.appendChild(document.createElement('button'))
  131. button.addEventListener('click', function () {
  132. window.clearInterval(iv)
  133. c.remove()
  134. })
  135. button.appendChild(document.createTextNode('Close'))
  136. button = c.appendChild(document.createElement('button'))
  137. button.addEventListener('click', () => listUrls())
  138. button.appendChild(document.createTextNode('Refresh'))
  139. })
  140. }
  141. })
  142. }
  143. }
  144.  
  145. GM.registerMenuCommand('List NRK subtitles', listUrls)
  146. iv = window.setInterval(listUrls, 5000)
  147. })()