You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
10 KiB

10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
  1. #!/usr/bin/env python2
  2. # -*- coding: utf-8 -*-
  3. """Youtubedlg module responsible for parsing the options. """
  4. from __future__ import unicode_literals
  5. import os.path
  6. from .utils import (
  7. remove_shortcuts,
  8. to_string
  9. )
  10. class OptionHolder(object):
  11. """Simple data structure that holds informations for the given option.
  12. Args:
  13. name (string): Option name. Must be a valid option name
  14. from the optionsmanager.OptionsManager class.
  15. See optionsmanager.OptionsManager load_default() method.
  16. flag (string): The option command line switch.
  17. See https://github.com/rg3/youtube-dl/#options
  18. default_value (any): The option default value. Must be the same type
  19. with the corresponding option from the optionsmanager.OptionsManager
  20. class.
  21. requirements (list): The requirements for the given option. This
  22. argument is a list of strings with the name of all the options
  23. that this specific option needs. For example 'subs_lang' needs the
  24. 'write_subs' option to be enabled.
  25. """
  26. def __init__(self, name, flag, default_value, requirements=None):
  27. self.name = name
  28. self.flag = flag
  29. self.requirements = requirements
  30. self.default_value = default_value
  31. def is_boolean(self):
  32. """Returns True if the option is a boolean switch else False. """
  33. return type(self.default_value) is bool
  34. def check_requirements(self, options_dict):
  35. """Check if the required options are enabled.
  36. Args:
  37. options_dict (dict): Dictionary with all the options.
  38. Returns:
  39. True if any of the required options is enabled else False.
  40. """
  41. if self.requirements is None:
  42. return True
  43. return any([options_dict[req] for req in self.requirements])
  44. class OptionsParser(object):
  45. """Parse optionsmanager.OptionsManager options.
  46. This class is responsible for turning some of the youtube-dlg options
  47. to youtube-dl command line options.
  48. """
  49. def __init__(self):
  50. self._ydl_options = [
  51. OptionHolder('playlist_start', '--playlist-start', 1),
  52. OptionHolder('playlist_end', '--playlist-end', 0),
  53. OptionHolder('max_downloads', '--max-downloads', 0),
  54. OptionHolder('username', '-u', ''),
  55. OptionHolder('password', '-p', ''),
  56. OptionHolder('video_password', '--video-password', ''),
  57. OptionHolder('retries', '-R', 10),
  58. OptionHolder('proxy', '--proxy', ''),
  59. OptionHolder('user_agent', '--user-agent', ''),
  60. OptionHolder('referer', '--referer', ''),
  61. OptionHolder('ignore_errors', '-i', False),
  62. OptionHolder('write_description', '--write-description', False),
  63. OptionHolder('write_info', '--write-info-json', False),
  64. OptionHolder('write_thumbnail', '--write-thumbnail', False),
  65. OptionHolder('min_filesize', '--min-filesize', 0),
  66. OptionHolder('max_filesize', '--max-filesize', 0),
  67. OptionHolder('write_all_subs', '--all-subs', False),
  68. OptionHolder('write_auto_subs', '--write-auto-sub', False),
  69. OptionHolder('write_subs', '--write-sub', False),
  70. OptionHolder('keep_video', '-k', False),
  71. OptionHolder('restrict_filenames', '--restrict-filenames', False),
  72. OptionHolder('save_path', '-o', ''),
  73. OptionHolder('embed_subs', '--embed-subs', False, ['write_auto_subs', 'write_subs']),
  74. OptionHolder('to_audio', '-x', False),
  75. OptionHolder('audio_format', '--audio-format', ''),
  76. OptionHolder('video_format', '-f', '0'),
  77. OptionHolder('subs_lang', '--sub-lang', '', ['write_subs']),
  78. OptionHolder('audio_quality', '--audio-quality', '5', ['to_audio']),
  79. OptionHolder('youtube_dl_debug', '-v', False),
  80. OptionHolder('ignore_config', '--ignore-config', False),
  81. OptionHolder('native_hls', '--hls-prefer-native', False),
  82. OptionHolder('nomtime', '--no-mtime', False),
  83. OptionHolder('embed_thumbnail', '--embed-thumbnail', False),
  84. OptionHolder('add_metadata', '--add-metadata', False)
  85. ]
  86. def parse(self, options_dictionary):
  87. """Parse optionsmanager.OptionsManager options.
  88. Parses the given options to youtube-dl command line arguments.
  89. Args:
  90. options_dictionary (dict): Dictionary with all the options.
  91. Returns:
  92. List of strings with all the youtube-dl command line options.
  93. """
  94. # REFACTOR
  95. options_list = ['--newline']
  96. # Create a copy of options_dictionary
  97. # We don't want to edit the original options dictionary
  98. # and change some of the options values like 'save_path' etc..
  99. options_dict = options_dictionary.copy()
  100. self._build_savepath(options_dict)
  101. self._build_videoformat(options_dict)
  102. self._build_filesizes(options_dict)
  103. # Parse basic youtube-dl command line options
  104. for option in self._ydl_options:
  105. #NOTE Special case should be removed
  106. if option.name == "to_audio":
  107. if options_dict["audio_format"] == "":
  108. value = options_dict[option.name]
  109. if value != option.default_value:
  110. options_list.append(option.flag)
  111. elif option.name == "audio_format":
  112. value = options_dict[option.name]
  113. if value != option.default_value:
  114. options_list.append("-x")
  115. options_list.append(option.flag)
  116. options_list.append(to_string(value))
  117. #NOTE Temp fix
  118. # If current 'audio_quality' is not the default one ('5')
  119. # then append the audio quality flag and value to the
  120. # options list
  121. if options_dict["audio_quality"] != "5":
  122. options_list.append("--audio-quality")
  123. options_list.append(to_string(options_dict["audio_quality"]))
  124. elif option.name == "audio_quality":
  125. # If the '--audio-quality' is not already in the options list
  126. # from the above branch then follow the standard procedure.
  127. # We don't have to worry for the sequence in which the code
  128. # will be executed since the 'audio_quality' option is placed
  129. # after the 'audio_format' option in the self._ydl_options list
  130. if option.flag not in options_list:
  131. if option.check_requirements(options_dict):
  132. value = options_dict[option.name]
  133. if value != option.default_value:
  134. options_list.append(option.flag)
  135. options_list.append(to_string(value))
  136. elif option.check_requirements(options_dict):
  137. value = options_dict[option.name]
  138. if value != option.default_value:
  139. options_list.append(option.flag)
  140. if not option.is_boolean():
  141. options_list.append(to_string(value))
  142. # Parse cmd_args
  143. # Indicates whether an item needs special handling
  144. special_case = False
  145. # Temp list to hold special items
  146. special_items = []
  147. for item in options_dict["cmd_args"].split():
  148. # Its a special case if its already a special case
  149. # or an item starts with double quotes
  150. special_case = (special_case or item[0] == "\"")
  151. if special_case:
  152. special_items.append(item)
  153. else:
  154. options_list.append(item)
  155. # If its a special case and we meet a double quote
  156. # at the end of the item, special case is over and
  157. # we need to join, filter and append our special items
  158. # to the options list
  159. if special_case and item[-1] == "\"":
  160. options_list.append(" ".join(special_items)[1:-1])
  161. special_case = False
  162. special_items = []
  163. return options_list
  164. def _build_savepath(self, options_dict):
  165. """Build the save path.
  166. We use this method to build the value of the 'save_path' option and
  167. store it back to the options dictionary.
  168. Args:
  169. options_dict (dict): Copy of the original options dictionary.
  170. """
  171. save_path = remove_shortcuts(options_dict['save_path'])
  172. if options_dict["output_format"] == 0:
  173. template = "%(id)s.%(ext)s"
  174. elif options_dict["output_format"] == 1:
  175. template = "%(title)s.%(ext)s"
  176. elif options_dict["output_format"] == 2:
  177. template = "%(title)s-%(id)s.%(ext)s"
  178. elif options_dict["output_format"] == 4:
  179. template = "%(title)s-%(height)sp.%(ext)s"
  180. elif options_dict["output_format"] == 5:
  181. template = "%(title)s-%(id)s-%(height)sp.%(ext)s"
  182. else:
  183. template = options_dict["output_template"]
  184. options_dict["save_path"] = os.path.join(save_path, template)
  185. def _build_videoformat(self, options_dict):
  186. """Build the video format.
  187. We use this method to build the value of the 'video_format' option and
  188. store it back to the options dictionary.
  189. Args:
  190. options_dict (dict): Copy of the original options dictionary.
  191. """
  192. if options_dict['video_format'] != '0' and options_dict['second_video_format'] != '0':
  193. options_dict['video_format'] = options_dict['video_format'] + '+' + options_dict['second_video_format']
  194. def _build_filesizes(self, options_dict):
  195. """Build the filesize options values.
  196. We use this method to build the values of 'min_filesize' and
  197. 'max_filesize' options and store them back to options dictionary.
  198. Args:
  199. options_dict (dict): Copy of the original options dictionary.
  200. """
  201. if options_dict['min_filesize']:
  202. options_dict['min_filesize'] = to_string(options_dict['min_filesize']) + options_dict['min_filesize_unit']
  203. if options_dict['max_filesize']:
  204. options_dict['max_filesize'] = to_string(options_dict['max_filesize']) + options_dict['max_filesize_unit']