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.

890 lines
34 KiB

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