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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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']