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.

952 lines
38 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 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
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. #!/usr/bin/env python2
  2. # -*- coding: utf-8 -*-
  3. """Youtubedlg module responsible for the options window. """
  4. from __future__ import unicode_literals
  5. import os
  6. import gettext
  7. import wx
  8. import wx.combo
  9. from wx.lib.art import flagart
  10. from .utils import (
  11. TwoWayOrderedDict as twodict,
  12. os_path_exists,
  13. get_icon_file,
  14. os_sep
  15. )
  16. from .info import __appname__
  17. from .formats import (
  18. OUTPUT_FORMATS,
  19. VIDEO_FORMATS,
  20. AUDIO_FORMATS
  21. )
  22. #REFACTOR Move all formats, etc to formats.py
  23. class OptionsFrame(wx.Frame):
  24. """Youtubedlg options frame class.
  25. Args:
  26. parent (mainframe.MainFrame): Parent class.
  27. """
  28. FRAME_TITLE = _("Options")
  29. FRAMES_MIN_SIZE = (500, 470)
  30. def __init__(self, parent):
  31. wx.Frame.__init__(self, parent, title=self.FRAME_TITLE, size=parent.opt_manager.options["opts_win_size"])
  32. self.opt_manager = parent.opt_manager
  33. self.log_manager = parent.log_manager
  34. self.app_icon = None
  35. # Set the app icon
  36. #REFACTOR Get icon from parent
  37. app_icon_path = get_icon_file()
  38. if app_icon_path is not None:
  39. self.app_icon = wx.Icon(app_icon_path, wx.BITMAP_TYPE_PNG)
  40. self.SetIcon(self.app_icon)
  41. self._was_shown = False
  42. # Create options frame basic components
  43. self.panel = wx.Panel(self)
  44. self.notebook = wx.Notebook(self.panel)
  45. self.separator_line = wx.StaticLine(self.panel)
  46. self.reset_button = wx.Button(self.panel, label=_("Reset"))
  47. self.close_button = wx.Button(self.panel, label=_("Close"))
  48. # Create tabs
  49. tab_args = (self, self.notebook)
  50. self.tabs = (
  51. (GeneralTab(*tab_args), _("General")),
  52. (FormatsTab(*tab_args), _("Formats")),
  53. (DownloadsTab(*tab_args), _("Downloads")),
  54. (AdvancedTab(*tab_args), _("Advanced")),
  55. (ExtraTab(*tab_args), _("Extra"))
  56. )
  57. # Add tabs on notebook
  58. for tab, label in self.tabs:
  59. self.notebook.AddPage(tab, label)
  60. # Bind events
  61. self.Bind(wx.EVT_BUTTON, self._on_reset, self.reset_button)
  62. self.Bind(wx.EVT_BUTTON, self._on_close, self.close_button)
  63. self.Bind(wx.EVT_CLOSE, self._on_close)
  64. self.SetMinSize(self.FRAMES_MIN_SIZE)
  65. self._set_layout()
  66. self.load_all_options()
  67. def _set_layout(self):
  68. main_sizer = wx.BoxSizer(wx.VERTICAL)
  69. main_sizer.Add(self.notebook, 1, wx.EXPAND | wx.ALL, border=5)
  70. main_sizer.Add(self.separator_line, 0, wx.EXPAND)
  71. buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
  72. buttons_sizer.Add(self.reset_button)
  73. buttons_sizer.AddSpacer((5, -1))
  74. buttons_sizer.Add(self.close_button)
  75. main_sizer.Add(buttons_sizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
  76. self.panel.SetSizer(main_sizer)
  77. self.panel.Layout()
  78. def _on_close(self, event):
  79. """Event handler for wx.EVT_CLOSE event."""
  80. self.save_all_options()
  81. #REFACTOR Parent create specific callback
  82. self.GetParent()._update_videoformat_combobox()
  83. self.Hide()
  84. def _on_reset(self, event):
  85. """Event handler for the reset button wx.EVT_BUTTON event."""
  86. self.reset()
  87. self.GetParent().reset()
  88. def reset(self):
  89. """Reset the default options."""
  90. self.opt_manager.load_default()
  91. self.load_all_options()
  92. def load_all_options(self):
  93. """Load all the options on each tab."""
  94. for tab, _ in self.tabs:
  95. tab.load_options()
  96. def save_all_options(self):
  97. """Save all the options from all the tabs back to the OptionsManager."""
  98. for tab, _ in self.tabs:
  99. tab.save_options()
  100. def Show(self, *args, **kwargs):
  101. # CenterOnParent can't go to main frame's __init__ as main frame may change
  102. # own position and options frame won't be centered on main frame anymore.
  103. if not self._was_shown:
  104. self._was_shown = True
  105. self.CenterOnParent()
  106. return wx.Frame.Show(self, *args, **kwargs)
  107. class TabPanel(wx.Panel):
  108. """Main tab class from which each tab inherits.
  109. Args:
  110. parent (OptionsFrame): The parent of all tabs.
  111. notebook (wx.Notebook): The container for each tab.
  112. Notes:
  113. In order to use a different size you must overwrite the below *_SIZE
  114. attributes on the corresponding child object.
  115. """
  116. CHECKBOX_SIZE = (-1, -1)
  117. if os.name == "nt":
  118. # Make checkboxes look the same on Windows
  119. CHECKBOX_SIZE = (-1, 25)
  120. BUTTONS_SIZE = (-1, -1)
  121. TEXTCTRL_SIZE = (-1, -1)
  122. SPINCTRL_SIZE = (70, -1)
  123. CHECKLISTBOX_SIZE = (-1, 80)
  124. LISTBOX_SIZE = (-1, 80)
  125. def __init__(self, parent, notebook):
  126. super(TabPanel, self).__init__(notebook)
  127. #REFACTOR Maybe add methods to access those
  128. #save_options(key, value)
  129. #load_options(key)
  130. self.opt_manager = parent.opt_manager
  131. self.log_manager = parent.log_manager
  132. self.app_icon = parent.app_icon
  133. self.reset_handler = parent.reset
  134. # Shortcut methods below
  135. def crt_button(self, label, event_handler=None):
  136. button = wx.Button(self, label=label, size=self.BUTTONS_SIZE)
  137. if event_handler is not None:
  138. button.Bind(wx.EVT_BUTTON, event_handler)
  139. return button
  140. def crt_checkbox(self, label, event_handler=None):
  141. checkbox = wx.CheckBox(self, label=label, size=self.CHECKBOX_SIZE)
  142. if event_handler is not None:
  143. checkbox.Bind(wx.EVT_CHECKBOX, event_handler)
  144. return checkbox
  145. def crt_textctrl(self, style=None):
  146. if style is None:
  147. textctrl = wx.TextCtrl(self, size=self.TEXTCTRL_SIZE)
  148. else:
  149. textctrl = wx.TextCtrl(self, size=self.TEXTCTRL_SIZE, style=style)
  150. return textctrl
  151. def crt_combobox(self, choices, size=(-1, -1), event_handler=None):
  152. combobox = wx.ComboBox(self, choices=choices, size=size, style=wx.CB_READONLY)
  153. if event_handler is not None:
  154. combobox.Bind(wx.EVT_COMBOBOX, event_handler)
  155. return combobox
  156. def crt_bitmap_combobox(self, choices, size=(-1, -1), event_handler=None):
  157. combobox = wx.combo.BitmapComboBox(self, size=size, style=wx.CB_READONLY)
  158. for item in choices:
  159. lang_code, lang_name = item
  160. _, country = lang_code.split('_')
  161. if country in flagart.catalog:
  162. flag_bmp = flagart.catalog[country].getBitmap()
  163. else:
  164. flag_bmp = flagart.catalog["BLANK"].getBitmap()
  165. combobox.Append(lang_name, flag_bmp)
  166. if event_handler is not None:
  167. combobox.Bind(wx.EVT_COMBOBOX, event_handler)
  168. return combobox
  169. def crt_spinctrl(self, spin_range=(0, 9999)):
  170. spinctrl = wx.SpinCtrl(self, size=self.SPINCTRL_SIZE)
  171. spinctrl.SetRange(*spin_range)
  172. return spinctrl
  173. def crt_statictext(self, label):
  174. return wx.StaticText(self, wx.ID_ANY, label)
  175. def crt_staticbox(self, label):
  176. return wx.StaticBox(self, wx.ID_ANY, label)
  177. def crt_checklistbox(self, choices, style=None):
  178. if style is None:
  179. checklistbox = wx.CheckListBox(self, choices=choices, size=self.CHECKLISTBOX_SIZE)
  180. else:
  181. checklistbox = wx.CheckListBox(self, choices=choices, style=style, size=self.CHECKLISTBOX_SIZE)
  182. return checklistbox
  183. def crt_listbox(self, choices, style=None):
  184. if style is None:
  185. listbox = wx.ListBox(self, choices=choices, size=self.LISTBOX_SIZE)
  186. else:
  187. listbox = wx.ListBox(self, choices=choices, style=style, size=self.LISTBOX_SIZE)
  188. return listbox
  189. class GeneralTab(TabPanel):
  190. # Lang code = <ISO 639-1>_<ISO 3166-1 alpha-2>
  191. LOCALE_NAMES = twodict([
  192. ('ar_SA', 'Arabic'),
  193. ('cs_CZ', 'Czech'),
  194. ('en_US', 'English'),
  195. ('fr_FR', 'French'),
  196. ('de_DE', 'German'),
  197. ('he_IL', 'Hebrew'),
  198. ('hu_HU', 'Hungarian'),
  199. ('it_IT', 'Italian'),
  200. ('ko_KR', 'Korean'),
  201. ('es_MX', 'Mexican Spanish'),
  202. ('pt_BR', 'Portuguese'),
  203. ('ru_RU', 'Russian'),
  204. ('es_ES', 'Spanish'),
  205. ('tr_TR', 'Turkish')
  206. ])
  207. OUTPUT_TEMPLATES = [
  208. "Id",
  209. "Title",
  210. "Ext",
  211. "Uploader",
  212. "Resolution",
  213. "Autonumber",
  214. "",
  215. "View Count",
  216. "Like Count",
  217. "Dislike Count",
  218. "Comment Count",
  219. "Average Rating",
  220. "Age Limit",
  221. "Width",
  222. "Height",
  223. "Extractor",
  224. "",
  225. "Playlist",
  226. "Playlist Index",
  227. ]
  228. BUTTONS_SIZE = (30, -1)
  229. def __init__(self, *args, **kwargs):
  230. super(GeneralTab, self).__init__(*args, **kwargs)
  231. self.language_label = self.crt_statictext(_("Language"))
  232. self.language_combobox = self.crt_bitmap_combobox(list(self.LOCALE_NAMES.items()), event_handler=self._on_language)
  233. self.filename_format_label = self.crt_statictext(_("Filename format"))
  234. self.filename_format_combobox = self.crt_combobox(list(OUTPUT_FORMATS.values()), event_handler=self._on_filename)
  235. self.filename_custom_format = self.crt_textctrl()
  236. self.filename_custom_format_button = self.crt_button("...", self._on_format)
  237. self.filename_opts_label = self.crt_statictext(_("Filename options"))
  238. self.filename_ascii_checkbox = self.crt_checkbox(_("Restrict filenames to ASCII"))
  239. self.more_opts_label = self.crt_statictext(_("More options"))
  240. self.confirm_exit_checkbox = self.crt_checkbox(_("Confirm on exit"))
  241. self.confirm_deletion_checkbox = self.crt_checkbox(_("Confirm item deletion"))
  242. self.show_completion_popup_checkbox = self.crt_checkbox(_("Inform me on download completion"))
  243. self.shutdown_checkbox = self.crt_checkbox(_("Shutdown on download completion"), event_handler=self._on_shutdown)
  244. self.sudo_textctrl = self.crt_textctrl(wx.TE_PASSWORD)
  245. # Build the menu for the custom format button
  246. self.custom_format_menu = self._build_custom_format_menu()
  247. self._set_layout()
  248. if os.name == "nt":
  249. self.sudo_textctrl.Hide()
  250. self.sudo_textctrl.SetToolTip(wx.ToolTip(_("SUDO password")))
  251. def _set_layout(self):
  252. main_sizer = wx.BoxSizer(wx.HORIZONTAL)
  253. vertical_sizer = wx.BoxSizer(wx.VERTICAL)
  254. vertical_sizer.Add(self.language_label)
  255. vertical_sizer.Add(self.language_combobox, flag=wx.EXPAND | wx.ALL, border=5)
  256. vertical_sizer.Add(self.filename_format_label, flag=wx.TOP, border=5)
  257. vertical_sizer.Add(self.filename_format_combobox, flag=wx.EXPAND | wx.ALL, border=5)
  258. custom_format_sizer = wx.BoxSizer(wx.HORIZONTAL)
  259. custom_format_sizer.Add(self.filename_custom_format, 1, wx.ALIGN_CENTER_VERTICAL)
  260. custom_format_sizer.AddSpacer((5, -1))
  261. custom_format_sizer.Add(self.filename_custom_format_button)
  262. vertical_sizer.Add(custom_format_sizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  263. vertical_sizer.Add(self.filename_opts_label, flag=wx.TOP, border=5)
  264. vertical_sizer.Add(self.filename_ascii_checkbox, flag=wx.ALL, border=5)
  265. vertical_sizer.Add(self.more_opts_label, flag=wx.TOP, border=5)
  266. vertical_sizer.Add(self.confirm_exit_checkbox, flag=wx.ALL, border=5)
  267. vertical_sizer.Add(self.confirm_deletion_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  268. vertical_sizer.Add(self.show_completion_popup_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  269. shutdown_sizer = wx.BoxSizer(wx.HORIZONTAL)
  270. shutdown_sizer.Add(self.shutdown_checkbox)
  271. shutdown_sizer.AddSpacer((-1, -1), 1)
  272. shutdown_sizer.Add(self.sudo_textctrl, 1)
  273. vertical_sizer.Add(shutdown_sizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  274. main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)
  275. self.SetSizer(main_sizer)
  276. def _build_custom_format_menu(self):
  277. menu = wx.Menu()
  278. for template in self.OUTPUT_TEMPLATES:
  279. if template:
  280. menu_item = menu.Append(wx.ID_ANY, template)
  281. menu.Bind(wx.EVT_MENU, self._on_template, menu_item)
  282. else:
  283. menu.AppendSeparator()
  284. return menu
  285. def _on_template(self, event):
  286. """Event handler for the wx.EVT_MENU of the custom_format_menu menu items."""
  287. label = self.custom_format_menu.GetLabelText(event.GetId())
  288. label = label.lower().replace(' ', '_')
  289. custom_format = self.filename_custom_format.GetValue()
  290. if label == "ext":
  291. prefix = '.'
  292. else:
  293. prefix = '-'
  294. if not custom_format or custom_format[-1] == os_sep:
  295. # If the custom format is empty or ends with path separator
  296. # remove the prefix
  297. prefix = ''
  298. template = "{0}%({1})s".format(prefix, label)
  299. self.filename_custom_format.SetValue(custom_format + template)
  300. def _on_format(self, event):
  301. """Event handler for the wx.EVT_BUTTON of the filename_custom_format_button."""
  302. event_object_pos = event.EventObject.GetPosition()
  303. event_object_height = event.EventObject.GetSize()[1]
  304. event_object_pos = (event_object_pos[0], event_object_pos[1] + event_object_height)
  305. self.PopupMenu(self.custom_format_menu, event_object_pos)
  306. def _on_language(self, event):
  307. """Event handler for the wx.EVT_COMBOBOX of the language_combobox."""
  308. wx.MessageBox(_("In order for the changes to take effect please restart {0}").format(__appname__),
  309. _("Restart"),
  310. wx.OK | wx.ICON_INFORMATION,
  311. self)
  312. def _on_filename(self, event):
  313. """Event handler for the wx.EVT_COMBOBOX of the filename_format_combobox."""
  314. custom_selected = self.filename_format_combobox.GetValue() == OUTPUT_FORMATS[3]
  315. self.filename_custom_format.Enable(custom_selected)
  316. self.filename_custom_format_button.Enable(custom_selected)
  317. def _on_shutdown(self, event):
  318. """Event handler for the wx.EVT_CHECKBOX of the shutdown_checkbox."""
  319. self.sudo_textctrl.Enable(self.shutdown_checkbox.GetValue())
  320. def load_options(self):
  321. self.language_combobox.SetValue(self.LOCALE_NAMES[self.opt_manager.options["locale_name"]])
  322. self.filename_format_combobox.SetValue(OUTPUT_FORMATS[self.opt_manager.options["output_format"]])
  323. self.filename_custom_format.SetValue(self.opt_manager.options["output_template"])
  324. self.filename_ascii_checkbox.SetValue(self.opt_manager.options["restrict_filenames"])
  325. self.shutdown_checkbox.SetValue(self.opt_manager.options["shutdown"])
  326. self.sudo_textctrl.SetValue(self.opt_manager.options["sudo_password"])
  327. self.confirm_exit_checkbox.SetValue(self.opt_manager.options["confirm_exit"])
  328. self.show_completion_popup_checkbox.SetValue(self.opt_manager.options["show_completion_popup"])
  329. self.confirm_deletion_checkbox.SetValue(self.opt_manager.options["confirm_deletion"])
  330. #REFACTOR Automatically call on the new methods
  331. #save_options
  332. #load_options
  333. #NOTE Maybe on init add callback?
  334. self._on_filename(None)
  335. self._on_shutdown(None)
  336. def save_options(self):
  337. self.opt_manager.options["locale_name"] = self.LOCALE_NAMES[self.language_combobox.GetValue()]
  338. self.opt_manager.options["output_format"] = OUTPUT_FORMATS[self.filename_format_combobox.GetValue()]
  339. self.opt_manager.options["output_template"] = self.filename_custom_format.GetValue()
  340. self.opt_manager.options["restrict_filenames"] = self.filename_ascii_checkbox.GetValue()
  341. self.opt_manager.options["shutdown"] = self.shutdown_checkbox.GetValue()
  342. self.opt_manager.options["sudo_password"] = self.sudo_textctrl.GetValue()
  343. self.opt_manager.options["confirm_exit"] = self.confirm_exit_checkbox.GetValue()
  344. self.opt_manager.options["show_completion_popup"] = self.show_completion_popup_checkbox.GetValue()
  345. self.opt_manager.options["confirm_deletion"] = self.confirm_deletion_checkbox.GetValue()
  346. class FormatsTab(TabPanel):
  347. AUDIO_QUALITY = twodict([("0", _("high")), ("5", _("mid")), ("9", _("low"))])
  348. def __init__(self, *args, **kwargs):
  349. super(FormatsTab, self).__init__(*args, **kwargs)
  350. self.video_formats_label = self.crt_statictext(_("Video formats"))
  351. self.video_formats_checklistbox = self.crt_checklistbox(list(VIDEO_FORMATS.values()))
  352. self.audio_formats_label = self.crt_statictext(_("Audio formats"))
  353. self.audio_formats_checklistbox = self.crt_checklistbox(list(AUDIO_FORMATS.values()))
  354. self.post_proc_opts_label = self.crt_statictext(_("Post-Process options"))
  355. self.keep_video_checkbox = self.crt_checkbox(_("Keep original files"))
  356. self.extract_audio_checkbox = self.crt_checkbox(_("Extract audio from video file"))
  357. self.embed_thumbnail_checkbox = self.crt_checkbox(_("Embed thumbnail in audio file"))
  358. self.add_metadata_checkbox = self.crt_checkbox(_("Add metadata to file"))
  359. self.audio_quality_label = self.crt_statictext(_("Audio quality"))
  360. self.audio_quality_combobox = self.crt_combobox(list(self.AUDIO_QUALITY.values()))
  361. self._set_layout()
  362. def _set_layout(self):
  363. main_sizer = wx.BoxSizer(wx.HORIZONTAL)
  364. vertical_sizer = wx.BoxSizer(wx.VERTICAL)
  365. vertical_sizer.Add(self.video_formats_label)
  366. vertical_sizer.Add(self.video_formats_checklistbox, 1, wx.EXPAND | wx.ALL, border=5)
  367. vertical_sizer.Add(self.audio_formats_label, flag=wx.TOP, border=5)
  368. vertical_sizer.Add(self.audio_formats_checklistbox, 1, wx.EXPAND | wx.ALL, border=5)
  369. vertical_sizer.Add(self.post_proc_opts_label, flag=wx.TOP, border=5)
  370. vertical_sizer.Add(self.keep_video_checkbox, flag=wx.ALL, border=5)
  371. vertical_sizer.Add(self.extract_audio_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  372. vertical_sizer.Add(self.embed_thumbnail_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  373. vertical_sizer.Add(self.add_metadata_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  374. audio_quality_sizer = wx.BoxSizer(wx.HORIZONTAL)
  375. audio_quality_sizer.Add(self.audio_quality_label, flag=wx.ALIGN_CENTER_VERTICAL)
  376. audio_quality_sizer.AddSpacer((20, -1))
  377. audio_quality_sizer.Add(self.audio_quality_combobox)
  378. vertical_sizer.Add(audio_quality_sizer, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  379. main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)
  380. self.SetSizer(main_sizer)
  381. def load_options(self):
  382. checked_video_formats = [VIDEO_FORMATS[vformat] for vformat in self.opt_manager.options["selected_video_formats"]]
  383. self.video_formats_checklistbox.SetCheckedStrings(checked_video_formats)
  384. checked_audio_formats = [AUDIO_FORMATS[aformat] for aformat in self.opt_manager.options["selected_audio_formats"]]
  385. self.audio_formats_checklistbox.SetCheckedStrings(checked_audio_formats)
  386. self.keep_video_checkbox.SetValue(self.opt_manager.options["keep_video"])
  387. self.audio_quality_combobox.SetValue(self.AUDIO_QUALITY[self.opt_manager.options["audio_quality"]])
  388. self.extract_audio_checkbox.SetValue(self.opt_manager.options["to_audio"])
  389. self.embed_thumbnail_checkbox.SetValue(self.opt_manager.options["embed_thumbnail"])
  390. self.add_metadata_checkbox.SetValue(self.opt_manager.options["add_metadata"])
  391. def save_options(self):
  392. checked_video_formats = [VIDEO_FORMATS[vformat] for vformat in self.video_formats_checklistbox.GetCheckedStrings()]
  393. self.opt_manager.options["selected_video_formats"] = checked_video_formats
  394. checked_audio_formats = [AUDIO_FORMATS[aformat] for aformat in self.audio_formats_checklistbox.GetCheckedStrings()]
  395. self.opt_manager.options["selected_audio_formats"] = checked_audio_formats
  396. self.opt_manager.options["keep_video"] = self.keep_video_checkbox.GetValue()
  397. self.opt_manager.options["audio_quality"] = self.AUDIO_QUALITY[self.audio_quality_combobox.GetValue()]
  398. self.opt_manager.options["to_audio"] = self.extract_audio_checkbox.GetValue()
  399. self.opt_manager.options["embed_thumbnail"] = self.embed_thumbnail_checkbox.GetValue()
  400. self.opt_manager.options["add_metadata"] = self.add_metadata_checkbox.GetValue()
  401. class DownloadsTab(TabPanel):
  402. # Lang code = ISO 639-1
  403. SUBS_LANG = twodict([
  404. ("en", _("English")),
  405. ("fr", _("French")),
  406. ("de", _("German")),
  407. ("el", _("Greek")),
  408. ("he", _("Hebrew")),
  409. ("it", _("Italian")),
  410. ("pt", _("Portuguese")),
  411. ("ru", _("Russian")),
  412. ("es", _("Spanish")),
  413. ("sv", _("Swedish")),
  414. ("tr", _("Turkish"))
  415. ])
  416. FILESIZES = twodict([
  417. ("", "Bytes"),
  418. ("k", "Kilobytes"),
  419. ("m", "Megabytes"),
  420. ("g", "Gigabytes"),
  421. ("t", "Terabytes"),
  422. ("p", "Petabytes"),
  423. ("e", "Exabytes"),
  424. ("z", "Zettabytes"),
  425. ("y", "Yottabytes")
  426. ])
  427. SUBS_CHOICES = [
  428. _("None"),
  429. _("Automatic subtitles (YOUTUBE ONLY)"),
  430. _("All available subtitles"),
  431. _("Subtitles by language")
  432. ]
  433. def __init__(self, *args, **kwargs):
  434. super(DownloadsTab, self).__init__(*args, **kwargs)
  435. self.subtitles_label = self.crt_statictext(_("Subtitles"))
  436. self.subtitles_combobox = self.crt_combobox(self.SUBS_CHOICES, event_handler=self._on_subtitles)
  437. self.subtitles_lang_listbox = self.crt_listbox(list(self.SUBS_LANG.values()))
  438. self.subtitles_opts_label = self.crt_statictext(_("Subtitles options"))
  439. self.embed_subs_checkbox = self.crt_checkbox(_("Embed subtitles into video file (mp4 ONLY)"))
  440. self.playlist_box = self.crt_staticbox(_("Playlist"))
  441. self.playlist_start_label = self.crt_statictext(_("Start"))
  442. self.playlist_start_spinctrl = self.crt_spinctrl((1, 9999))
  443. self.playlist_stop_label = self.crt_statictext(_("Stop"))
  444. self.playlist_stop_spinctrl = self.crt_spinctrl()
  445. self.playlist_max_label = self.crt_statictext(_("Max"))
  446. self.playlist_max_spinctrl = self.crt_spinctrl()
  447. self.filesize_box = self.crt_staticbox(_("Filesize"))
  448. self.filesize_max_label = self.crt_statictext(_("Max"))
  449. self.filesize_max_spinctrl = self.crt_spinctrl((0, 1024))
  450. self.filesize_max_sizeunit_combobox = self.crt_combobox(list(self.FILESIZES.values()))
  451. self.filesize_min_label = self.crt_statictext(_("Min"))
  452. self.filesize_min_spinctrl = self.crt_spinctrl((0, 1024))
  453. self.filesize_min_sizeunit_combobox = self.crt_combobox(list(self.FILESIZES.values()))
  454. self._set_layout()
  455. def _set_layout(self):
  456. main_sizer = wx.BoxSizer(wx.HORIZONTAL)
  457. vertical_sizer = wx.BoxSizer(wx.VERTICAL)
  458. vertical_sizer.Add(self.subtitles_label)
  459. vertical_sizer.Add(self.subtitles_combobox, flag=wx.EXPAND | wx.ALL, border=5)
  460. vertical_sizer.Add(self.subtitles_lang_listbox, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  461. vertical_sizer.Add(self.subtitles_opts_label, flag=wx.TOP, border=5)
  462. vertical_sizer.Add(self.embed_subs_checkbox, flag=wx.ALL, border=5)
  463. plist_and_fsize_sizer = wx.BoxSizer(wx.HORIZONTAL)
  464. plist_and_fsize_sizer.Add(self._build_playlist_sizer(), 1, wx.EXPAND)
  465. plist_and_fsize_sizer.AddSpacer((5, -1))
  466. plist_and_fsize_sizer.Add(self._build_filesize_sizer(), 1, wx.EXPAND)
  467. vertical_sizer.Add(plist_and_fsize_sizer, 1, wx.EXPAND | wx.TOP, border=5)
  468. main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)
  469. self.SetSizer(main_sizer)
  470. def _build_playlist_sizer(self):
  471. playlist_box_sizer = wx.StaticBoxSizer(self.playlist_box, wx.VERTICAL)
  472. playlist_box_sizer.AddSpacer((-1, 10))
  473. border = wx.GridBagSizer(5, 40)
  474. border.Add(self.playlist_start_label, (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  475. border.Add(self.playlist_start_spinctrl, (0, 1))
  476. border.Add(self.playlist_stop_label, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  477. border.Add(self.playlist_stop_spinctrl, (1, 1))
  478. border.Add(self.playlist_max_label, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  479. border.Add(self.playlist_max_spinctrl, (2, 1))
  480. playlist_box_sizer.Add(border, flag=wx.ALIGN_CENTER)
  481. return playlist_box_sizer
  482. def _build_filesize_sizer(self):
  483. filesize_box_sizer = wx.StaticBoxSizer(self.filesize_box, wx.VERTICAL)
  484. border = wx.GridBagSizer(5, 20)
  485. border.Add(self.filesize_max_label, (0, 0), (1, 2), wx.ALIGN_CENTER_HORIZONTAL)
  486. border.Add(self.filesize_max_spinctrl, (1, 0))
  487. border.Add(self.filesize_max_sizeunit_combobox, (1, 1))
  488. border.Add(self.filesize_min_label, (2, 0), (1, 2), wx.ALIGN_CENTER_HORIZONTAL)
  489. border.Add(self.filesize_min_spinctrl, (3, 0))
  490. border.Add(self.filesize_min_sizeunit_combobox, (3, 1))
  491. filesize_box_sizer.Add(border, flag=wx.ALIGN_CENTER)
  492. return filesize_box_sizer
  493. def _on_subtitles(self, event):
  494. """Event handler for the wx.EVT_COMBOBOX of the subtitles_combobox."""
  495. self.subtitles_lang_listbox.Enable(self.subtitles_combobox.GetValue() == self.SUBS_CHOICES[-1])
  496. def load_options(self):
  497. #NOTE Find a better way to do this
  498. if self.opt_manager.options["write_subs"]:
  499. self.subtitles_combobox.SetValue(self.SUBS_CHOICES[3])
  500. elif self.opt_manager.options["write_all_subs"]:
  501. self.subtitles_combobox.SetValue(self.SUBS_CHOICES[2])
  502. elif self.opt_manager.options["write_auto_subs"]:
  503. self.subtitles_combobox.SetValue(self.SUBS_CHOICES[1])
  504. else:
  505. self.subtitles_combobox.SetValue(self.SUBS_CHOICES[0])
  506. self.subtitles_lang_listbox.SetStringSelection(self.SUBS_LANG[self.opt_manager.options["subs_lang"]])
  507. self.embed_subs_checkbox.SetValue(self.opt_manager.options["embed_subs"])
  508. self.playlist_start_spinctrl.SetValue(self.opt_manager.options["playlist_start"])
  509. self.playlist_stop_spinctrl.SetValue(self.opt_manager.options["playlist_end"])
  510. self.playlist_max_spinctrl.SetValue(self.opt_manager.options["max_downloads"])
  511. self.filesize_min_spinctrl.SetValue(self.opt_manager.options["min_filesize"])
  512. self.filesize_max_spinctrl.SetValue(self.opt_manager.options["max_filesize"])
  513. self.filesize_min_sizeunit_combobox.SetValue(self.FILESIZES[self.opt_manager.options["min_filesize_unit"]])
  514. self.filesize_max_sizeunit_combobox.SetValue(self.FILESIZES[self.opt_manager.options["max_filesize_unit"]])
  515. self._on_subtitles(None)
  516. def save_options(self):
  517. subs_choice = self.SUBS_CHOICES.index(self.subtitles_combobox.GetValue())
  518. if subs_choice == 1:
  519. self.opt_manager.options["write_subs"] = False
  520. self.opt_manager.options["write_all_subs"] = False
  521. self.opt_manager.options["write_auto_subs"] = True
  522. elif subs_choice == 2:
  523. self.opt_manager.options["write_subs"] = False
  524. self.opt_manager.options["write_all_subs"] = True
  525. self.opt_manager.options["write_auto_subs"] = False
  526. elif subs_choice == 3:
  527. self.opt_manager.options["write_subs"] = True
  528. self.opt_manager.options["write_all_subs"] = False
  529. self.opt_manager.options["write_auto_subs"] = False
  530. else:
  531. self.opt_manager.options["write_subs"] = False
  532. self.opt_manager.options["write_all_subs"] = False
  533. self.opt_manager.options["write_auto_subs"] = False
  534. self.opt_manager.options["subs_lang"] = self.SUBS_LANG[self.subtitles_lang_listbox.GetStringSelection()]
  535. self.opt_manager.options["embed_subs"] = self.embed_subs_checkbox.GetValue()
  536. self.opt_manager.options["playlist_start"] = self.playlist_start_spinctrl.GetValue()
  537. self.opt_manager.options["playlist_end"] = self.playlist_stop_spinctrl.GetValue()
  538. self.opt_manager.options["max_downloads"] = self.playlist_max_spinctrl.GetValue()
  539. self.opt_manager.options["min_filesize"] = self.filesize_min_spinctrl.GetValue()
  540. self.opt_manager.options["max_filesize"] = self.filesize_max_spinctrl.GetValue()
  541. self.opt_manager.options["min_filesize_unit"] = self.FILESIZES[self.filesize_min_sizeunit_combobox.GetValue()]
  542. self.opt_manager.options["max_filesize_unit"] = self.FILESIZES[self.filesize_max_sizeunit_combobox.GetValue()]
  543. class AdvancedTab(TabPanel):
  544. TEXTCTRL_SIZE = (300, -1)
  545. def __init__(self, *args, **kwargs):
  546. super(AdvancedTab, self).__init__(*args, **kwargs)
  547. self.retries_label = self.crt_statictext(_("Retries"))
  548. self.retries_spinctrl = self.crt_spinctrl((1, 999))
  549. self.auth_label = self.crt_statictext(_("Authentication"))
  550. self.username_label = self.crt_statictext(_("Username"))
  551. self.username_textctrl = self.crt_textctrl()
  552. self.password_label = self.crt_statictext(_("Password"))
  553. self.password_textctrl = self.crt_textctrl(wx.TE_PASSWORD)
  554. self.video_pass_label = self.crt_statictext(_("Video password"))
  555. self.video_pass_textctrl = self.crt_textctrl(wx.TE_PASSWORD)
  556. self.network_label = self.crt_statictext(_("Network"))
  557. self.proxy_label = self.crt_statictext(_("Proxy"))
  558. self.proxy_textctrl = self.crt_textctrl()
  559. self.useragent_label = self.crt_statictext(_("User agent"))
  560. self.useragent_textctrl = self.crt_textctrl()
  561. self.referer_label = self.crt_statictext(_("Referer"))
  562. self.referer_textctrl = self.crt_textctrl()
  563. self.logging_label = self.crt_statictext(_("Logging"))
  564. self.enable_log_checkbox = self.crt_checkbox(_("Enable log"), self._on_enable_log)
  565. self.view_log_button = self.crt_button(_("View"), self._on_view)
  566. self.clear_log_button = self.crt_button(_("Clear"), self._on_clear)
  567. self._set_layout()
  568. if self.log_manager is None:
  569. self.view_log_button.Disable()
  570. self.clear_log_button.Disable()
  571. def _set_layout(self):
  572. main_sizer = wx.BoxSizer(wx.HORIZONTAL)
  573. vertical_sizer = wx.BoxSizer(wx.VERTICAL)
  574. # Set up retries box
  575. retries_sizer = wx.BoxSizer(wx.HORIZONTAL)
  576. retries_sizer.Add(self.retries_label, flag=wx.ALIGN_CENTER_VERTICAL)
  577. retries_sizer.AddSpacer((20, -1))
  578. retries_sizer.Add(self.retries_spinctrl)
  579. vertical_sizer.Add(retries_sizer, flag=wx.ALIGN_RIGHT | wx.TOP | wx.RIGHT, border=5)
  580. # Set up authentication box
  581. vertical_sizer.Add(self.auth_label, flag=wx.TOP, border=10)
  582. auth_sizer = wx.GridBagSizer(5, -1)
  583. auth_sizer.Add(self.username_label, (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  584. auth_sizer.Add(self.username_textctrl, (0, 2))
  585. auth_sizer.Add(self.password_label, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  586. auth_sizer.Add(self.password_textctrl, (1, 2))
  587. auth_sizer.Add(self.video_pass_label, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  588. auth_sizer.Add(self.video_pass_textctrl, (2, 2))
  589. auth_sizer.AddGrowableCol(1)
  590. vertical_sizer.Add(auth_sizer, flag=wx.EXPAND | wx.ALL, border=5)
  591. # Set up network box
  592. vertical_sizer.Add(self.network_label, flag=wx.TOP, border=10)
  593. network_sizer = wx.GridBagSizer(5, -1)
  594. network_sizer.Add(self.proxy_label, (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  595. network_sizer.Add(self.proxy_textctrl, (0, 2))
  596. network_sizer.Add(self.useragent_label, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  597. network_sizer.Add(self.useragent_textctrl, (1, 2))
  598. network_sizer.Add(self.referer_label, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  599. network_sizer.Add(self.referer_textctrl, (2, 2))
  600. network_sizer.AddGrowableCol(1)
  601. vertical_sizer.Add(network_sizer, flag=wx.EXPAND | wx.ALL, border=5)
  602. # Set up logging box
  603. vertical_sizer.Add(self.logging_label, flag=wx.TOP, border=10)
  604. logging_sizer = wx.BoxSizer(wx.HORIZONTAL)
  605. logging_sizer.Add(self.enable_log_checkbox)
  606. logging_sizer.AddSpacer((-1, -1), 1)
  607. logging_sizer.Add(self.view_log_button)
  608. logging_sizer.AddSpacer((5, -1))
  609. logging_sizer.Add(self.clear_log_button)
  610. vertical_sizer.Add(logging_sizer, flag=wx.EXPAND | wx.ALL, border=5)
  611. main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)
  612. self.SetSizer(main_sizer)
  613. def _on_enable_log(self, event):
  614. """Event handler for the wx.EVT_CHECKBOX of the enable_log_checkbox."""
  615. wx.MessageBox(_("In order for the changes to take effect please restart {0}").format(__appname__),
  616. _("Restart"),
  617. wx.OK | wx.ICON_INFORMATION,
  618. self)
  619. def _on_view(self, event):
  620. """Event handler for the wx.EVT_BUTTON of the view_log_button."""
  621. log_window = LogGUI(self)
  622. log_window.load(self.log_manager.log_file)
  623. log_window.Show()
  624. def _on_clear(self, event):
  625. """Event handler for the wx.EVT_BUTTON of the clear_log_button."""
  626. if self.log_manager is not None:
  627. self.log_manager.clear()
  628. def load_options(self):
  629. self.retries_spinctrl.SetValue(self.opt_manager.options["retries"])
  630. self.username_textctrl.SetValue(self.opt_manager.options["username"])
  631. self.password_textctrl.SetValue(self.opt_manager.options["password"])
  632. self.video_pass_textctrl.SetValue(self.opt_manager.options["video_password"])
  633. self.proxy_textctrl.SetValue(self.opt_manager.options["proxy"])
  634. self.useragent_textctrl.SetValue(self.opt_manager.options["user_agent"])
  635. self.referer_textctrl.SetValue(self.opt_manager.options["referer"])
  636. self.enable_log_checkbox.SetValue(self.opt_manager.options["enable_log"])
  637. def save_options(self):
  638. self.opt_manager.options["retries"] = self.retries_spinctrl.GetValue()
  639. self.opt_manager.options["username"] = self.username_textctrl.GetValue()
  640. self.opt_manager.options["password"] = self.password_textctrl.GetValue()
  641. self.opt_manager.options["video_password"] = self.video_pass_textctrl.GetValue()
  642. self.opt_manager.options["proxy"] = self.proxy_textctrl.GetValue()
  643. self.opt_manager.options["user_agent"] = self.useragent_textctrl.GetValue()
  644. self.opt_manager.options["referer"] = self.referer_textctrl.GetValue()
  645. self.opt_manager.options["enable_log"] = self.enable_log_checkbox.GetValue()
  646. class ExtraTab(TabPanel):
  647. def __init__(self, *args, **kwargs):
  648. super(ExtraTab, self).__init__(*args, **kwargs)
  649. self.cmdline_args_label = self.crt_statictext(_("Youtube-dl command line options (e.g. --help)"))
  650. self.cmdline_args_textctrl = self.crt_textctrl(wx.TE_MULTILINE | wx.TE_LINEWRAP)
  651. self.extra_opts_label = self.crt_statictext(_("Extra options"))
  652. self.youtube_dl_debug_checkbox = self.crt_checkbox(_("Debug youtube-dl"))
  653. self.ignore_errors_checkbox = self.crt_checkbox(_("Ignore errors"))
  654. self.ignore_config_checkbox = self.crt_checkbox(_("Ignore youtube-dl config"))
  655. self.no_mtime_checkbox = self.crt_checkbox(_("No mtime"))
  656. self.native_hls_checkbox = self.crt_checkbox(_("Prefer native HLS"))
  657. self._set_layout()
  658. def _set_layout(self):
  659. main_sizer = wx.BoxSizer(wx.HORIZONTAL)
  660. vertical_sizer = wx.BoxSizer(wx.VERTICAL)
  661. vertical_sizer.Add(self.cmdline_args_label)
  662. vertical_sizer.Add(self.cmdline_args_textctrl, 1, wx.EXPAND | wx.ALL, border=5)
  663. vertical_sizer.Add(self.extra_opts_label, flag=wx.TOP, border=5)
  664. extra_opts_sizer = wx.WrapSizer()
  665. extra_opts_sizer.Add(self.youtube_dl_debug_checkbox)
  666. extra_opts_sizer.AddSpacer((5, -1))
  667. extra_opts_sizer.Add(self.ignore_errors_checkbox)
  668. extra_opts_sizer.AddSpacer((5, -1))
  669. extra_opts_sizer.Add(self.ignore_config_checkbox)
  670. extra_opts_sizer.AddSpacer((5, -1))
  671. extra_opts_sizer.Add(self.no_mtime_checkbox)
  672. extra_opts_sizer.AddSpacer((5, -1))
  673. extra_opts_sizer.Add(self.native_hls_checkbox)
  674. vertical_sizer.Add(extra_opts_sizer, flag=wx.ALL, border=5)
  675. main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)
  676. self.SetSizer(main_sizer)
  677. def load_options(self):
  678. self.cmdline_args_textctrl.SetValue(self.opt_manager.options["cmd_args"])
  679. self.ignore_errors_checkbox.SetValue(self.opt_manager.options["ignore_errors"])
  680. self.youtube_dl_debug_checkbox.SetValue(self.opt_manager.options["youtube_dl_debug"])
  681. self.ignore_config_checkbox.SetValue(self.opt_manager.options["ignore_config"])
  682. self.native_hls_checkbox.SetValue(self.opt_manager.options["native_hls"])
  683. self.no_mtime_checkbox.SetValue(self.opt_manager.options["nomtime"])
  684. def save_options(self):
  685. self.opt_manager.options["cmd_args"] = self.cmdline_args_textctrl.GetValue()
  686. self.opt_manager.options["ignore_errors"] = self.ignore_errors_checkbox.GetValue()
  687. self.opt_manager.options["youtube_dl_debug"] = self.youtube_dl_debug_checkbox.GetValue()
  688. self.opt_manager.options["ignore_config"] = self.ignore_config_checkbox.GetValue()
  689. self.opt_manager.options["native_hls"] = self.native_hls_checkbox.GetValue()
  690. self.opt_manager.options["nomtime"] = self.no_mtime_checkbox.GetValue()
  691. class LogGUI(wx.Frame):
  692. """Simple window for reading the STDERR.
  693. Attributes:
  694. TITLE (string): Frame title.
  695. FRAME_SIZE (tuple): Tuple that holds the frame size (width, height).
  696. Args:
  697. parent (wx.Window): Frame parent.
  698. """
  699. # REFACTOR move it on widgets module
  700. TITLE = _("Log Viewer")
  701. FRAME_SIZE = (750, 200)
  702. def __init__(self, parent=None):
  703. wx.Frame.__init__(self, parent, title=self.TITLE, size=self.FRAME_SIZE)
  704. panel = wx.Panel(self)
  705. self._text_area = wx.TextCtrl(
  706. panel,
  707. style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL
  708. )
  709. sizer = wx.BoxSizer()
  710. sizer.Add(self._text_area, 1, wx.EXPAND)
  711. panel.SetSizerAndFit(sizer)
  712. def load(self, filename):
  713. """Load file content on the text area. """
  714. if os_path_exists(filename):
  715. self._text_area.LoadFile(filename)