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.

901 lines
35 KiB

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