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.

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