Download YouTube Captions

Download subtitles / captions from YouTube in several formats

  1. // ==UserScript==
  2. // @name Download YouTube Captions
  3. // @namespace http://userscripts.org/users/tim
  4. // @include http://*youtube.com/watch*
  5. // @include https://*youtube.com/watch*
  6. // @author Tim Smart
  7. // @copyright 2009 Tim Smart; 2011 gw111zz
  8. // @license GNU GPL v3.0 or later. http://www.gnu.org/copyleft/gpl.html
  9. // @version 0.0.1.20140425034707
  10. // @description Download subtitles / captions from YouTube in several formats
  11. // ==/UserScript==
  12.  
  13. var PLAYER = unsafeWindow.document.getElementById('movie_player'),
  14. VIDEO_ID = unsafeWindow.yt.getConfig('VIDEO_ID'),
  15. TITLE = unsafeWindow.ytplayer.config.args.title,
  16. FORMATS = {srt: '.srt', text: 'text'},
  17. FORMAT_SELECTOR_ID = 'format_selector',
  18. caption_array = [];
  19.  
  20. var makeTimeline = function (time) {
  21. var string,
  22. time_array = [],
  23. milliseconds = Math.round(time % 1 * 1000).toString();
  24.  
  25. while (3 > milliseconds.length) {
  26. milliseconds = '0' + milliseconds;
  27. }
  28.  
  29. time_array.push(Math.floor(time / (60 * 60)));
  30. time_array.push(Math.floor((time - (time_array[0] * 60 * 60)) / 60));
  31. time_array.push(Math.floor(time - ((time_array[1] * 60) + (time_array[0] * 60 * 60))));
  32.  
  33. for (var i = 0, il = time_array.length; i < il; i++) {
  34. string = '' + time_array[i];
  35.  
  36. if (1 === string.length) {
  37. time_array[i] = '0' + string;
  38. }
  39. }
  40.  
  41. return time_array.join(":") + "," + milliseconds;
  42. }
  43.  
  44. function loadCaption (selector) {
  45. var caption = caption_array[selector.selectedIndex - 1];
  46.  
  47. if (!caption) return;
  48.  
  49. GM_xmlhttpRequest({
  50. method: 'GET',
  51. url: 'http://video.google.com/timedtext?hl=' + caption.lang_code +
  52. '&lang=' + caption.lang_code + '&name=' + caption.name + '&v=' + VIDEO_ID,
  53. onload:function(xhr) {
  54. if (xhr.responseText !== "") {
  55. var caption, previous_start, start, end,
  56. captions = new DOMParser().parseFromString(xhr.responseText, "text/xml").getElementsByTagName('text'),
  57. textarea = document.createElement("textarea"),
  58. output_format = FORMATS[document.getElementById(FORMAT_SELECTOR_ID).value] || FORMATS['srt'],
  59. srt_output = '';
  60.  
  61. for (var i = 0, il = captions.length; i < il; i++) {
  62. caption = captions[i];
  63. start = +caption.getAttribute('start');
  64.  
  65. if (0 <= previous_start) {
  66. textarea.innerHTML = captions[i - 1].textContent.replace(/</g, "&lt;").
  67. replace( />/g, "&gt;" );
  68. if (output_format === FORMATS['text']) {
  69. srt_output += textarea.value + "\n";
  70. } else {
  71. srt_output += (i + 1) + "\n" + makeTimeline(previous_start) + ' --> ' +
  72. makeTimeline(start) + "\n" + textarea.value + "\n\n";
  73. }
  74. previous_start = null;
  75. }
  76.  
  77. if (end = +caption.getAttribute('dur')) {
  78. end = start + end;
  79. } else {
  80. if (captions[i + 1]) {
  81. previous_start = start;
  82. continue;
  83. }
  84. end = PLAYER.getDuration();
  85. }
  86.  
  87. textarea.innerHTML = caption.textContent.replace(/</g, "&lt;").replace(/>/g, "&gt;");
  88. if (output_format === FORMATS['text']) {
  89. srt_output += textarea.value + "\n";
  90. } else {
  91. srt_output += (i + 1) + "\n" + makeTimeline(start) + ' --> ' +
  92. makeTimeline(end) + "\n" + textarea.value + "\n\n";
  93. }
  94. }
  95.  
  96. textarea = null;
  97.  
  98. GM_openInTab("data:text/srt;charset=utf-8," + encodeURIComponent(srt_output));
  99. } else {
  100. alert("Error: No response from server.");
  101. }
  102.  
  103. selector.options[0].selected = true;
  104.  
  105. }
  106. });
  107. }
  108.  
  109. function loadCaptions (select) {
  110. GM_xmlhttpRequest({
  111. method: 'GET',
  112. url: 'http://video.google.com/timedtext?hl=en&v=' + VIDEO_ID + '&type=list',
  113. onload: function( xhr ) {
  114.  
  115. var caption, option, caption_info,
  116. captions = new DOMParser().parseFromString(xhr.responseText, "text/xml").
  117. getElementsByTagName('track');
  118. if (captions.length === 0) {
  119. return select.options[0].textContent = 'No captions.';
  120. }
  121.  
  122. for (var i = 0, il = captions.length; i < il; i++) {
  123. caption = captions[i];
  124. option = document.createElement('option');
  125. caption_info = {
  126. name: caption.getAttribute('name'),
  127. lang_code: caption.getAttribute('lang_code'),
  128. lang_name: caption.getAttribute('lang_translated')
  129. };
  130.  
  131. caption_array.push(caption_info);
  132. option.textContent = caption_info.lang_name;
  133.  
  134. select.appendChild(option);
  135. }
  136.  
  137. select.options[0].textContent = 'Download captions.';
  138. select.disabled = false;
  139. }
  140. });
  141. }
  142.  
  143. function loadFormats (select) {
  144. var type
  145.  
  146. for (type in FORMATS) {
  147. option = document.createElement('option');
  148. option.value = type;
  149. option.textContent = FORMATS[type];
  150.  
  151. select.appendChild(option);
  152. }
  153.  
  154. select.options[0].textContent = 'Select caption format.';
  155. select.disabled = false;
  156. }
  157.  
  158. (function () {
  159. var div = document.createElement('div'),
  160. select = document.createElement('select'),
  161. option = document.createElement('option'),
  162. controls = document.getElementById('watch7-headline');
  163.  
  164. div.setAttribute( 'style', 'display: inline-block;' );
  165.  
  166. select.id = 'captions_selector';
  167. select.disabled = true;
  168.  
  169. option.textContent = 'Loading...';
  170. option.selected = true;
  171.  
  172. select.appendChild(option);
  173. select.addEventListener('change', function() {
  174. loadCaption(this);
  175. }, false);
  176.  
  177. div.appendChild(select);
  178.  
  179.  
  180. var format_div = document.createElement('div'),
  181. format_select = document.createElement('select'),
  182. format_option = document.createElement('option');
  183.  
  184. format_div.setAttribute('style', 'display: inline-block;');
  185.  
  186. format_select.id = FORMAT_SELECTOR_ID;
  187. format_select.disabled = true;
  188.  
  189. format_option.textContent = 'Loading...';
  190. format_option.selected = true;
  191.  
  192. format_select.appendChild(format_option);
  193.  
  194. format_div.appendChild(format_select);
  195.  
  196. controls.appendChild(format_div);
  197. controls.appendChild(div);
  198.  
  199. loadFormats(format_select);
  200. loadCaptions(select);
  201. })();
  202.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址