def session_save(self): restore_session = string.str2bool(settings.get_config().get(config.NOSECTION, "restore-session")) if restore_session: #preset = None prog_type = None #channel = None combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #preset = model[tree_iter][PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] #channel = model[tree_iter][PresetComboModelColumn.CHANNEL] categories = None if string.str2bool(settings.get_config().get(config.NOSECTION, "enable-category-filter")): categories = "" combo = self.tool_bar_box.category_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() categories = model[tree_iter][KEY_INDEX] if categories.startswith(","): # Remove prepended "all" filter categories = categories[1:] channels = None if string.str2bool(settings.get_config().get(config.NOSECTION, "enable-channel-filter")): channels = "" combo = self.tool_bar_box.channel_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() channels = model[tree_iter][KEY_INDEX] since = -1 if string.str2bool(settings.get_config().get(config.NOSECTION, "enable-since-filter")): since = 0 combo = self.tool_bar_box.since_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() since = model[tree_iter][KEY_INDEX] search_all = self.tool_bar_box.search_all_check_button.get_active() # Save values # If not an empty string (and not None) if prog_type is not None: settings.get_config().set("session", "programme-type", prog_type) if categories is not None: settings.get_config().set("session", "categories", categories) if channels is not None: settings.get_config().set("session", "channels", channels) if since >= 0: settings.get_config().set("session", "since", str(since)) settings.get_config().set("session", "search-all", str(search_all)) settings.save_config()
def on_button_properties_clicked(self, button): # button can be None preset = None combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #search_all = self.tool_bar_box.search_all_presets_check_button.get_active() #if search_all_presets: if string.str2bool(settings.get_config().get( config.NOSECTION, "disable-presets")): preset = None else: preset = model[tree_iter][self.PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] proxy_disabled = string.str2bool(settings.get_config().get( config.NOSECTION, "disable-proxy")) future = self.tool_bar_box.future_check_button.get_active() model, tree_iter = self.main_tree_view.get_selection().get_selected() if tree_iter is not None: #index = model[tree_iter][SearchResultColumn.INDEX] #if index: pid = model[tree_iter][SearchResultColumn.PID] if pid: self.main_window.display_busy_mouse_cursor(True) get_iplayer_output_lines = get_iplayer.info( pid, None, preset=preset, prog_type=prog_type, proxy_disabled=proxy_disabled, future=future) self.main_window.display_busy_mouse_cursor(False) self.on_progress_bar_update(None) window = props_window.PropertiesWindow( self, get_iplayer_output_lines, icon=self.main_window.get_icon()) window.show_all() else: dialog = Gtk.MessageDialog( self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "No episode highlighted. A series is highlighted") #dialog.format_secondary_text("") dialog.run() dialog.destroy() else: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "No episode highlighted") #dialog.format_secondary_text("") dialog.run() dialog.destroy()
def on_button_properties_clicked(self, button): # button can be None preset = None combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #search_all = self.tool_bar_box.search_all_presets_check_button.get_active() #if search_all_presets: if string.str2bool(settings.get_config().get(config.NOSECTION, "disable-presets")): preset = None else: preset = model[tree_iter][self.PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] proxy_disabled = string.str2bool(settings.get_config().get(config.NOSECTION, "disable-proxy")) future = self.tool_bar_box.future_check_button.get_active() model, tree_iter = self.main_tree_view.get_selection().get_selected() if tree_iter is not None: #index = model[tree_iter][SearchResultColumn.INDEX] #if index: pid = model[tree_iter][SearchResultColumn.PID] if pid: self.main_window.display_busy_mouse_cursor(True) get_iplayer_output_lines = get_iplayer.info( pid, None, preset=preset, prog_type=prog_type, proxy_disabled=proxy_disabled, future=future) self.main_window.display_busy_mouse_cursor(False) self.on_progress_bar_update(None) window = props_window.PropertiesWindow(self, get_iplayer_output_lines, icon=self.main_window.get_icon()) window.show_all() else: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "No episode highlighted. A series is highlighted") #dialog.format_secondary_text("") dialog.run() dialog.destroy() else: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "No episode highlighted") #dialog.format_secondary_text("") dialog.run() dialog.destroy()
def precheck(quiet=False): log_output = "" # Check required program(s) # Call get_iplayer directly, instead of wrapping it in "shell code", to check whether it is installed or not process_output = command.run(_GET_IPLAYER_PROG + " --usage", quiet=True, temp_pathname=settings.TEMP_PATHNAME) #TODO not Linux specific ("not found") if quiet and "not found" in process_output: # command.run() already logs the error (logger.warning()) log_output += "WARNING:{0}".format(process_output) # Check get_iplayer preset files if not string.str2bool(settings.get_config().get(config.NOSECTION, "disable-presets")): pathname = os.path.join(os.path.expanduser("~"), ".get_iplayer", "presets") for preset in [Preset.RADIO, Preset.TV]: filename = os.path.join(pathname, preset) if not os.path.exists(filename): msg = "preset file {0} does not exist".format(filename) logger.warning(msg) log_output += "WARNING:{0}".format(msg) return log_output
ALL_CATEGORIES_LABEL = "Categories" ALL_CHANNELS_LABEL = "Channels" SINCE_FOREVER_LABEL = "Since" # Indices of a key-value pair KEY_INDEX = 0 VALUE_INDEX = 1 #### _GET_IPLAYER_PROG = "get_iplayer" #_SINCE_HOUR_MARGIN = 6 _SINCE_HOUR_MARGIN = string.str2int(settings.get_config().get(config.NOSECTION, "since-margin-hours", fallback=6)) _COMPACT_TOOLBAR = string.str2bool(settings.get_config().get(config.NOSECTION, "compact-tool-bar")) _ALL_CATEGORIES_LABEL = ALL_CATEGORIES_LABEL if _COMPACT_TOOLBAR else "" _ALL_CHANNELS_LABEL = ALL_CHANNELS_LABEL if _COMPACT_TOOLBAR else "" _SINCE_FOREVER_LABEL = SINCE_FOREVER_LABEL if _COMPACT_TOOLBAR else "" #_SINCE_FUTURE_LABEL = "Future" #### # List of key-value pairs #SINCE_LIST = [[0, _SINCE_FOREVER_LABEL], [1, _SINCE_FUTURE_LABEL], SINCE_LIST = [[0, _SINCE_FOREVER_LABEL], # [ 4, "4 hours"], [8, "8 hours"], [12, "12 hours"], [16, "16 hours"], [20, "20 hours"], # [ 24 + _SINCE_HOUR_MARGIN, "1 day" ], [ 48 + _SINCE_HOUR_MARGIN, "2 days"], [ 2, "2 hours"], [ 4, "4 hours"], [6, "6 hours"], [8, "8 hours"], [10, "10 hours"], [ 12, "12 hours"], [14, "14 hours"], [16, "16 hours"], [18, "18 hours"], [20, "20 hours"], [ 24 + _SINCE_HOUR_MARGIN, "1 day" ], [ 48 + _SINCE_HOUR_MARGIN, "2 days"],
def _init_grid(self, controller, prop_table): ##min_content_height=600, min_content_width=600 ##visible=True, can_focus=True, hscrollbar_policy=Gtk.Policy.AUTOMATIC, # vscrollbar_policy=Gtk.Policy.AUTOMATIC scrolled_window = Gtk.ScrolledWindow() ##scrolled_window.min_content_height(700) ##scrolled_window.min_content_width(800) #scrolled_window.set_property("min-content-height", 5800) #scrolled_window.set_property("min-content-width", 400) ##self.set_default_size(400, 400) #scrolled_window.set_hexpand(True) #scrolled_window.set_vexpand(True) self.add(scrolled_window) self.grid = Gtk.Grid(orientation=Gtk.Orientation.VERTICAL, margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) ##self.grid.set_row_homogeneous(False) ##self.grid.set_column_homogeneous(False) scrolled_window.add_with_viewport(self.grid) ## # Find property values episode = None longname = None name = None image_url = None pid = None title = None for prop_label, prop_value in prop_table: if prop_label == "episode": episode = prop_value if prop_label == "longname": longname = prop_value if prop_label == "name": name = prop_value #OLD if prop_label == "thumbnail" or prop_label == "thumbnail4": if prop_label == "thumbnail": image_url = prop_value if prop_label == "pid": pid = prop_value if prop_label == "title": title = prop_value locate_search_term = name #### Thumbnail, title, play button ##ALTERNATIVE #for i in range(len(prop_table)): # if prop_table[i][InfoResultColumn.PROP_LABEL /* 0 */] == "thumbnail4": # break #if i < len(prop_table): # image_url = prop_table[i][InfoResultColumn.PROP_VALUE /* 1 */] if image_url is not None: #WORKAROUND for getting a larger image (used to be "thumbnail4") #image_url = re.sub("/[0-9]+x[0-9]+/", "/640x360/", image_url) if string.str2bool(settings.get_config().get(config.NOSECTION, "show-images")): timeout = string.str2float(settings.get_config().get(config.NOSECTION, "load-image-timeout-seconds")) image = Image.image(image_url, relpath="large", timeout=timeout, max_width=get_iplayer_downloader.ui.main_window.IMAGE_MAX_WIDTH, max_height=get_iplayer_downloader.ui.main_window.IMAGE_MAX_HEIGHT) if image is not None: self.grid.add(image) if title is not None: props_title = title else: # Programme type is "podcast" or episode info is not available if longname is not None and episode is not None: props_title = longname + " : " + episode else: props_title = episode if props_title is not None: TITLE_MAX_LENGTH = int(get_iplayer_downloader.ui.main_window.WINDOW_LARGE_WIDTH / 8) + 3 # 8: gestimated character width if len(props_title) > TITLE_MAX_LENGTH: # Label1 should not exceed the grid width props_title = props_title[:TITLE_MAX_LENGTH] + "..." label1 = Gtk.Label(props_title, halign=Gtk.Align.CENTER) #label1.set_selectable(False) self.grid.add(label1) # The play button URL is the same as the "player" property URL #NOTE Do not expand/fill the button in the grid: halign=Gtk.Align.CENTER button = Gtk.Button(relief=Gtk.ReliefStyle.NONE, image_position=Gtk.PositionType.TOP, halign=Gtk.Align.CENTER) button.set_image(Gtk.Image(icon_name=Gtk.STOCK_MEDIA_PLAY)) #button.set_label("Play") #if title is not None: # button.set_label(title) button.set_tooltip_text(get_iplayer_downloader.ui.main_window.TOOLTIP_VIEW_PLAYER) button.connect("clicked", controller.on_button_play_clicked_by_pid, pid) self.grid.add(button) #### Property table #NOTE To expand the main grid (self.grid), expand one of its child widgets: hexpand=True frame = Gtk.Frame(label="Properties", label_xalign=0.01, hexpand=True, margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) self.grid.add(frame) ## PROP_LABEL_LIST = ["available", "categories", "channel", "desclong", "dir", "duration", "episode", "expiry", "expiryrel", "firstbcast", "firstbcastrel", "index", "lastbcast", "lastbcastrel", "longname", "modes", "modesizes", "pid", "player", "senum", "timeadded", "title", "type", "versions", "web"] prop_grid = Gtk.Grid(column_homogeneous=False, row_homogeneous=False, margin_top=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, margin_bottom=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) frame.add(prop_grid) #focused_label = None #for prop_row in prop_table: #for i, prop_row in enumerate(prop_table): for i, (prop_label, prop_value) in enumerate(prop_table): if prop_label in PROP_LABEL_LIST: if prop_label == "duration": try: # Convert into hours and minutes #NOTE // is the integer division operator #DUPLICATE duration_mins = int(prop_value) // 60 prop_value = "{0:2}".format(duration_mins // 60) + ":" + \ "{0:02}".format(duration_mins % 60) prop_value = prop_value.strip() except ValueError: #NOTE prop_value still has its original value pass label1 = Gtk.Label(prop_label, valign=Gtk.Align.START, halign=Gtk.Align.START) label1.set_padding(get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, 0) label1.set_line_wrap(True) #label1.set_selectable(False) prop_grid.attach(label1, 0, i, 1, 1) label2 = Gtk.Label(markup.text2html(prop_value), margin_start=40, valign=Gtk.Align.START, halign=Gtk.Align.START, use_markup=True) label2.set_padding(get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, 0) label2.set_line_wrap(True) label2.set_selectable(True) label2.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) # Avoid centering of text, when line wrap warps at word boundaries (WORD, WORD_CHAR) label2.set_alignment(0, 0) prop_grid.attach(label2, 1, i, 1, 1) #if prop_label == "index" or prop_label == "title": # focused_label = label2 #if focused_label: # focused_label.grab_focus() # # Avoid highlighted text # focused_label.select_region(0, 0) ## ##ALTERNATIVE array indexing and populating Gtk.Grid #self.table = Gtk.Table() #self.add(self.table) #for y in range((len(prop_list)): # for x in range(len(prop_list[y])): # label = Gtk.Label() # ... # self.table.attach(label, x, x+1, y, y+1) ## ##ALTERNATIVE however, Gtk.Grid has better geometry management #prop_table = Gtk.Table(len(prop_list), len(prop_list[0]), False) #frame.add(prop_table) # #i = 0 #for prop_row in prop_list: # label = Gtk.Label(prop_row[InfoResultColumn.PROP_LABEL /* 0 */], valign=Gtk.Align.START, halign=Gtk.Align.START) # label.set_padding(4, 0) # label.set_line_wrap(True) # prop_table.attach(label, 0, 1, i, i+1) # # #, use_markup=True # label = Gtk.Label(prop_row[InfoResultColumn.PROP_VALUE /* 1 */], valign=Gtk.Align.START, halign=Gtk.Align.START) # label.set_padding(4, 0) # label.set_line_wrap(True) # prop_table.attach(label, 1, 2, i, i+1) # # i += 1 #### Additional Links frame = Gtk.Frame(label="Additional links", label_xalign=0.01, margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) self.grid.add(frame) #TODO if prog_type not in [get_iplayer.Channels.CH4, get_iplayer.Channels.ITV]: url = "<a href=\"http://www.bbc.co.uk/iplayer\" title=\"BBC iPlayer\">BBC iPlayer</a>" url += " " # Add URLs to get_iplayer's pvr configuration folder and filenames filepath = os.path.join(os.path.expanduser("~"), ".get_iplayer", "pvr") url += file.files2urls(filepath) url += " " # Add URLs to get_iplayer's presets configuration folder and filenames filepath = os.path.join(os.path.expanduser("~"), ".get_iplayer", "presets") url += file.files2urls(filepath) label1 = Gtk.Label(url, valign=Gtk.Align.START, halign=Gtk.Align.START, use_markup=True, margin_top=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, margin_bottom=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) label1.set_padding(get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, 0) label1.set_line_wrap(True) #WORD_CHAR label1.set_line_wrap_mode(Pango.WrapMode.CHAR) #label1.set_selectable(False) frame.add(label1) #### Buttons box = Gtk.Box(spacing=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) self.grid.add(box) button = Gtk.Button("Close", margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) button.set_image(Gtk.Image(icon_name=Gtk.STOCK_CLOSE)) button.connect("clicked", lambda user_data: self.destroy()) box.pack_end(button, False, False, 0) button.grab_focus() if os.name == "posix": button = Gtk.Button("Similar", margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) button.set_image(Gtk.Image(icon_name=Gtk.STOCK_FIND)) button.set_tooltip_text(get_iplayer_downloader.ui.main_window.TOOLTIP_SEARCH_LOCATE_SIMILAR) button.connect("clicked", controller.on_button_similar_clicked, locate_search_term) box.pack_end(button, False, False, 0)
def session_restore(self): restore_session = string.str2bool(settings.get_config().get( config.NOSECTION, "restore-session")) if restore_session: prog_type = settings.get_config().get("session", "programme-type") categories = settings.get_config().get("session", "categories") channels = settings.get_config().get("session", "channels") #NOTE Variables created in the try clause or except clause remain allocated after the try-except statement try: since = int(settings.get_config().get("session", "since")) except ValueError: since = 0 search_all = string.str2bool(settings.get_config().get( "session", "search-all")) # If empty string or None (in case of an error) or ch4/itv has been disabled, then set the default value if not prog_type or \ (prog_type == "ch4" and not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-ch4"))) or \ (prog_type == "itv" and not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-itv"))): prog_type = get_iplayer.ProgType.RADIO if not categories: categories = "" if not channels: channels = "" # Don't restore when filter widget is disabled if not string.str2bool(settings.get_config().get( config.NOSECTION, "enable-category-filter")): categories = None if not string.str2bool(settings.get_config().get( config.NOSECTION, "enable-channel-filter")): channels = None if not string.str2bool(settings.get_config().get( config.NOSECTION, "enable-since-filter")): since = -1 # Restore values #ALTERNATIVE way of looping (see categories and since looping below) combo = self.tool_bar_box.preset_combo model = combo.get_model() if model is not None: tree_iter = model.get_iter_first() i = 0 while tree_iter is not None: value = model.get_value(tree_iter, 1) if value == prog_type: combo.set_active(i) #NOTE combo.set_active() already causes the invocation of on_combo_preset_changed() #self.on_combo_preset_changed(combo) break tree_iter = model.iter_next(tree_iter) i += 1 self.on_combo_preset_changed(combo) if categories: combo = self.tool_bar_box.category_combo model = combo.get_model() if model is not None: # Default #combo.set_active(0) tree_iter = model.get_iter_first() while tree_iter is not None: model = combo.get_model() # Find the categories key if model[tree_iter][KEY_INDEX] == categories: combo.set_active_iter(tree_iter) break tree_iter = model.iter_next(tree_iter) if channels: combo = self.tool_bar_box.channel_combo model = combo.get_model() if model is not None: # Default #combo.set_active(0) tree_iter = model.get_iter_first() while tree_iter is not None: model = combo.get_model() # Find the channels key if model[tree_iter][KEY_INDEX] == channels: combo.set_active_iter(tree_iter) break tree_iter = model.iter_next(tree_iter) if since >= 0: combo = self.tool_bar_box.since_combo model = combo.get_model() if model is not None: # Default #combo.set_active(SinceListIndex.FOREVER) tree_iter = model.get_iter_first() while tree_iter is not None: model = combo.get_model() if model[tree_iter][KEY_INDEX] == int(since): combo.set_active_iter(tree_iter) break tree_iter = model.iter_next(tree_iter) self.tool_bar_box.search_all_check_button.set_active(search_all)
def session_save(self): restore_session = string.str2bool(settings.get_config().get( config.NOSECTION, "restore-session")) if restore_session: #preset = None prog_type = None #channel = None combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #preset = model[tree_iter][PresetComboModelColumn.PRESET] prog_type = model[tree_iter][ self.PresetComboModelColumn.PROG_TYPE] #channel = model[tree_iter][PresetComboModelColumn.CHANNEL] categories = None if string.str2bool(settings.get_config().get( config.NOSECTION, "enable-category-filter")): categories = "" combo = self.tool_bar_box.category_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() categories = model[tree_iter][KEY_INDEX] if categories.startswith(","): # Remove prepended "all" filter categories = categories[1:] channels = None if string.str2bool(settings.get_config().get( config.NOSECTION, "enable-channel-filter")): channels = "" combo = self.tool_bar_box.channel_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() channels = model[tree_iter][KEY_INDEX] since = -1 if string.str2bool(settings.get_config().get( config.NOSECTION, "enable-since-filter")): since = 0 combo = self.tool_bar_box.since_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() since = model[tree_iter][KEY_INDEX] search_all = self.tool_bar_box.search_all_check_button.get_active() # Save values # If not an empty string (and not None) if prog_type is not None: settings.get_config().set("session", "programme-type", prog_type) if categories is not None: settings.get_config().set("session", "categories", categories) if channels is not None: settings.get_config().set("session", "channels", channels) if since >= 0: settings.get_config().set("session", "since", str(since)) settings.get_config().set("session", "search-all", str(search_all)) settings.save_config()
def on_button_find_clicked(self, button): # button can be None search_text = self.tool_bar_box.search_entry.get_text() search_all = self.tool_bar_box.search_all_check_button.get_active() disable_presets = string.str2bool(settings.get_config().get( config.NOSECTION, "disable-presets")) preset = None prog_type = None combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() if disable_presets: preset = None else: preset = model[tree_iter][self.PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] categories = None exclude_categories = None combo = self.tool_bar_box.category_combo if not search_all or combo.get_active() > 0: # A specific set of categories has been selected tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #WORKAROUND see also get_iplayer.py (at least in Python 2.7) # On some systems, when model[tree_iter][KEY_INDEX] == None, the following exception is raised: # AttributeError: 'NoneType' object has no attribute 'decode' # In the debugger, model[tree_iter][KEY_INDEX] is displayed as a unicode string. categories = model[tree_iter][KEY_INDEX] (categories, exclude_categories ) = _separate_excluded_categories(categories) channels = None exclude_channels = None combo = self.tool_bar_box.channel_combo if not search_all or combo.get_active() > 0: # A specific set of channels has been selected tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() channels = model[tree_iter][KEY_INDEX] #ALTERNATIVE #channels = model.get_value(tree_iter, KEY_INDEX) (channels, exclude_channels) = _separate_excluded_channels(channels) since = 0 combo = self.tool_bar_box.since_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() since = model[tree_iter][KEY_INDEX] future = self.tool_bar_box.future_check_button.get_active() self.main_window.display_busy_mouse_cursor(True) if self._check_first_time_find(prog_type): get_iplayer.refresh(preset=preset, prog_type=prog_type, channels=channels, exclude_channels=exclude_channels, future=future) output_lines = get_iplayer.search( search_text, preset=preset, prog_type=prog_type, channels=channels, exclude_channels=exclude_channels, categories=categories, exclude_categories=exclude_categories, since=since, future=future) self.main_window.display_busy_mouse_cursor(False) self.on_progress_bar_update(None) self.main_tree_view.set_store(output_lines) # Scroll to top adjustment = self.main_window.main_tree_view_scrollbars.get_vadjustment( ) adjustment.set_value(0.0) adjustment.value_changed() #adjustment = self.main_window.main_tree_view_scrollbars.set_vadjustment(adjustment) #if disable_presets: # prog_type = None self.main_window.set_window_title(prog_type=prog_type) # Invalidate downloaded PID list self.downloaded_pid_list = [] # Disable filters when there are cached search results available self._set_filters_sensitive(prog_type)
def session_restore(self): restore_session = string.str2bool(settings.get_config().get(config.NOSECTION, "restore-session")) if restore_session: prog_type = settings.get_config().get("session", "programme-type") categories = settings.get_config().get("session", "categories") channels = settings.get_config().get("session", "channels") #NOTE Variables created in the try clause or except clause remain allocated after the try-except statement try: since = int(settings.get_config().get("session", "since")) except ValueError: since = 0 search_all = string.str2bool(settings.get_config().get("session", "search-all")) # If empty string or None (in case of an error) or ch4/itv has been disabled, then set the default value if not prog_type or \ (prog_type == "ch4" and not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-ch4"))) or \ (prog_type == "itv" and not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-itv"))): prog_type = get_iplayer.ProgType.RADIO if not categories: categories = "" if not channels: channels = "" # Don't restore when filter widget is disabled if not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-category-filter")): categories = None if not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-channel-filter")): channels = None if not string.str2bool(settings.get_config().get(config.NOSECTION, "enable-since-filter")): since = -1 # Restore values #ALTERNATIVE way of looping (see categories and since looping below) combo = self.tool_bar_box.preset_combo model = combo.get_model() if model is not None: tree_iter = model.get_iter_first() i = 0 while tree_iter is not None: value = model.get_value(tree_iter, 1) if value == prog_type: combo.set_active(i) #NOTE combo.set_active() already causes the invocation of on_combo_preset_changed() #self.on_combo_preset_changed(combo) break tree_iter = model.iter_next(tree_iter) i += 1 self.on_combo_preset_changed(combo) if categories: combo = self.tool_bar_box.category_combo model = combo.get_model() if model is not None: # Default #combo.set_active(0) tree_iter = model.get_iter_first() while tree_iter is not None: model = combo.get_model() # Find the categories key if model[tree_iter][KEY_INDEX] == categories: combo.set_active_iter(tree_iter) break tree_iter = model.iter_next(tree_iter) if channels: combo = self.tool_bar_box.channel_combo model = combo.get_model() if model is not None: # Default #combo.set_active(0) tree_iter = model.get_iter_first() while tree_iter is not None: model = combo.get_model() # Find the channels key if model[tree_iter][KEY_INDEX] == channels: combo.set_active_iter(tree_iter) break tree_iter = model.iter_next(tree_iter) if since >= 0: combo = self.tool_bar_box.since_combo model = combo.get_model() if model is not None: # Default #combo.set_active(SinceListIndex.FOREVER) tree_iter = model.get_iter_first() while tree_iter is not None: model = combo.get_model() if model[tree_iter][KEY_INDEX] == int(since): combo.set_active_iter(tree_iter) break tree_iter = model.iter_next(tree_iter) self.tool_bar_box.search_all_check_button.set_active(search_all)
def on_button_download_clicked(self, button, pvr_queue=False): # button can be None preset = None prog_type = None alt_recording_mode = False combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #NOTE Downloading in "All" preset mode will not even work when all settings (radiomode, tvmode, # outputradio, outputtv, etc.) are in one file (in the options file or a single preset file). # Get_iplayer.get() cannot (easily) determine the prog_type of each episode and # get_iplayer does not determine the programme type by it self #search_all = self.tool_bar_box.search_all_presets_check_button.get_active() #if search_all_presets: if string.str2bool(settings.get_config().get(config.NOSECTION, "disable-presets")): preset = None else: preset = model[tree_iter][self.PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] #channel = model[tree_iter][self.PresetComboModelColumn.CHANNEL] alt_recording_mode = self.tool_bar_box.alt_recording_mode_check_button.get_active() dry_run = self.tool_bar_box.dry_run_check_button.get_active() force = self.tool_bar_box.force_check_button.get_active() future = self.tool_bar_box.future_check_button.get_active() #PVR_CHECK_BUTTON #pvr_queue_checkbox_state = self.tool_bar_box.pvr_queue_check_button.get_active() #if button is not None and not pvr_queue: # # If event was raised from the tool bar download button and not from a keyboard shortcut, # # then the PVR check button determines the download/queue mode # pvr_queue = pvr_queue_checkbox_state # Search selected leaf nodes (the second level) two levels deep model = self.main_tree_view.get_model() #indices = "" pid_list = [] root_iter = model.get_iter_first() while root_iter is not None: row = model[root_iter] if row[SearchResultColumn.DOWNLOAD] and row[SearchResultColumn.PID]: #indices += row[SearchResultColumn.INDEX] + " " pid_list.append(row[SearchResultColumn.PID]) #if model.iter_has_child(root_iter): child_iter = model.iter_children(root_iter) while child_iter is not None: row = model[child_iter] if row[SearchResultColumn.DOWNLOAD]: #indices += row[SearchResultColumn.INDEX] + " " pid_list.append(row[SearchResultColumn.PID]) child_iter = model.iter_next(child_iter) root_iter = model.iter_next(root_iter) #if not indices: if len(pid_list) == 0: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "No episodes selected") #dialog.format_secondary_text("") dialog.run() dialog.destroy() #return True return #### ## Avoid downloading an episode twice in parallel, otherwise continue downloading ## twice and let get_iplayer generate an "Already in history" INFO log message. ## The user can download episodes in parallel without having ## to clear the previous download selection and therefore avoiding ## download errors because of two threads trying to download the same episode # #try: # if os.name == "posix": # #NOTE 2>/dev/null: to surpress error messages, e.g.: # # Signal 18 (CONT) caught by ps (procps-ng version 3.3.9). # # ps:display.c:66: please report this bug # gipd_processes = int(command.run("echo -n $(ps xo cmd 2>/dev/null | grep 'get_iplayer_downloader' | grep 'python' | grep -v 'grep' | wc -l) ; exit 0", quiet=True)) # else: # gipd_processes = 1 #except ValueError: # # Sometimes gipd_processes is not a valid int (empty string?) # gipd_processes = 1 # ## If there are more than one get_iplayer_downloader processes running, ## then don't perform the 'running in parallel' check (self.processes is ## the number of >all< the get_iplayer processes on the system). ## (TODO Limit self.processes to the get_iplayer processes which belong ## to the current get_iplayer_downloader process). ## TODO detect 'programme type' change ##PVR_CHECK_BUTTON ##if gipd_processes == 1 and not dry_run and not pvr_queue: #if gipd_processes == 1 and not dry_run: # # Update self.processes now, to avoid any progress bar update delay # self._update_processes_count() # if self.processes > 0: # #if not force: # # Remove already downloaded PIDs from the PID set (copy of pid_list) # pid_set = set(pid_list) # downloaded_pid_set = set(self.downloaded_pid_list) # pid_list = list(pid_set.difference(downloaded_pid_set)) # if len(pid_list) == 0: # dialog = Gtk.MessageDialog(self.main_window, 0, # Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, # "Already downloading all the selected episodes") # #dialog.format_secondary_text("") # dialog.run() # dialog.destroy() # #return True # return #### self.main_window.display_busy_mouse_cursor(True) launched, process_output = get_iplayer.get(pid_list, pid=True, pvr_queue=pvr_queue, preset=preset, prog_type=prog_type, alt_recording_mode=alt_recording_mode, dry_run=dry_run, force=force, future=future) self.main_window.display_busy_mouse_cursor(False) self.on_progress_bar_update(None) if not launched: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "get_iplayer not launched") #dialog.format_secondary_text("") dialog.run() dialog.destroy() #PVR_CHECK_BUTTON #elif pvr_queue_checkbox_state or pvr_queue or future: # # If implicitly or explicitly queuing, always show the Queued Episodes dialog window, # # even if nothing will be queued elif dry_run or pvr_queue or future: if dry_run: if process_output is not None: process_output = process_output.replace("; ", "\n") + "\n" message_format = "Command list" else: message_format = "Queued Episodes" # When queuing episodes, always show the Queued Episodes dialog window, even if nothing will be queued dialog = ExtendedMessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, message_format) #dialog.format_secondary_text("") dialog.set_default_response(Gtk.ResponseType.CLOSE) dialog.get_content_area().set_size_request(get_iplayer_downloader.ui.main_window.WINDOW_LARGE_WIDTH, get_iplayer_downloader.ui.main_window.WINDOW_LARGE_HEIGHT) dialog.format_tertiary_scrolled_text("" if process_output is None else process_output) label = dialog.get_scrolled_label() label.set_valign(Gtk.Align.START) label.set_halign(Gtk.Align.START) label.set_selectable(True) #label.override_font(Pango.FontDescription("monospace small")) label.override_font(Pango.FontDescription("monospace 10")) dialog.run() dialog.destroy() else: #self.main_window.main_tree_view.grab_focus() self.downloaded_pid_list = pid_list
def get(search_term_list, pid=True, pvr_queue=False, preset=None, prog_type=None, alt_recording_mode=False, dry_run=False, force=False, output_path=None, categories=None, future=False): """ Run get_iplayer --get, get_iplayer --pid or get_iplayer --pvrqueue. If @pid is true, then @search_term_list contains pids. Return tuple: launched boolean, process output string. """ if preset == Preset.RADIO: output_path = RADIO_DOWNLOAD_PATH elif preset == Preset.TV: output_path = TV_DOWNLOAD_PATH else: output_path = None #WORKAROUND Preset can be None: disable-presets is true AND data models and configuration are based on presets, not on programme types #if preset and string.str2bool(settings.get_config().get(preset, "run-in-terminal")): # terminal_prog = settings.get_config().get(config.NOSECTION, "terminal-emulator") #else: # terminal_prog = None preset_fallback = None if preset: preset_fallback = preset else: # Determine preset from programme type if prog_type == ProgType.RADIO: preset_fallback = Preset.RADIO elif prog_type == ProgType.TV: preset_fallback = Preset.RADIO if prog_type and string.str2bool(settings.get_config().get( preset_fallback, "run-in-terminal")): terminal_prog = settings.get_config().get(config.NOSECTION, "terminal-emulator") else: terminal_prog = None if alt_recording_mode: if prog_type == ProgType.CH4: alt_radio_modes = "" alt_tv_modes = "flashnormal" elif prog_type == ProgType.ITV: alt_radio_modes = "" alt_tv_modes = "itvnormal,itvhigh,itvlow" else: alt_radio_modes = settings.get_config().get( Preset.RADIO, "recording-modes") alt_tv_modes = settings.get_config().get(Preset.TV, "recording-modes") #cmd = "( for i in" #for search_term_row in search_term_list: # cmd += " " + search_term_row[SearchTermColumn.PID_OR_INDEX] #cmd += "; do " + _GET_IPLAYER_PROG cmd = "" for i, search_term in enumerate(search_term_list): cmd += _GET_IPLAYER_PROG + " --hash" if preset: cmd += " --preset=" + preset #WORKAROUND Preset can be None: disable-presets is true AND models and configuration are based on presets, not on programme types # if alt_recording_mode: # if preset == Preset.RADIO and alt_radio_modes: # #cmd += " --modes=\"" + alt_radio_modes + "\"" # cmd += " --radiomode=\"" + alt_radio_modes + "\"" # elif preset == Preset.TV and alt_tv_modes: # #cmd += " --modes=\"" + alt_tv_modes + "\"" # cmd += " --tvmode=\"" + alt_tv_modes + "\"" if alt_recording_mode: if preset_fallback == Preset.RADIO and alt_radio_modes: #cmd += " --modes=\"" + alt_radio_modes + "\"" cmd += " --radiomode=\"" + alt_radio_modes + "\"" elif preset_fallback == Preset.TV and alt_tv_modes: #cmd += " --modes=\"" + alt_tv_modes + "\"" cmd += " --tvmode=\"" + alt_tv_modes + "\"" if prog_type: cmd += " --type=" + prog_type cmd += " --nocopyright" if force: cmd += " --force --overwrite" if output_path: cmd += " --output=\"" + output_path + "\"" #if pvr_queue or future: if pvr_queue: if not preset: return False # Must explicitly specify programme type and PID on the command line when in pvr queue mode cmd += " --pvrqueue --pid=" #cmd += " --pvr-exclude=" + ",".join(exclude_search_term_list) elif pid: cmd += " --pid=" else: cmd += " --get " ##cmd += "\"$i\" ; done" #cmd += "$i; done )" if search_term: #TEMP if search_term is a PID and the PID is numeric, # then add a leading non-digit character to the PID # so that get_iplayer will not assume the search_term to be an index if " " not in search_term and prog_type in [ Channels.CH4, Channels.ITV ]: search_term = " " + search_term # search_term_list could be a set of episode indices, so don't surround them with quotes cmd += search_term if (i < len(search_term_list) - 1): #cmd += "; " cmd += "; echo '----'; " if pvr_queue or dry_run: launched = True process_output = command.run(cmd, dry_run=dry_run, temp_pathname=settings.TEMP_PATHNAME) else: #CommandQueue.CommandQueue().run(...) launched = command_queue.run(cmd, temp_pathname=settings.TEMP_PATHNAME, terminal_prog=terminal_prog, terminal_title="get_iplayer get") process_output = None return (launched, process_output)
ALL_CHANNELS_LABEL = "Channels" SINCE_FOREVER_LABEL = "Since" # Indices of a key-value pair KEY_INDEX = 0 VALUE_INDEX = 1 #### _GET_IPLAYER_PROG = "get_iplayer" #_SINCE_HOUR_MARGIN = 6 _SINCE_HOUR_MARGIN = string.str2int(settings.get_config().get( config.NOSECTION, "since-margin-hours", fallback=6)) _COMPACT_TOOLBAR = string.str2bool(settings.get_config().get( config.NOSECTION, "compact-tool-bar")) _ALL_CATEGORIES_LABEL = ALL_CATEGORIES_LABEL if _COMPACT_TOOLBAR else "" _ALL_CHANNELS_LABEL = ALL_CHANNELS_LABEL if _COMPACT_TOOLBAR else "" _SINCE_FOREVER_LABEL = SINCE_FOREVER_LABEL if _COMPACT_TOOLBAR else "" #_SINCE_FUTURE_LABEL = "Future" #### # List of key-value pairs #SINCE_LIST = [[0, _SINCE_FOREVER_LABEL], [1, _SINCE_FUTURE_LABEL], SINCE_LIST = [ [0, _SINCE_FOREVER_LABEL], # [ 4, "4 hours"], [8, "8 hours"], [12, "12 hours"], [16, "16 hours"], [20, "20 hours"], # [ 24 + _SINCE_HOUR_MARGIN, "1 day" ], [ 48 + _SINCE_HOUR_MARGIN, "2 days"], [2, "2 hours"], [4, "4 hours"],
def _init_grid(self, controller, prop_table): ##min_content_height=600, min_content_width=600 ##visible=True, can_focus=True, hscrollbar_policy=Gtk.Policy.AUTOMATIC, # vscrollbar_policy=Gtk.Policy.AUTOMATIC scrolled_window = Gtk.ScrolledWindow() ##scrolled_window.min_content_height(700) ##scrolled_window.min_content_width(800) #scrolled_window.set_property("min-content-height", 5800) #scrolled_window.set_property("min-content-width", 400) ##self.set_default_size(400, 400) #scrolled_window.set_hexpand(True) #scrolled_window.set_vexpand(True) self.add(scrolled_window) self.grid = Gtk.Grid( orientation=Gtk.Orientation.VERTICAL, margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) ##self.grid.set_row_homogeneous(False) ##self.grid.set_column_homogeneous(False) scrolled_window.add_with_viewport(self.grid) ## # Find property values episode = None longname = None name = None image_url = None pid = None title = None for prop_label, prop_value in prop_table: if prop_label == "episode": episode = prop_value if prop_label == "longname": longname = prop_value if prop_label == "name": name = prop_value #OLD if prop_label == "thumbnail" or prop_label == "thumbnail4": if prop_label == "thumbnail": image_url = prop_value if prop_label == "pid": pid = prop_value if prop_label == "title": title = prop_value locate_search_term = name #### Thumbnail, title, play button ##ALTERNATIVE #for i in range(len(prop_table)): # if prop_table[i][InfoResultColumn.PROP_LABEL /* 0 */] == "thumbnail4": # break #if i < len(prop_table): # image_url = prop_table[i][InfoResultColumn.PROP_VALUE /* 1 */] if image_url is not None: #WORKAROUND for getting a larger image (used to be "thumbnail4") #image_url = re.sub("/[0-9]+x[0-9]+/", "/640x360/", image_url) if string.str2bool(settings.get_config().get( config.NOSECTION, "show-images")): timeout = string.str2float(settings.get_config().get( config.NOSECTION, "load-image-timeout-seconds")) image = Image.image(image_url, relpath="large", timeout=timeout, max_width=get_iplayer_downloader.ui. main_window.IMAGE_MAX_WIDTH, max_height=get_iplayer_downloader.ui. main_window.IMAGE_MAX_HEIGHT) if image is not None: self.grid.add(image) if title is not None: props_title = title else: # Episode info is not available if longname is not None and episode is not None: props_title = longname + " : " + episode else: props_title = episode if props_title is not None: TITLE_MAX_LENGTH = int( get_iplayer_downloader.ui.main_window.WINDOW_LARGE_WIDTH / 8) + 3 # 8: gestimated character width if len(props_title) > TITLE_MAX_LENGTH: # Label1 should not exceed the grid width props_title = props_title[:TITLE_MAX_LENGTH] + "..." label1 = Gtk.Label(props_title, halign=Gtk.Align.CENTER) #label1.set_selectable(False) self.grid.add(label1) #NOTE Do not expand/fill the button in the grid: halign=Gtk.Align.CENTER button = Gtk.Button(relief=Gtk.ReliefStyle.NONE, image_position=Gtk.PositionType.TOP, halign=Gtk.Align.CENTER) button.set_image(Gtk.Image(icon_name=Gtk.STOCK_MEDIA_PLAY)) #button.set_label("Play") #if title is not None: # button.set_label(title) button.set_tooltip_text( get_iplayer_downloader.ui.main_window.TOOLTIP_VIEW_PLAYER) button.connect("clicked", controller.on_button_play_clicked_by_pid, pid) self.grid.add(button) #### Property table #NOTE To expand the main grid (self.grid), expand one of its child widgets: hexpand=True frame = Gtk.Frame( label="Properties", label_xalign=0.01, hexpand=True, margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) self.grid.add(frame) ## PROP_LABEL_LIST = [ "available", "categories", "channel", "desclong", "dir", "duration", "episode", "expiry", "expiryrel", "firstbcast", "firstbcastrel", "index", "lastbcast", "lastbcastrel", "longname", "modes", "modesizes", "pid", "player", "senum", "timeadded", "title", "type", "versions", "web" ] prop_grid = Gtk.Grid(column_homogeneous=False, row_homogeneous=False, margin_top=get_iplayer_downloader.ui.main_window. WIDGET_BORDER_WIDTH, margin_bottom=get_iplayer_downloader.ui. main_window.WIDGET_BORDER_WIDTH) frame.add(prop_grid) #focused_label = None #for prop_row in prop_table: #for i, prop_row in enumerate(prop_table): for i, (prop_label, prop_value) in enumerate(prop_table): if prop_label in PROP_LABEL_LIST: if prop_label == "duration": try: # Convert into hours and minutes #NOTE // is the integer division operator #DUPLICATE duration_mins = int(prop_value) // 60 prop_value = "{0:2}".format(duration_mins // 60) + ":" + \ "{0:02}".format(duration_mins % 60) prop_value = prop_value.strip() except ValueError: #NOTE prop_value still has its original value pass label1 = Gtk.Label(prop_label, valign=Gtk.Align.START, halign=Gtk.Align.START) label1.set_padding( get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, 0) label1.set_line_wrap(True) #label1.set_selectable(False) prop_grid.attach(label1, 0, i, 1, 1) label2 = Gtk.Label(markup.text2html(prop_value), margin_start=40, valign=Gtk.Align.START, halign=Gtk.Align.START, use_markup=True) label2.set_padding( get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, 0) label2.set_line_wrap(True) label2.set_selectable(True) label2.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) # Avoid centering of text, when line wrap warps at word boundaries (WORD, WORD_CHAR) label2.set_alignment(0, 0) prop_grid.attach(label2, 1, i, 1, 1) #if prop_label == "index" or prop_label == "title": # focused_label = label2 #if focused_label: # focused_label.grab_focus() # # Avoid highlighted text # focused_label.select_region(0, 0) ## ##ALTERNATIVE array indexing and populating Gtk.Grid #self.table = Gtk.Table() #self.add(self.table) #for y in range((len(prop_list)): # for x in range(len(prop_list[y])): # label = Gtk.Label() # ... # self.table.attach(label, x, x+1, y, y+1) ## ##ALTERNATIVE however, Gtk.Grid has better geometry management #prop_table = Gtk.Table(len(prop_list), len(prop_list[0]), False) #frame.add(prop_table) # #i = 0 #for prop_row in prop_list: # label = Gtk.Label(prop_row[InfoResultColumn.PROP_LABEL /* 0 */], valign=Gtk.Align.START, halign=Gtk.Align.START) # label.set_padding(4, 0) # label.set_line_wrap(True) # prop_table.attach(label, 0, 1, i, i+1) # # #, use_markup=True # label = Gtk.Label(prop_row[InfoResultColumn.PROP_VALUE /* 1 */], valign=Gtk.Align.START, halign=Gtk.Align.START) # label.set_padding(4, 0) # label.set_line_wrap(True) # prop_table.attach(label, 1, 2, i, i+1) # # i += 1 #### Additional Links frame = Gtk.Frame( label="Additional links", label_xalign=0.01, margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) self.grid.add(frame) #TODO if prog_type not in [get_iplayer.Channels.CH4, get_iplayer.Channels.ITV]: url = "<a href=\"https://www.bbc.co.uk/iplayer\" title=\"BBC iPlayer\">BBC iPlayer</a>" url += " " # Add URLs to get_iplayer's pvr configuration folder and filenames filepath = os.path.join(os.path.expanduser("~"), ".get_iplayer", "pvr") url += file.files2urls(filepath) url += " " # Add URLs to get_iplayer's presets configuration folder and filenames filepath = os.path.join(os.path.expanduser("~"), ".get_iplayer", "presets") url += file.files2urls(filepath) label1 = Gtk.Label(url, valign=Gtk.Align.START, halign=Gtk.Align.START, use_markup=True, margin_top=get_iplayer_downloader.ui.main_window. WIDGET_BORDER_WIDTH, margin_bottom=get_iplayer_downloader.ui.main_window. WIDGET_BORDER_WIDTH) label1.set_padding( get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH, 0) label1.set_line_wrap(True) #WORD_CHAR label1.set_line_wrap_mode(Pango.WrapMode.CHAR) #label1.set_selectable(False) frame.add(label1) #### Buttons box = Gtk.Box( spacing=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) self.grid.add(box) button = Gtk.Button( "Close", margin=get_iplayer_downloader.ui.main_window.WIDGET_BORDER_WIDTH) button.set_image(Gtk.Image(icon_name=Gtk.STOCK_CLOSE)) button.connect("clicked", lambda user_data: self.destroy()) box.pack_end(button, False, False, 0) button.grab_focus() if os.name == "posix": button = Gtk.Button("Similar", margin=get_iplayer_downloader.ui.main_window. WIDGET_BORDER_WIDTH) button.set_image(Gtk.Image(icon_name=Gtk.STOCK_FIND)) button.set_tooltip_text(get_iplayer_downloader.ui.main_window. TOOLTIP_SEARCH_LOCATE_SIMILAR) button.connect("clicked", controller.on_button_similar_clicked, locate_search_term) box.pack_end(button, False, False, 0)
def _display_settings(self): """ Retrieve in-memory settings and put them in dialog fields. """ self.general_clear_cache_on_exit_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "clear-cache-on-exit"))) self.general_compact_toolbar_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "compact-tool-bar"))) self.general_compact_treeview_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "compact-tree-view"))) self.general_disable_presets_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "disable-presets"))) self.general_disable_proxy_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "disable-proxy"))) self.general_refresh_cache_on_startup_check_button.set_active( string.str2bool(settings.get_config().get( config.NOSECTION, "refresh-cache-on-startup"))) self.general_show_buttonmenu_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "show-button-menu"))) self.general_show_menubar_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "show-menu-bar"))) self.general_show_tooltip_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "show-tooltip"))) self.general_start_maximized_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "start-maximized"))) value = settings.get_config().get(config.NOSECTION, "terminal-emulator") if not value: # Get default value (as an example value) if stored value is empty settings.revert_config_option(config.NOSECTION, "terminal-emulator") self.general_terminal_emulator_entry.set_text( settings.get_config().get(config.NOSECTION, "terminal-emulator")) # self.radio_channels_entry.set_text(settings.get_config().get( "radio", "channels")) download_path = settings.get_config().get("radio", "download-path") self.radio_download_path_entry.set_text(download_path) if download_path: if not os.path.exists(download_path): os.makedirs(download_path) self.radio_download_file_chooser_button.set_filename(download_path) else: # Set to root path self.radio_download_file_chooser_button.set_filename(os.sep) self.radio_preset_file_entry.set_text(settings.get_config().get( "radio", "preset-file")) self.radio_recording_modes_entry.set_text(settings.get_config().get( "radio", "recording-modes")) self.radio_run_in_terminal_check_button.set_active( string.str2bool(settings.get_config().get("radio", "run-in-terminal"))) # self.tv_channels_entry.set_text(settings.get_config().get( "tv", "channels")) download_path = settings.get_config().get("tv", "download-path") self.tv_download_path_entry.set_text(download_path) if download_path: if not os.path.exists(download_path): os.makedirs(download_path) self.tv_download_file_chooser_button.set_filename(download_path) else: # Set to root path self.tv_download_file_chooser_button.set_filename(os.sep) self.tv_preset_file_entry.set_text(settings.get_config().get( "tv", "preset-file")) self.tv_recording_modes_entry.set_text(settings.get_config().get( "tv", "recording-modes")) self.tv_run_in_terminal_check_button.set_active( string.str2bool(settings.get_config().get("tv", "run-in-terminal")))
def get(search_term_list, pid=True, pvr_queue=False, preset=None, prog_type=None, alt_recording_mode=False, dry_run=False, force=False, output_path=None, categories=None, future=False): """ Run get_iplayer --get, get_iplayer --pid or get_iplayer --pvrqueue. If @pid is true, then @search_term_list contains pids. Return tuple: launched boolean, process output string. """ if preset == Preset.RADIO: output_path = RADIO_DOWNLOAD_PATH elif preset == Preset.TV: output_path = TV_DOWNLOAD_PATH else: output_path = None #WORKAROUND Preset can be None: disable-presets is true AND data models and configuration are based on presets, not on programme types #if preset and string.str2bool(settings.get_config().get(preset, "run-in-terminal")): # terminal_prog = settings.get_config().get(config.NOSECTION, "terminal-emulator") #else: # terminal_prog = None preset_fallback = None if preset: preset_fallback = preset else: # Determine preset from programme type if prog_type == ProgType.RADIO or prog_type == ProgType.PODCAST: preset_fallback = Preset.RADIO elif prog_type == ProgType.TV: preset_fallback = Preset.RADIO if prog_type and string.str2bool(settings.get_config().get(preset_fallback, "run-in-terminal")): terminal_prog = settings.get_config().get(config.NOSECTION, "terminal-emulator") else: terminal_prog = None if alt_recording_mode: if prog_type == ProgType.CH4: alt_radio_modes = "" alt_tv_modes = "flashnormal" elif prog_type == ProgType.ITV: alt_radio_modes = "" alt_tv_modes = "itvnormal,itvhigh,itvlow" else: alt_radio_modes = settings.get_config().get(Preset.RADIO, "recording-modes") alt_tv_modes = settings.get_config().get(Preset.TV, "recording-modes") #cmd = "( for i in" #for search_term_row in search_term_list: # cmd += " " + search_term_row[SearchTermColumn.PID_OR_INDEX] #cmd += "; do " + _GET_IPLAYER_PROG cmd = "" for i, search_term in enumerate(search_term_list): cmd += _GET_IPLAYER_PROG + " --hash" if preset: cmd += " --preset=" + preset #WORKAROUND Preset can be None: disable-presets is true AND models and configuration are based on presets, not on programme types # if alt_recording_mode: # if preset == Preset.RADIO and alt_radio_modes: # #cmd += " --modes=\"" + alt_radio_modes + "\"" # cmd += " --radiomode=\"" + alt_radio_modes + "\"" # elif preset == Preset.TV and alt_tv_modes: # #cmd += " --modes=\"" + alt_tv_modes + "\"" # cmd += " --tvmode=\"" + alt_tv_modes + "\"" if alt_recording_mode: if preset_fallback == Preset.RADIO and alt_radio_modes: #cmd += " --modes=\"" + alt_radio_modes + "\"" cmd += " --radiomode=\"" + alt_radio_modes + "\"" elif preset_fallback == Preset.TV and alt_tv_modes: #cmd += " --modes=\"" + alt_tv_modes + "\"" cmd += " --tvmode=\"" + alt_tv_modes + "\"" if prog_type: cmd += " --type=" + prog_type cmd += " --nocopyright" if force: cmd += " --force --overwrite" if output_path: cmd += " --output=\"" + output_path + "\"" #if pvr_queue or future: if pvr_queue: if not preset: return False # Must explicitly specify programme type and PID on the command line when in pvr queue mode cmd += " --pvrqueue --pid=" #cmd += " --pvr-exclude=" + ",".join(exclude_search_term_list) elif pid: cmd += " --pid=" else: cmd += " --get " ##cmd += "\"$i\" ; done" #cmd += "$i; done )" if search_term: #TEMP if search_term is a PID and the PID is numeric, # then add a leading non-digit character to the PID # so that get_iplayer will not assume the search_term to be an index if " " not in search_term and prog_type in [Channels.CH4, Channels.ITV]: search_term = " " + search_term # search_term_list could be a set of episode indices, so don't surround them with quotes cmd += search_term if (i < len(search_term_list) - 1): #cmd += "; " cmd += "; echo '----'; " if pvr_queue or dry_run: launched = True process_output = command.run(cmd, dry_run=dry_run, temp_pathname=settings.TEMP_PATHNAME) else: #CommandQueue.CommandQueue().run(...) launched = command_queue.run(cmd, temp_pathname=settings.TEMP_PATHNAME, terminal_prog=terminal_prog, terminal_title="get_iplayer get") process_output = None return (launched, process_output)
def on_button_download_clicked(self, button, pvr_queue=False): # button can be None preset = None prog_type = None alt_recording_mode = False combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #NOTE Downloading in "All" preset mode will not even work when all settings (radiomode, tvmode, # outputradio, outputtv, etc.) are in one file (in the options file or a single preset file). # Get_iplayer.get() cannot (easily) determine the prog_type of each episode and # get_iplayer does not determine the programme type by it self #search_all = self.tool_bar_box.search_all_presets_check_button.get_active() #if search_all_presets: if string.str2bool(settings.get_config().get( config.NOSECTION, "disable-presets")): preset = None else: preset = model[tree_iter][self.PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] #channel = model[tree_iter][self.PresetComboModelColumn.CHANNEL] alt_recording_mode = self.tool_bar_box.alt_recording_mode_check_button.get_active( ) dry_run = self.tool_bar_box.dry_run_check_button.get_active() force = self.tool_bar_box.force_check_button.get_active() future = self.tool_bar_box.future_check_button.get_active() #PVR_CHECK_BUTTON #pvr_queue_checkbox_state = self.tool_bar_box.pvr_queue_check_button.get_active() #if button is not None and not pvr_queue: # # If event was raised from the tool bar download button and not from a keyboard shortcut, # # then the PVR check button determines the download/queue mode # pvr_queue = pvr_queue_checkbox_state # Search selected leaf nodes (the second level) two levels deep model = self.main_tree_view.get_model() #indices = "" pid_list = [] root_iter = model.get_iter_first() while root_iter is not None: row = model[root_iter] if row[SearchResultColumn.DOWNLOAD] and row[ SearchResultColumn.PID]: #indices += row[SearchResultColumn.INDEX] + " " pid_list.append(row[SearchResultColumn.PID]) #if model.iter_has_child(root_iter): child_iter = model.iter_children(root_iter) while child_iter is not None: row = model[child_iter] if row[SearchResultColumn.DOWNLOAD]: #indices += row[SearchResultColumn.INDEX] + " " pid_list.append(row[SearchResultColumn.PID]) child_iter = model.iter_next(child_iter) root_iter = model.iter_next(root_iter) #if not indices: if len(pid_list) == 0: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "No episodes selected") #dialog.format_secondary_text("") dialog.run() dialog.destroy() #return True return #### ## Avoid downloading an episode twice in parallel, otherwise continue downloading ## twice and let get_iplayer generate an "Already in history" INFO log message. ## The user can download episodes in parallel without having ## to clear the previous download selection and therefore avoiding ## download errors because of two threads trying to download the same episode # #try: # if os.name == "posix": # #NOTE 2>/dev/null: to surpress error messages, e.g.: # # Signal 18 (CONT) caught by ps (procps-ng version 3.3.9). # # ps:display.c:66: please report this bug # gipd_processes = int(command.run("echo -n $(ps xo cmd 2>/dev/null | grep 'get_iplayer_downloader' | grep 'python' | grep -v 'grep' | wc -l) ; exit 0", quiet=True)) # else: # gipd_processes = 1 #except ValueError: # # Sometimes gipd_processes is not a valid int (empty string?) # gipd_processes = 1 # ## If there are more than one get_iplayer_downloader processes running, ## then don't perform the 'running in parallel' check (self.processes is ## the number of >all< the get_iplayer processes on the system). ## (TODO Limit self.processes to the get_iplayer processes which belong ## to the current get_iplayer_downloader process). ## TODO detect 'programme type' change ##PVR_CHECK_BUTTON ##if gipd_processes == 1 and not dry_run and not pvr_queue: #if gipd_processes == 1 and not dry_run: # # Update self.processes now, to avoid any progress bar update delay # self._update_processes_count() # if self.processes > 0: # #if not force: # # Remove already downloaded PIDs from the PID set (copy of pid_list) # pid_set = set(pid_list) # downloaded_pid_set = set(self.downloaded_pid_list) # pid_list = list(pid_set.difference(downloaded_pid_set)) # if len(pid_list) == 0: # dialog = Gtk.MessageDialog(self.main_window, 0, # Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, # "Already downloading all the selected episodes") # #dialog.format_secondary_text("") # dialog.run() # dialog.destroy() # #return True # return #### self.main_window.display_busy_mouse_cursor(True) launched, process_output = get_iplayer.get( pid_list, pid=True, pvr_queue=pvr_queue, preset=preset, prog_type=prog_type, alt_recording_mode=alt_recording_mode, dry_run=dry_run, force=force, future=future) self.main_window.display_busy_mouse_cursor(False) self.on_progress_bar_update(None) if not launched: dialog = Gtk.MessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, "get_iplayer not launched") #dialog.format_secondary_text("") dialog.run() dialog.destroy() #PVR_CHECK_BUTTON #elif pvr_queue_checkbox_state or pvr_queue or future: # # If implicitly or explicitly queuing, always show the Queued Episodes dialog window, # # even if nothing will be queued elif dry_run or pvr_queue or future: if dry_run: if process_output is not None: process_output = process_output.replace("; ", "\n") + "\n" message_format = "Command list" else: message_format = "Queued Episodes" # When queuing episodes, always show the Queued Episodes dialog window, even if nothing will be queued dialog = ExtendedMessageDialog(self.main_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, message_format) #dialog.format_secondary_text("") dialog.set_default_response(Gtk.ResponseType.CLOSE) dialog.get_content_area().set_size_request( get_iplayer_downloader.ui.main_window.WINDOW_LARGE_WIDTH, get_iplayer_downloader.ui.main_window.WINDOW_LARGE_HEIGHT) dialog.format_tertiary_scrolled_text( "" if process_output is None else process_output) label = dialog.get_scrolled_label() label.set_valign(Gtk.Align.START) label.set_halign(Gtk.Align.START) label.set_selectable(True) #label.override_font(Pango.FontDescription("monospace small")) label.override_font(Pango.FontDescription("monospace 10")) dialog.run() dialog.destroy() else: #self.main_window.main_tree_view.grab_focus() self.downloaded_pid_list = pid_list
def _display_settings(self): """ Retrieve in-memory settings and put them in dialog fields. """ self.general_clear_cache_on_exit_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "clear-cache-on-exit")) ) self.general_compact_toolbar_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "compact-tool-bar")) ) self.general_compact_treeview_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "compact-tree-view")) ) self.general_disable_presets_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "disable-presets")) ) self.general_disable_proxy_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "disable-proxy")) ) self.general_refresh_cache_on_startup_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "refresh-cache-on-startup")) ) self.general_show_buttonmenu_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "show-button-menu")) ) self.general_show_menubar_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "show-menu-bar")) ) self.general_show_tooltip_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "show-tooltip")) ) self.general_start_maximized_check_button.set_active( string.str2bool(settings.get_config().get(config.NOSECTION, "start-maximized")) ) value = settings.get_config().get(config.NOSECTION, "terminal-emulator") if not value: # Get default value (as an example value) if stored value is empty settings.revert_config_option(config.NOSECTION, "terminal-emulator") self.general_terminal_emulator_entry.set_text(settings.get_config().get(config.NOSECTION, "terminal-emulator")) # self.radio_channels_entry.set_text(settings.get_config().get("radio", "channels")) download_path = settings.get_config().get("radio", "download-path") self.radio_download_path_entry.set_text(download_path) if download_path: if not os.path.exists(download_path): os.makedirs(download_path) self.radio_download_file_chooser_button.set_filename(download_path) else: # Set to root path self.radio_download_file_chooser_button.set_filename(os.sep) self.radio_preset_file_entry.set_text(settings.get_config().get("radio", "preset-file")) self.radio_recording_modes_entry.set_text(settings.get_config().get("radio", "recording-modes")) self.radio_run_in_terminal_check_button.set_active( string.str2bool(settings.get_config().get("radio", "run-in-terminal")) ) # self.tv_channels_entry.set_text(settings.get_config().get("tv", "channels")) download_path = settings.get_config().get("tv", "download-path") self.tv_download_path_entry.set_text(download_path) if download_path: if not os.path.exists(download_path): os.makedirs(download_path) self.tv_download_file_chooser_button.set_filename(download_path) else: # Set to root path self.tv_download_file_chooser_button.set_filename(os.sep) self.tv_preset_file_entry.set_text(settings.get_config().get("tv", "preset-file")) self.tv_recording_modes_entry.set_text(settings.get_config().get("tv", "recording-modes")) self.tv_run_in_terminal_check_button.set_active( string.str2bool(settings.get_config().get("tv", "run-in-terminal")) )
def on_button_find_clicked(self, button): # button can be None search_text = self.tool_bar_box.search_entry.get_text() search_all = self.tool_bar_box.search_all_check_button.get_active() disable_presets = string.str2bool(settings.get_config().get(config.NOSECTION, "disable-presets")) preset = None prog_type = None combo = self.tool_bar_box.preset_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() if disable_presets: preset = None else: preset = model[tree_iter][self.PresetComboModelColumn.PRESET] prog_type = model[tree_iter][self.PresetComboModelColumn.PROG_TYPE] categories = None exclude_categories = None combo = self.tool_bar_box.category_combo if not search_all or combo.get_active() > 0: # A specific set of categories has been selected tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() #WORKAROUND see also get_iplayer.py (at least in Python 2.7) # On some systems, when model[tree_iter][KEY_INDEX] == None, the following exception is raised: # AttributeError: 'NoneType' object has no attribute 'decode' # In the debugger, model[tree_iter][KEY_INDEX] is displayed as a unicode string. categories = model[tree_iter][KEY_INDEX] (categories, exclude_categories) = _separate_excluded_categories(categories) channels = None exclude_channels = None combo = self.tool_bar_box.channel_combo if not search_all or combo.get_active() > 0: # A specific set of channels has been selected tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() channels = model[tree_iter][KEY_INDEX] #ALTERNATIVE #channels = model.get_value(tree_iter, KEY_INDEX) (channels, exclude_channels) = _separate_excluded_channels(channels) since = 0 combo = self.tool_bar_box.since_combo tree_iter = combo.get_active_iter() if tree_iter is not None: model = combo.get_model() since = model[tree_iter][KEY_INDEX] future = self.tool_bar_box.future_check_button.get_active() self.main_window.display_busy_mouse_cursor(True) if self._check_first_time_find(prog_type): get_iplayer.refresh(preset=preset, prog_type=prog_type, channels=channels, exclude_channels=exclude_channels, future=future) output_lines = get_iplayer.search(search_text, preset=preset, prog_type=prog_type, channels=channels, exclude_channels=exclude_channels, categories=categories, exclude_categories=exclude_categories, since=since, future=future) self.main_window.display_busy_mouse_cursor(False) self.on_progress_bar_update(None) self.main_tree_view.set_store(output_lines) # Scroll to top adjustment = self.main_window.main_tree_view_scrollbars.get_vadjustment() adjustment.set_value(0.0) adjustment.value_changed() #adjustment = self.main_window.main_tree_view_scrollbars.set_vadjustment(adjustment) #if disable_presets: # prog_type = None self.main_window.set_window_title(prog_type=prog_type) # Invalidate downloaded PID list self.downloaded_pid_list = [] # Disable filters when there are cached search results available self._set_filters_sensitive(prog_type)