diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 37d6d86..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-# This file copied from
-#   https://python-packaging.readthedocs.io/en/latest/minimal.html?highlight=gitignore
-
-# Compiled python modules.
-*.pyc
-
-# Setuptools distribution folder.
-/dist/
-
-# Python egg metadata, regenerated from source files by setuptools.
-/*.egg-info
diff --git a/docs/mswin_install.rst b/docs/mswin_install.rst
index 813543e..286d121 100644
--- a/docs/mswin_install.rst
+++ b/docs/mswin_install.rst
@@ -1,32 +1,38 @@
 Tartube installation problems on MS Windows
 ===========================================
 
-Some users on MS Windows report that they can't run Tartube at all. 
+Some users on MS Windows report that they can't run Tartube at all. There are three possible fixes.
 
-This page describes what you can do, if you are one of them.
+Fix #1
+~~~~~~
 
-The problem
-~~~~~~~~~~~
+Wait for v1.2 of Tartube, which should fix the problem.
 
-A full installation of Tartube and all of its dependencies uses over 2GB of your hard drive. The download is over 600MB.
+Fix #2
+~~~~~~
 
-This is obviously too much, so I've removed everything that is not necessary. As a result, the installer is a 90MB download.
+Find the **tartube_mswin.sh** file, and modify it.
 
-The installer works for most people, but some users are reporting that they can't run Tartube at all. 
+If you used the MS Windows installer, it should be installed in 
 
-Obviously, something is missing from the installer. I can't reproduce the problem on any computer, so I don't know what is missing.
+**C:\\Users\\YOURNAME\\AppData\\Local\\Tartube\\msys64\\home\\user\\tartube**
 
-The solution
-~~~~~~~~~~~~
+(You may have to `make hidden folders visible <https://support.microsoft.com/en-us/help/14201/windows-show-hidden-files>`__ to see the parent folder.)
 
-A workaround is to perform a manual installation. This takes about 10-30 minutes, depending on your internet speed.
+Open the file in a text editor, and replace this line
 
-If the manual installation works, you can try to diagnose the original problem. 
+**cd tartube**
 
-As soon as someone discovers what is missing from the installer, I can add it, and everyone will be happy again.
+with this line
 
-MS Windows manual installation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+**cd /home/user/tartube/tartube**
+
+Save the file. You may now be able to run Tartube from the Windows Start Menu, or from the desktop shortcut.
+
+Fix #3
+~~~~~~
+
+Perform a manual installation. This takes about 10-30 minutes, depending on your internet speed.
 
 - This section assumes you have a 64-bit computer
 - Download and install MSYS2 from `msys2.org <https://msys2.org>`__. You need the file that looks something like **msys2-x86_64-yyyymmdd.exe**
@@ -61,26 +67,3 @@ MS Windows manual installation
         
         **python3 tartube**
 
-Diagnosing the problem
-----------------------
-
-If the manual installation works, and if you have the time and the patience, you can work out what the missing dependency is. When you find it, please `tell me <https://github.com/axcore/tartube/issues>`__.
-
-- Download the normal installer from `Sourceforge <https://tartube.sourceforge.io/>`__
-- Run the installer. Tell it to install Tartube in **C:\\tartube**
- 
-.. image:: screenshots/diagnose1.png
-  :alt: Set the installation folder
-  
-- You now have two installations - a working one in **C:\\msys64\\**, and a broken one in **C:\\tartube\\msys64\\**
-
-.. image:: screenshots/diagnose2.png
-  :alt: Picture of both folders
-  
-- Now, you can start moving files and folders from the working folder into the broken folder, **one at a time**
-- For example, you could move the file **C:\\msys64\\usr\\bin\\awk** to **C:\\tartube\\msys64\\usr\\bin\\awk**
-- For example, you could move the folder **C:\\msys64\\usr\\etc** to **C:\\tartube\\msys64\\usr\\bin\\etc**
-- Every time you copy something, try to run Tartube (from the Start menu, or from the desktop shortcut)
-- When Tartube runs for the first time, `tell me which file/folder you moved <https://github.com/axcore/tartube/issues>`__. I don't need to know everything - just tell me the **last** thing you moved.
-
-
diff --git a/docs/mswin_install_old.rst b/docs/mswin_install_old.rst
new file mode 100644
index 0000000..813543e
--- /dev/null
+++ b/docs/mswin_install_old.rst
@@ -0,0 +1,86 @@
+Tartube installation problems on MS Windows
+===========================================
+
+Some users on MS Windows report that they can't run Tartube at all. 
+
+This page describes what you can do, if you are one of them.
+
+The problem
+~~~~~~~~~~~
+
+A full installation of Tartube and all of its dependencies uses over 2GB of your hard drive. The download is over 600MB.
+
+This is obviously too much, so I've removed everything that is not necessary. As a result, the installer is a 90MB download.
+
+The installer works for most people, but some users are reporting that they can't run Tartube at all. 
+
+Obviously, something is missing from the installer. I can't reproduce the problem on any computer, so I don't know what is missing.
+
+The solution
+~~~~~~~~~~~~
+
+A workaround is to perform a manual installation. This takes about 10-30 minutes, depending on your internet speed.
+
+If the manual installation works, you can try to diagnose the original problem. 
+
+As soon as someone discovers what is missing from the installer, I can add it, and everyone will be happy again.
+
+MS Windows manual installation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- This section assumes you have a 64-bit computer
+- Download and install MSYS2 from `msys2.org <https://msys2.org>`__. You need the file that looks something like **msys2-x86_64-yyyymmdd.exe**
+- MSYS2 wants to install in **C:\\msys64**, so do that
+- Open the MINGW64 terminal, which is **C:\\msys64\\mingw64.exe**
+- In the MINGW64 terminal, type:
+
+        **pacman -Syu**
+        
+- If the terminal wants to shut down, close it, and then restart it
+- Now type the following commands, one by one:
+
+        **pacman -Su**
+        
+        **pacman -S mingw-w64-x86_64-python3**
+        
+        **pacman -S mingw-w64-x86_64-python3-pip**
+        
+        **pacman -S mingw-w64-x86_64-python3-gobject**
+        
+        **pacman -S mingw-w64-x86_64-python3-requests**
+        
+        **pacman -S mingw-w64-x86_64-gtk3**
+        
+        **pacman -S mingw-w64-x86_64-gsettings-desktop-schemas**        
+        
+- Download the `Tartube source code <https://sourceforge.net/projects/tartube/files/v0.7.0/tartube_v0.7.0.tar.gz/download>`__ from Sourceforge
+- Extract it into the folder **C:\\msys64\\home\\YOURNAME**, creating a folder called **C:\\msys64\\home\\YOURNAME\\tartube**
+- Now, to run Tartube, type these commands in the MINGW64 terminal:
+
+        **cd tartube**
+        
+        **python3 tartube**
+
+Diagnosing the problem
+----------------------
+
+If the manual installation works, and if you have the time and the patience, you can work out what the missing dependency is. When you find it, please `tell me <https://github.com/axcore/tartube/issues>`__.
+
+- Download the normal installer from `Sourceforge <https://tartube.sourceforge.io/>`__
+- Run the installer. Tell it to install Tartube in **C:\\tartube**
+ 
+.. image:: screenshots/diagnose1.png
+  :alt: Set the installation folder
+  
+- You now have two installations - a working one in **C:\\msys64\\**, and a broken one in **C:\\tartube\\msys64\\**
+
+.. image:: screenshots/diagnose2.png
+  :alt: Picture of both folders
+  
+- Now, you can start moving files and folders from the working folder into the broken folder, **one at a time**
+- For example, you could move the file **C:\\msys64\\usr\\bin\\awk** to **C:\\tartube\\msys64\\usr\\bin\\awk**
+- For example, you could move the folder **C:\\msys64\\usr\\etc** to **C:\\tartube\\msys64\\usr\\bin\\etc**
+- Every time you copy something, try to run Tartube (from the Start menu, or from the desktop shortcut)
+- When Tartube runs for the first time, `tell me which file/folder you moved <https://github.com/axcore/tartube/issues>`__. I don't need to know everything - just tell me the **last** thing you moved.
+
+
diff --git a/icons/small/archived.png b/icons/small/archived.png
new file mode 100644
index 0000000..fd69e8f
Binary files /dev/null and b/icons/small/archived.png differ
diff --git a/icons/small/folder_black.png b/icons/small/folder_black.png
new file mode 100644
index 0000000..cb1d414
Binary files /dev/null and b/icons/small/folder_black.png differ
diff --git a/icons/small/folder_blue.png b/icons/small/folder_blue.png
new file mode 100644
index 0000000..696561a
Binary files /dev/null and b/icons/small/folder_blue.png differ
diff --git a/icons/small/folder_green.png b/icons/small/folder_green.png
new file mode 100644
index 0000000..68c3221
Binary files /dev/null and b/icons/small/folder_green.png differ
diff --git a/icons/small/folder_red.png b/icons/small/folder_red.png
new file mode 100644
index 0000000..83cef08
Binary files /dev/null and b/icons/small/folder_red.png differ
diff --git a/icons/small/ok.png b/icons/small/ok.png
deleted file mode 100644
index c277e6b..0000000
Binary files a/icons/small/ok.png and /dev/null differ
diff --git a/setup.py b/setup.py
index 2cfa38a..51efbf6 100755
--- a/setup.py
+++ b/setup.py
@@ -34,8 +34,10 @@ import sys
 
 
 # For the Debian distribution, use an environment variable. When specified,
-#   the default executable 'tartube' is replaced by the 'tartube_debian'
-#   executiable, in which youtube-dl updates are disabled
+#   the 'tartube_debian' file is the executable, rather than the 'tartube'
+#   file
+# When the 'tartube_debian' file is the executable, youtube-dl updates are
+#   disabled, and Tartube's config file is stored at $XDG_CONFIG_HOME
 # The package maintainer should use
 #   TARTUBE_NO_UPDATES=1 python3 setup.py build
 env_var_name = 'TARTUBE_NO_UPDATES'
@@ -60,7 +62,7 @@ if env_var_value is not None:
 # Setup
 setuptools.setup(
     name='tartube',
-    version='1.1.015',
+    version='1.1.050',
     description='GUI front-end for youtube-dl',
 #    long_description=long_description,
     long_description="""Tartube is a GUI front-end for youtube-dl, partly based
diff --git a/tartube/config.py b/tartube/config.py
index 1754b4a..71b03e5 100644
--- a/tartube/config.py
+++ b/tartube/config.py
@@ -575,7 +575,7 @@ class GenericEditWin(GenericConfigWin):
 
         Returns:
 
-            The image created
+            The Gtk.Frame containing the image
 
         """
 
@@ -588,7 +588,7 @@ class GenericEditWin(GenericConfigWin):
             self.app_obj.file_manager_obj.load_to_pixbuf(image_path),
         )
 
-        return image
+        return frame
 
 
     def add_label(self, grid, text, x, y, wid, hei):
@@ -1069,6 +1069,226 @@ class GenericEditWin(GenericConfigWin):
     # (Inherited by VideoEditWin, ChannelPlaylistEditWin and FolderEditWin)
 
 
+    def add_container_properties(self, grid):
+
+        """Called by VideoEditWin.setup_tabs(),
+        ChannelPlaylistEditWin.setup_tabs() and FolderEditWin.setup_tabs().
+
+        Adds widgets common to those edit windows.
+
+        Args:
+
+            grid (Gtk.Grid): The grid on which widgets are arranged in their
+                tab
+                
+        """
+
+        entry = self.add_entry(grid,
+            None,
+            0, 1, 1, 1,
+        )
+        entry.set_text('#' + str(self.edit_obj.dbid))
+        entry.set_editable(False)
+        entry.set_hexpand(False)
+        entry.set_width_chars(8)
+
+        main_win_obj = self.app_obj.main_win_obj
+        parent_obj = self.edit_obj.parent_obj
+        if isinstance(self.edit_obj, media.Channel):
+            icon_path = main_win_obj.icon_dict['channel_small']
+        elif isinstance(self.edit_obj, media.Playlist):
+            icon_path = main_win_obj.icon_dict['playlist_small']
+        else:
+            icon_path = main_win_obj.icon_dict['folder_small']
+
+        frame = self.add_image(grid,
+            icon_path,
+            1, 1, 1, 1,
+        )
+        # (The frame looks cramped without this. The icon itself is 16x16)
+        frame.set_size_request(
+            16 + (self.spacing_size * 2),
+            -1,
+        )
+
+        entry2 = self.add_entry(grid,
+            'name',
+            2, 1, 1, 1,
+        )
+        entry2.set_editable(False)
+
+        label = self.add_label(grid,
+            'Listed as',
+            0, 2, 1, 1,
+        )
+        label.set_hexpand(False)
+
+        entry3 = self.add_entry(grid,
+            'nickname',
+            2, 2, 1, 1,
+        )
+        entry3.set_editable(False)
+
+        label2 = self.add_label(grid,
+            'Contained in',
+            0, 3, 1, 1,
+        )
+        label2.set_hexpand(False)
+        
+        if parent_obj:
+            icon_path2 = main_win_obj.icon_dict['folder_small']
+        else:
+            icon_path2 = main_win_obj.icon_dict['folder_black_small']
+
+        frame2 = self.add_image(grid,
+            icon_path2,
+            1, 3, 1, 1,
+        )
+        frame2.set_size_request(
+            16 + (self.spacing_size * 2),
+            -1,
+        )
+        
+        entry4 = self.add_entry(grid,
+            None,
+            2, 3, 1, 1,
+        )
+        entry4.set_editable(False)
+        if parent_obj:
+            entry4.set_text(parent_obj.name)
+
+
+    def add_source_properties(self, grid):
+
+        """Called by VideoEditWin.setup_tabs() and
+        ChannelPlaylistEditWin.setup_tabs().
+
+        Adds widgets common to those edit windows.
+
+        Args:
+
+            grid (Gtk.Grid): The grid on which widgets are arranged in their
+                tab
+                
+        """
+
+        label2 = self.add_label(grid,
+            utils.upper_case_first(self.media_type) + ' URL',
+            0, 4, 1, 1,
+        )
+        label2.set_hexpand(False)
+
+        entry5 = self.add_entry(grid,
+            'source',
+            1, 4, 2, 1,
+        )
+        entry5.set_editable(False)
+
+        
+    def add_destination_properties(self, grid):
+
+        """Called by ChannelPlaylistEditWin.setup_tabs() and
+        FolderEditWin.setup_tabs().
+
+        Adds widgets common to those edit windows.
+
+        Args:
+
+            grid (Gtk.Grid): The grid on which widgets are arranged in their
+                tab
+                
+        """
+
+        # To avoid messing up the neat format of the rows above, add another
+        #   grid, and put the next set of widgets inside it
+        grid2 = Gtk.Grid()
+        grid.attach(grid2, 0, 5, 3, 1)
+        grid2.set_vexpand(False)
+        grid2.set_column_spacing(self.spacing_size)
+        grid2.set_row_spacing(self.spacing_size)
+
+        label3 = self.add_label(grid2,
+            'Videos downloaded to',
+            0, 0, 1, 1,
+        )
+        label3.set_hexpand(False)
+
+        main_win_obj = self.app_obj.main_win_obj
+        dest_obj = self.app_obj.media_reg_dict[self.edit_obj.master_dbid]
+        if isinstance(dest_obj, media.Channel):
+            icon_path3 = main_win_obj.icon_dict['channel_small']
+        elif isinstance(dest_obj, media.Playlist):
+            icon_path3 = main_win_obj.icon_dict['playlist_small']
+        else:
+            icon_path3 = main_win_obj.icon_dict['folder_small']
+
+        frame3 = self.add_image(grid2,
+            icon_path3,
+            1, 0, 1, 1,
+        )
+        frame3.set_size_request(
+            16 + (self.spacing_size * 2),
+            -1,
+        )
+        
+        entry6 = self.add_entry(grid2,
+            None,
+            2, 0, 1, 1,
+        )
+        entry6.set_editable(False)
+        entry6.set_text(dest_obj.name)
+
+        label5 = self.add_label(grid2,
+            'Location on filesystem',
+            0, 1, 1, 1,
+        )
+        label5.set_hexpand(False)
+        
+        entry7 = self.add_entry(grid2,
+            None,
+            1, 1, 2, 1,
+        )
+        entry7.set_editable(False)
+        entry7.set_text(self.edit_obj.get_dir(self.app_obj))
+        
+        
+    def setup_download_options_tab(self):
+
+        """Called by self.setup_tabs().
+
+        Sets up the 'General' tab.
+        """
+
+        tab, grid = self.add_notebook_tab('_Download options')
+
+        # Download options
+        self.add_label(grid,
+            '<u>Download options</u>',
+            0, 0, 2, 1,
+        )
+
+        self.apply_options_button = Gtk.Button('Apply download options')
+        grid.attach(self.apply_options_button, 0, 1, 1, 1)
+        self.apply_options_button.connect(
+            'clicked',
+            self.on_button_apply_clicked,
+        )
+
+        self.edit_button = Gtk.Button('Edit download options')
+        grid.attach(self.edit_button, 1, 1, 1, 1)
+        self.edit_button.connect('clicked', self.on_button_edit_clicked)
+
+        self.remove_button = Gtk.Button('Remove download options')
+        grid.attach(self.remove_button, 1, 2, 1, 1)
+        self.remove_button.connect('clicked', self.on_button_remove_clicked)
+
+        if self.edit_obj.options_obj:
+            self.apply_options_button.set_sensitive(False)
+        else:
+            self.edit_button.set_sensitive(False)
+            self.remove_button.set_sensitive(False)
+
+            
     def on_button_apply_clicked(self, button):
 
         """Called from callback in self.setup_general_tab().
@@ -1090,9 +1310,9 @@ class GenericEditWin(GenericConfigWin):
         # Apply download options to the media data object
         self.app_obj.apply_download_options(self.edit_obj)
         # (De)sensitise buttons appropriately
-        self.apply_button.set_sensitive(False)
-        self.edit_button.set_sensitive(True)
-        self.remove_button.set_sensitive(True)
+        self.apply_options_button.set_sensitive(False)
+        self.edit_options_button.set_sensitive(True)
+        self.remove_options_button.set_sensitive(True)
 
 
     def on_button_edit_clicked(self, button):
@@ -1142,9 +1362,9 @@ class GenericEditWin(GenericConfigWin):
         # Remove download options from the media data object
         self.app_obj.remove_download_options(self.edit_obj)
         # (De)sensitise buttons appropriately
-        self.apply_button.set_sensitive(True)
-        self.edit_button.set_sensitive(False)
-        self.remove_button.set_sensitive(False)
+        self.apply_options_button.set_sensitive(True)
+        self.edit_options_button.set_sensitive(False)
+        self.remove_options_button.set_sensitive(False)
 
 
 class GenericPrefWin(GenericConfigWin):
@@ -1929,30 +2149,75 @@ class OptionsEditWin(GenericEditWin):
             self.file_tab_sensitise_widgets(False)
 
         # Filesystem options
-
-        # (empty label for spacing)
-        self.add_label(grid,
-            '',
-            0, 7, grid_width, 1,
-        )
-
         self.add_label(grid,
             '<u>Filesystem options</u>',
-            0, 8, grid_width, 1,
+            0, 7, grid_width, 1,
         )
 
         self.add_checkbutton(grid,
             'Restrict filenames to using ASCII characters',
             'restrict_filenames',
-            0, 9, grid_width, 1,
+            0, 8, grid_width, 1,
         )
 
         self.add_checkbutton(grid,
             'Set the file modification time from the server',
             'nomtime',
-            0, 10, grid_width, 1,
+            0, 9, grid_width, 1,
         )
 
+        # Filesystem overrides
+        self.add_label(grid,
+            '<u>Filesystem overrides</u>',
+            0, 10, grid_width, 1,
+        )        
+
+        checkbutton = self.add_checkbutton(grid,
+            'Download all videos into this folder',
+            None,
+            0, 11, 2, 1,
+        )
+        # Signal connect below
+
+        # (Currently, only two fixed folders are elligible for this mode, so
+        #   we'll just add them individually)
+        store5 = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
+        pixbuf = self.app_obj.main_win_obj.pixbuf_dict['folder_green_small']
+        store5.append( [pixbuf, self.app_obj.fixed_misc_folder.name] )
+        pixbuf = self.app_obj.main_win_obj.pixbuf_dict['folder_blue_small']
+        store5.append( [pixbuf, self.app_obj.fixed_temp_folder.name] )
+
+        combo5 = Gtk.ComboBox.new_with_model(store5)
+        grid.attach(combo5, 2, 11, (grid_width - 2), 1)
+        renderer_pixbuf5 = Gtk.CellRendererPixbuf()
+        combo5.pack_start(renderer_pixbuf5, False)
+        combo5.add_attribute(renderer_pixbuf5, 'pixbuf', 0)
+        renderer_text5 = Gtk.CellRendererText()
+        combo5.pack_start(renderer_text5, True)
+        combo5.add_attribute(renderer_text5, 'text', 1)
+        combo5.set_entry_text_column(1)
+        # Signal connect below
+
+        current_override = self.edit_obj.options_dict['use_fixed_folder']        
+        if current_override is None:
+            checkbutton.set_active(False)
+            combo5.set_sensitive(False)
+            combo5.set_active(0)
+        else:
+            checkbutton.set_active(True)
+            combo5.set_sensitive(True)
+            if current_override == self.app_obj.fixed_temp_folder.name:
+                combo5.set_active(1)
+            else:
+                # The value should be either None, 'Unsorted Videos' or
+                #   'Temporary Videos'. In case the value is anything else,
+                #   use 'Unsorted Videos'
+                combo5.set_active(0)
+
+        # Signal connects from above
+        checkbutton.connect('toggled', self.on_fixed_folder_toggled, combo5)
+        combo5.connect('changed', self.on_fixed_folder_changed)
+
 
     def setup_formats_tab(self):
 
@@ -2546,6 +2811,48 @@ class OptionsEditWin(GenericEditWin):
     # Callback class methods
 
 
+    def on_fixed_folder_toggled(self, checkbutton, combo):
+
+        """Called by callback in self.setup_files_tab().
+
+        Args:
+
+            checkbutton (Gtk.CheckButton): The widget clicked
+
+            combo (Gtk.ComboBox): Another widget to be modified by this
+                function
+
+        """
+
+        if not checkbutton.get_active():
+            self.edit_dict['use_fixed_folder'] = None
+            combo.set_sensitive(False)
+
+        else:
+
+            tree_iter = combo.get_active_iter()
+            model = combo.get_model()
+            pixbuf, name = model[tree_iter][:2]
+            self.edit_dict['use_fixed_folder'] = name
+            combo.set_sensitive(True)
+
+
+    def on_fixed_folder_changed(self, combo):
+
+        """Called by callback in self.setup_files_tab().
+
+        Args:
+
+            combo (Gtk.ComboBox): The widget clicked
+
+        """
+
+        tree_iter = combo.get_active_iter()
+        model = combo.get_model()
+        pixbuf, name = model[tree_iter][:2]
+        self.edit_dict['use_fixed_folder'] = name
+
+        
     def on_file_tab_button_clicked(self, button, entry, combo):
 
         """Called by callback in self.setup_files_tab().
@@ -2994,9 +3301,9 @@ class VideoEditWin(GenericEditWin):
         self.ok_button = None                   # Gtk.Button
         self.cancel_button = None               # Gtk.Button
         # (Non-standard widgets)
-        self.apply_button = None                # Gtk.Button
-        self.edit_button = None                 # Gtk.Button
-        self.remove_button = None               # Gtk.Button
+        self.apply_options_button = None        # Gtk.Button
+        self.edit_options_button = None         # Gtk.Button
+        self.remove_options_button = None       # Gtk.Button
 
 
         # IV list - other
@@ -3020,6 +3327,9 @@ class VideoEditWin(GenericEditWin):
         #   closed, the dictionary will still be empty)
         self.edit_dict = {}
 
+        # String identifying the media type
+        self.media_type = 'video'
+        
 
         # Code
         # ----
@@ -3070,6 +3380,7 @@ class VideoEditWin(GenericEditWin):
         """
 
         self.setup_general_tab()
+        self.setup_download_options_tab()
         self.setup_descrip_tab()
         self.setup_errors_warnings_tab()
 
@@ -3088,68 +3399,10 @@ class VideoEditWin(GenericEditWin):
             0, 0, 2, 1,
         )
 
-        entry = self.add_entry(grid,
-            None,
-            0, 1, 1, 1,
-        )
-        entry.set_text('#' + str(self.edit_obj.dbid))
-        entry.set_editable(False)
-        entry.set_hexpand(False)
-        entry.set_width_chars(8)
-
-        entry2 = self.add_entry(grid,
-            'name',
-            1, 1, 1, 1,
-        )
-        entry2.set_editable(False)
-
-        label = self.add_label(grid,
-            'Listed as',
-            0, 2, 1, 1,
-        )
-        label.set_hexpand(False)
-
-        entry3 = self.add_entry(grid,
-            'nickname',
-            1, 2, 1, 1,
-        )
-        entry3.set_editable(False)
-
-        parent_obj = self.edit_obj.parent_obj
-        if isinstance(parent_obj, media.Channel):
-            icon_path \
-            = self.app_obj.main_win_obj.icon_dict['channel_none_large']
-        elif isinstance(parent_obj, media.Playlist):
-            icon_path \
-            = self.app_obj.main_win_obj.icon_dict['playlist_none_large']
-        else:
-            icon_path \
-            = self.app_obj.main_win_obj.icon_dict['folder_none_large']
-
-        self.add_image(grid,
-            icon_path,
-            0, 3, 1, 1,
-        )
-
-        entry4 = self.add_entry(grid,
-            None,
-            1, 3, 1, 1,
-        )
-        entry4.set_text(parent_obj.name)
-        entry4.set_editable(False)
-
-        label2 = self.add_label(grid,
-            'URL',
-            0, 4, 1, 1,
-        )
-        label2.set_hexpand(False)
-
-        entry5 = self.add_entry(grid,
-            'source',
-            1, 4, 1, 1,
-        )
-        entry5.set_editable(False)
-
+        # The first sets of widgets are shared by multiple edit windows
+        self.add_container_properties(grid)
+        self.add_source_properties(grid)
+      
         label3 = self.add_label(grid,
             'File',
             0, 5, 1, 1,
@@ -3158,7 +3411,7 @@ class VideoEditWin(GenericEditWin):
 
         entry6 = self.add_entry(grid,
             None,
-            1, 5, 1, 1,
+            1, 5, 2, 1,
         )
         entry6.set_editable(False)
         if self.edit_obj.file_dir:
@@ -3173,27 +3426,27 @@ class VideoEditWin(GenericEditWin):
 
         # To avoid messing up the neat format of the rows above, add another
         #   grid, and put the next set of widgets inside it
-        grid2 = Gtk.Grid()
-        grid.attach(grid2, 0, 6, 2, 1)
-        grid2.set_vexpand(False)
-        grid2.set_border_width(self.spacing_size)
-        grid2.set_column_spacing(self.spacing_size)
-        grid2.set_row_spacing(self.spacing_size)
+        grid3 = Gtk.Grid()
+        grid.attach(grid3, 0, 6, 3, 1)
+        grid3.set_vexpand(False)
+        grid3.set_border_width(self.spacing_size)
+        grid3.set_column_spacing(self.spacing_size)
+        grid3.set_row_spacing(self.spacing_size)
 
-        checkbutton = self.add_checkbutton(grid2,
+        checkbutton = self.add_checkbutton(grid3,
             'Always simulate download of this video',
             'dl_sim_flag',
             0, 0, 1, 1,
         )
         checkbutton.set_sensitive(False)
 
-        label4 = self.add_label(grid2,
+        label4 = self.add_label(grid3,
             'Duration',
             1, 0, 1, 1,
         )
         label4.set_hexpand(False)
 
-        entry7 = self.add_entry(grid2,
+        entry7 = self.add_entry(grid3,
             None,
             2, 0, 1, 1,
         )
@@ -3203,20 +3456,20 @@ class VideoEditWin(GenericEditWin):
                 utils.convert_seconds_to_string(self.edit_obj.duration),
             )
 
-        checkbutton2 = self.add_checkbutton(grid2,
+        checkbutton2 = self.add_checkbutton(grid3,
             'Video is marked as unwatched',
             'new_flag',
             0, 1, 1, 1,
         )
         checkbutton2.set_sensitive(False)
 
-        label5 = self.add_label(grid2,
+        label5 = self.add_label(grid3,
             'File size',
             1, 1, 1, 1,
         )
         label5.set_hexpand(False)
 
-        entry8 = self.add_entry(grid2,
+        entry8 = self.add_entry(grid3,
             None,
             2, 1, 1, 1,
         )
@@ -3224,20 +3477,20 @@ class VideoEditWin(GenericEditWin):
         if self.edit_obj.file_size is not None:
             entry8.set_text(self.edit_obj.get_file_size_string())
 
-        checkbutton3 = self.add_checkbutton(grid2,
+        checkbutton3 = self.add_checkbutton(grid3,
             'Video is marked as favourite',
             'fav_flag',
             0, 2, 1, 1,
         )
         checkbutton3.set_sensitive(False)
 
-        label6 = self.add_label(grid2,
+        label6 = self.add_label(grid3,
             'Upload time',
             1, 2, 1, 1,
         )
         label6.set_hexpand(False)
 
-        entry9 = self.add_entry(grid2,
+        entry9 = self.add_entry(grid3,
             None,
             2, 2, 1, 1,
         )
@@ -3245,20 +3498,20 @@ class VideoEditWin(GenericEditWin):
         if self.edit_obj.upload_time is not None:
             entry9.set_text(self.edit_obj.get_upload_time_string())
 
-        checkbutton4 = self.add_checkbutton(grid2,
+        checkbutton4 = self.add_checkbutton(grid3,
             'Video has been downloaded',
             'dl_flag',
             0, 3, 1, 1,
         )
         checkbutton4.set_sensitive(False)
 
-        label7 = self.add_label(grid2,
+        label7 = self.add_label(grid3,
             'Receive time',
             1, 3, 1, 1,
         )
         label7.set_hexpand(False)
 
-        entry10 = self.add_entry(grid2,
+        entry10 = self.add_entry(grid3,
             None,
             2, 3, 1, 1,
         )
@@ -3266,28 +3519,8 @@ class VideoEditWin(GenericEditWin):
         if self.edit_obj.receive_time is not None:
             entry10.set_text(self.edit_obj.get_receive_time_string())
 
-        # To avoid messing up the formatting again, put the next buttons inside
-        #   an hbox
-        hbox = Gtk.HBox()
-        grid.attach(hbox, 0, 7, 2, 1)
 
-        self.apply_button = Gtk.Button('Apply download options')
-        hbox.pack_start(self.apply_button, True, True, self.spacing_size)
-        self.apply_button.connect('clicked', self.on_button_apply_clicked)
-
-        self.edit_button = Gtk.Button('Edit download options')
-        hbox.pack_start(self.edit_button, True, True, self.spacing_size)
-        self.edit_button.connect('clicked', self.on_button_edit_clicked)
-
-        self.remove_button = Gtk.Button('Remove download options')
-        hbox.pack_start(self.remove_button, True, True, self.spacing_size)
-        self.remove_button.connect('clicked', self.on_button_remove_clicked)
-
-        if self.edit_obj.options_obj:
-            self.apply_button.set_sensitive(False)
-        else:
-            self.edit_button.set_sensitive(False)
-            self.remove_button.set_sensitive(False)
+#   def setup_download_options_tab():   # Inherited from GenericConfigWin
 
 
     def setup_descrip_tab(self):
@@ -3417,9 +3650,9 @@ class ChannelPlaylistEditWin(GenericEditWin):
         self.ok_button = None                   # Gtk.Button
         self.cancel_button = None               # Gtk.Button
         # (Non-standard widgets)
-        self.apply_button = None                # Gtk.Button
-        self.edit_button = None                 # Gtk.Button
-        self.remove_button = None               # Gtk.Button
+        self.apply_options_button = None        # Gtk.Button
+        self.edit_options_button = None         # Gtk.Button
+        self.remove_options_button = None       # Gtk.Button
 
 
         # IV list - other
@@ -3496,6 +3729,7 @@ class ChannelPlaylistEditWin(GenericEditWin):
         """
 
         self.setup_general_tab()
+        self.setup_download_options_tab()
         self.setup_errors_warnings_tab()
 
 
@@ -3510,176 +3744,95 @@ class ChannelPlaylistEditWin(GenericEditWin):
 
         self.add_label(grid,
             '<u>General properties</u>',
-            0, 0, 2, 1,
+            0, 0, 3, 1,
         )
 
-        entry = self.add_entry(grid,
-            None,
-            0, 1, 1, 1,
-        )
-        entry.set_text('#' + str(self.edit_obj.dbid))
-        entry.set_editable(False)
-        entry.set_hexpand(False)
-        entry.set_width_chars(8)
-
-        entry2 = self.add_entry(grid,
-            'name',
-            1, 1, 1, 1,
-        )
-        entry2.set_editable(False)
-
-        label = self.add_label(grid,
-            'Listed as',
-            0, 2, 1, 1,
-        )
-        label.set_hexpand(False)
-
-        entry3 = self.add_entry(grid,
-            'nickname',
-            1, 2, 1, 1,
-        )
-        entry3.set_editable(False)
-
-        main_win_obj = self.app_obj.main_win_obj
-        parent_obj = self.edit_obj.parent_obj
-        if parent_obj:
-            icon_path = main_win_obj.icon_dict['folder_none_large']
-        else:
-            icon_path = main_win_obj.icon_dict['folder_no_parent_none_large']
-
-        self.add_image(grid,
-            icon_path,
-            0, 3, 1, 1,
-        )
-
-        entry4 = self.add_entry(grid,
-            None,
-            1, 3, 1, 1,
-        )
-        entry4.set_editable(False)
-        if parent_obj:
-            entry4.set_text(parent_obj.name)
-
-        label2 = self.add_label(grid,
-            'URL',
-            0, 4, 1, 1,
-        )
-        label2.set_hexpand(False)
-
-        entry5 = self.add_entry(grid,
-            'source',
-            1, 4, 1, 1,
-        )
-        entry5.set_editable(False)
-
-        label3 = self.add_label(grid,
-            'Location',
-            0, 5, 1, 1,
-        )
-        label3.set_hexpand(False)
-
-        entry6 = self.add_entry(grid,
-            None,
-            1, 5, 1, 1,
-        )
-        entry6.set_editable(False)
-        entry6.set_text(self.edit_obj.get_dir(self.app_obj))
+        # The first sets of widgets are shared by multiple edit windows
+        self.add_container_properties(grid)
+        self.add_source_properties(grid)
+        self.add_destination_properties(grid)
 
         # To avoid messing up the neat format of the rows above, add another
         #   grid, and put the next set of widgets inside it
-        grid2 = Gtk.Grid()
-        grid.attach(grid2, 0, 6, 2, 1)
-        grid2.set_vexpand(False)
-        grid2.set_border_width(self.spacing_size)
-        grid2.set_column_spacing(self.spacing_size)
-        grid2.set_row_spacing(self.spacing_size)
+        grid3 = Gtk.Grid()
+        grid.attach(grid3, 0, 6, 3, 1)
+        grid3.set_vexpand(False)
+        grid3.set_column_spacing(self.spacing_size)
+        grid3.set_row_spacing(self.spacing_size)
 
-        checkbutton = self.add_checkbutton(grid2,
+        checkbutton = self.add_checkbutton(grid3,
             'Always simulate download of videos in this ' + self.media_type,
             'dl_sim_flag',
             0, 0, 1, 1,
         )
         checkbutton.set_sensitive(False)
 
-        checkbutton2 = self.add_checkbutton(grid2,
-            'This ' + self.media_type + ' is marked as a favourite',
-            'fav_flag',
+        checkbutton2 = self.add_checkbutton(grid3,
+            'Disable checking/downloading for this ' + self.media_type,
+            'dl_disable_flag',
             0, 1, 1, 1,
         )
         checkbutton2.set_sensitive(False)
 
-        self.add_label(grid2,
+        checkbutton3 = self.add_checkbutton(grid3,
+            'This ' + self.media_type + ' is marked as a favourite',
+            'fav_flag',
+            0, 2, 1, 1,
+        )
+        checkbutton3.set_sensitive(False)
+
+        self.add_label(grid3,
             'Total videos',
             1, 0, 1, 1,
         )
-        entry7 = self.add_entry(grid2,
+        entry8 = self.add_entry(grid3,
             'vid_count',
             2, 0, 1, 1,
         )
-        entry7.set_editable(False)
-        entry7.set_width_chars(8)
-        entry7.set_hexpand(False)
-
-        self.add_label(grid2,
-            'New videos',
-            1, 1, 1, 1,
-        )
-        entry8 = self.add_entry(grid2,
-            'new_count',
-            2, 1, 1, 1,
-        )
         entry8.set_editable(False)
         entry8.set_width_chars(8)
         entry8.set_hexpand(False)
 
-        self.add_label(grid2,
-            'Favourite videos',
-            1, 2, 1, 1,
+        self.add_label(grid3,
+            'New videos',
+            1, 1, 1, 1,
         )
-        entry9 = self.add_entry(grid2,
-            'fav_count',
-            2, 2, 1, 1,
+        entry9 = self.add_entry(grid3,
+            'new_count',
+            2, 1, 1, 1,
         )
         entry9.set_editable(False)
         entry9.set_width_chars(8)
         entry9.set_hexpand(False)
 
-        self.add_label(grid2,
-            'Downloaded videos',
-            1, 3, 1, 1,
+        self.add_label(grid3,
+            'Favourite videos',
+            1, 2, 1, 1,
         )
-        entry10 = self.add_entry(grid2,
-            'dl_count',
-            2, 3, 1, 1,
+        entry10 = self.add_entry(grid3,
+            'fav_count',
+            2, 2, 1, 1,
         )
         entry10.set_editable(False)
         entry10.set_width_chars(8)
         entry10.set_hexpand(False)
 
-        # To avoid messing up the formatting again, but the next buttons inside
-        #   an hbox
-        hbox = Gtk.HBox()
-        grid.attach(hbox, 0, 7, 2, 1)
-
-        self.apply_button = Gtk.Button('Apply download options')
-        hbox.pack_start(self.apply_button, True, True, self.spacing_size)
-        self.apply_button.connect('clicked', self.on_button_apply_clicked)
-
-        self.edit_button = Gtk.Button('Edit download options')
-        hbox.pack_start(self.edit_button, True, True, self.spacing_size)
-        self.edit_button.connect('clicked', self.on_button_edit_clicked)
-
-        self.remove_button = Gtk.Button('Remove download options')
-        hbox.pack_start(self.remove_button, True, True, self.spacing_size)
-        self.remove_button.connect('clicked', self.on_button_remove_clicked)
-
-        if self.edit_obj.options_obj:
-            self.apply_button.set_sensitive(False)
-        else:
-            self.edit_button.set_sensitive(False)
-            self.remove_button.set_sensitive(False)
+        self.add_label(grid3,
+            'Downloaded videos',
+            1, 3, 1, 1,
+        )
+        entry11 = self.add_entry(grid3,
+            'dl_count',
+            2, 3, 1, 1,
+        )
+        entry11.set_editable(False)
+        entry11.set_width_chars(8)
+        entry11.set_hexpand(False)
 
 
+#   def setup_download_options_tab():   # Inherited from GenericConfigWin
+
+            
     def setup_errors_warnings_tab(self):
 
         """Called by self.setup_tabs().
@@ -3780,9 +3933,9 @@ class FolderEditWin(GenericEditWin):
         self.ok_button = None                   # Gtk.Button
         self.cancel_button = None               # Gtk.Button
         # (Non-standard widgets)
-        self.apply_button = None                # Gtk.Button
-        self.edit_button = None                 # Gtk.Button
-        self.remove_button = None               # Gtk.Button
+        self.apply_options_button = None        # Gtk.Button
+        self.edit_options_button = None         # Gtk.Button
+        self.remove_options_button = None       # Gtk.Button
 
 
         # IV list - other
@@ -3806,6 +3959,9 @@ class FolderEditWin(GenericEditWin):
         #   closed, the dictionary will still be empty)
         self.edit_dict = {}
 
+        # String identifying the media type
+        self.media_type = 'folder'
+        
 
         # Code
         # ----
@@ -3856,6 +4012,7 @@ class FolderEditWin(GenericEditWin):
         """
 
         self.setup_general_tab()
+        self.setup_download_options_tab()
 
 
     def setup_general_tab(self):
@@ -3872,148 +4029,79 @@ class FolderEditWin(GenericEditWin):
             0, 0, 2, 1,
         )
 
-        entry = self.add_entry(grid,
-            None,
-            0, 1, 1, 1,
-        )
-        entry.set_text('#' + str(self.edit_obj.dbid))
-        entry.set_editable(False)
-        entry.set_hexpand(False)
-        entry.set_width_chars(8)
-
-        entry2 = self.add_entry(grid,
-            'name',
-            1, 1, 1, 1,
-        )
-        entry2.set_editable(False)
-
-        label = self.add_label(grid,
-            'Listed as',
-            0, 2, 1, 1,
-        )
-        label.set_hexpand(False)
-
-        entry3 = self.add_entry(grid,
-            'nickname',
-            1, 2, 1, 1,
-        )
-        entry3.set_editable(False)
-
-        main_win_obj = self.app_obj.main_win_obj
-        parent_obj = self.edit_obj.parent_obj
-        if parent_obj:
-            icon_path = main_win_obj.icon_dict['folder_none_large']
-        else:
-            icon_path = main_win_obj.icon_dict['folder_no_parent_none_large']
-
-        self.add_image(grid,
-            icon_path,
-            0, 3, 1, 1,
-        )
-
-        entry4 = self.add_entry(grid,
-            None,
-            1, 3, 1, 1,
-        )
-        entry4.set_editable(False)
-        if parent_obj:
-            entry4.set_text(parent_obj.name)
-
-        label2 = self.add_label(grid,
-            'Location',
-            0, 4, 1, 1,
-        )
-        label2.set_hexpand(False)
-
-        entry5 = self.add_entry(grid,
-            None,
-            1, 4, 1, 1,
-        )
-        entry5.set_editable(False)
-        entry5.set_text(self.edit_obj.get_dir(self.app_obj))
+        # The first sets of widgets are shared by multiple edit windows
+        self.add_container_properties(grid)
+        self.add_destination_properties(grid)
 
         # To avoid messing up the neat format of the rows above, add another
         #   grid, and put the next set of widgets inside it
-        grid2 = Gtk.Grid()
-        grid.attach(grid2, 0, 5, 2, 1)
-        grid2.set_vexpand(False)
-        grid2.set_border_width(self.spacing_size)
-        grid2.set_column_spacing(self.spacing_size)
-        grid2.set_row_spacing(self.spacing_size)
+        grid3 = Gtk.Grid()
+        grid.attach(grid3, 0, 6, 3, 1)
+        grid3.set_vexpand(False)
+        grid3.set_border_width(self.spacing_size)
+        grid3.set_column_spacing(self.spacing_size)
+        grid3.set_row_spacing(self.spacing_size)
 
-        checkbutton = self.add_checkbutton(grid2,
+        checkbutton = self.add_checkbutton(grid3,
             'Always simulate download of videos',
             'dl_sim_flag',
             0, 0, 1, 1,
         )
         checkbutton.set_sensitive(False)
 
-        checkbutton2 = self.add_checkbutton(grid2,
-            'This folder is marked as a favourite',
-            'fav_flag',
+        checkbutton2 = self.add_checkbutton(grid3,
+            'Disable checking/downloading for this folder',
+            'dl_disable_flag',
             0, 1, 1, 1,
         )
         checkbutton2.set_sensitive(False)
 
-        checkbutton3 = self.add_checkbutton(grid2,
-            'This folder is hidden',
-            'hidden_flag',
+        checkbutton3 = self.add_checkbutton(grid3,
+            'This folder is marked as a favourite',
+            'fav_flag',
             0, 2, 1, 1,
         )
         checkbutton3.set_sensitive(False)
 
-        checkbutton4 = self.add_checkbutton(grid2,
+        checkbutton4 = self.add_checkbutton(grid3,
+            'This folder is hidden',
+            'hidden_flag',
+            0, 3, 1, 1,
+        )
+        checkbutton4.set_sensitive(False)
+
+        checkbutton5 = self.add_checkbutton(grid3,
             'This folder can\'t be deleted by the user',
             'fixed_flag',
             1, 0, 1, 1,
         )
-        checkbutton4.set_sensitive(False)
+        checkbutton5.set_sensitive(False)
 
-        checkbutton5 = self.add_checkbutton(grid2,
+        checkbutton6 = self.add_checkbutton(grid3,
             'This is a system-controlled folder',
             'priv_flag',
             1, 1, 1, 1,
         )
-        checkbutton5.set_sensitive(False)
+        checkbutton6.set_sensitive(False)
 
-        checkbutton6 = self.add_checkbutton(grid2,
+        checkbutton7 = self.add_checkbutton(grid3,
             'Only videos can be added to this folder',
             'restrict_flag',
             1, 2, 1, 1,
         )
-        checkbutton6.set_sensitive(False)
+        checkbutton7.set_sensitive(False)
 
-        checkbutton7 = self.add_checkbutton(grid2,
+        checkbutton8 = self.add_checkbutton(grid3,
             'All contents deleted when ' \
             + utils.upper_case_first(__main__. __packagename__) \
             + ' shuts down',
             'temp_flag',
             1, 3, 1, 1,
         )
-        checkbutton7.set_sensitive(False)
+        checkbutton8.set_sensitive(False)
 
-        # To avoid messing up the formatting again, but the next buttons inside
-        #   an hbox
-        hbox = Gtk.HBox()
-        grid.attach(hbox, 0, 6, 2, 1)
 
-        self.apply_button = Gtk.Button('Apply download options')
-        hbox.pack_start(self.apply_button, True, True, self.spacing_size)
-        self.apply_button.connect('clicked', self.on_button_apply_clicked)
-
-        self.edit_button = Gtk.Button('Edit download options')
-        hbox.pack_start(self.edit_button, True, True, self.spacing_size)
-        self.edit_button.connect('clicked', self.on_button_edit_clicked)
-
-        self.remove_button = Gtk.Button('Remove download options')
-        hbox.pack_start(self.remove_button, True, True, self.spacing_size)
-        self.remove_button.connect('clicked', self.on_button_remove_clicked)
-
-        if self.edit_obj.options_obj:
-            self.apply_button.set_sensitive(False)
-        else:
-            self.edit_button.set_sensitive(False)
-            self.remove_button.set_sensitive(False)
+#   def setup_download_options_tab():   # Inherited from GenericConfigWin
 
 
     # Callback class methods
@@ -4153,9 +4241,7 @@ class SystemPrefWin(GenericPrefWin):
 
         # (This is a placeholder, to be replaced when we add translations)
         store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
-        pixbuf = self.app_obj.file_manager_obj.load_to_pixbuf(
-            os.path.abspath(os.path.join('icons', 'locale', 'flag_uk.png')),
-        )
+        pixbuf = self.app_obj.main_win_obj.pixbuf_dict['flag_uk']
         store.append( [pixbuf, 'English'] )
 
         combo = Gtk.ComboBox.new_with_model(store)
@@ -4516,6 +4602,47 @@ class SystemPrefWin(GenericPrefWin):
             self.on_match_spinbutton_changed,
         )
 
+        # Video deletion preferences
+        self.add_label(grid,
+            '<u>Video deletion preferences</u>',
+            0, 7, grid_width, 1,
+        )
+
+        checkbutton2 = self.add_checkbutton(grid,
+            'Automatically delete downloaded videos after this many days',
+            self.app_obj.auto_delete_flag,
+            True,               # Can be toggled by user
+            0, 8, (grid_width - 1), 1,
+        )
+        # Signal connect appears below
+
+        spinbutton3 = self.add_spinbutton(grid,
+            1, 999, 1, self.app_obj.auto_delete_days,
+            2, 8, 1, 1,
+        )
+        # Signal connect appears below
+
+        checkbutton3 = self.add_checkbutton(grid,
+            '...but only delete videos which have been watched',
+            self.app_obj.auto_delete_watched_flag,
+            True,               # Can be toggled by user
+            0, 9, grid_width, 1,
+        )
+        # Signal connect appears below
+        
+        # Signal connects from above
+        checkbutton2.connect(
+            'toggled',
+            self.on_auto_delete_button_toggled,
+            spinbutton3,
+            checkbutton2,
+        )
+        spinbutton3.connect(
+            'value-changed',
+            self.on_auto_delete_spinbutton_changed,
+        )
+        checkbutton3.connect('toggled', self.on_delete_watched_button_toggled)
+
 
     def setup_operations_tab(self):
 
@@ -4540,6 +4667,8 @@ class SystemPrefWin(GenericPrefWin):
             0, 1, grid_width, 1,
         )
         checkbutton.connect('toggled', self.on_auto_update_button_toggled)
+        if __main__.__debian_install_flag__:
+            checkbutton.set_sensitive(False)
 
         checkbutton2 = self.add_checkbutton(grid,
             'Automatically save files at the end of a download/update/' \
@@ -4752,7 +4881,7 @@ class SystemPrefWin(GenericPrefWin):
         )
         combo2.connect('changed', self.on_update_combo_changed)
 
-        if __main__.__disable_ytdl_update_flag__:
+        if __main__.__debian_install_flag__:
             combo.set_sensitive(False)
             combo2.set_sensitive(False)
 
@@ -4866,6 +4995,53 @@ class SystemPrefWin(GenericPrefWin):
     # Callback class methods
 
 
+    def on_auto_delete_button_toggled(self, checkbutton, spinbutton,
+    checkbutton2):
+
+        """Called from callback in self.setup_videos_tab().
+
+        Enables/disables automatic deletion of downloaded videos.
+        
+        Args:
+
+            checkbutton (Gtk.CheckButton): The widget clicked
+
+            spinbutton (Gtk.SpinButton): A widget to be (de)sensitised
+
+            checkbutton2 (Gtk.CheckButton): Another widget to be
+                (de)sensitised
+
+        """
+
+        if checkbutton.get_active() \
+        and not self.app_obj.auto_delete_flag:
+            self.app_obj.set_auto_delete_flag(True)
+            spinbutton.set_sensitive(True)
+            checkbutton2.set_sensitive(True)
+            
+        elif not checkbutton.get_active() \
+        and self.app_obj.auto_delete_flag:
+            self.app_obj.set_auto_delete_flag(False)
+            spinbutton.set_sensitive(False)
+            checkbutton2.set_sensitive(False)
+            
+
+    def on_auto_delete_spinbutton_changed(self, spinbutton):
+
+        """Called from callback in self.setup_videos_tab().
+
+        Sets the number of days after which downloaded videos should be
+        deleted.
+
+        Args:
+
+            spinbutton (Gtk.SpinButton): The widget clicked
+
+        """
+
+        self.app_obj.set_auto_delete_days(spinbutton.get_value())
+
+
     def on_auto_update_button_toggled(self, checkbutton):
 
         """Called from callback in self.setup_general_tab().
@@ -5135,6 +5311,27 @@ class SystemPrefWin(GenericPrefWin):
                 )
 
 
+    def on_delete_watched_button_toggled(self, checkbutton):
+
+        """Called from callback in self.setup_videos_tab().
+
+        Enables/disables automatic deletion of videos, but only those that have
+        been watched.
+        
+        Args:
+
+            checkbutton (Gtk.CheckButton): The widget clicked
+
+        """
+
+        if checkbutton.get_active() \
+        and not self.app_obj.auto_delete_watched_flag:
+            self.app_obj.set_auto_delete_watched_flag(True)
+        elif not checkbutton.get_active() \
+        and self.app_obj.auto_delete_watched_flag:
+            self.app_obj.set_auto_delete_watched_flag(False)
+
+
     def on_dialogue_button_toggled(self, checkbutton):
 
         """Called from callback in self.setup_general_tab().
diff --git a/tartube/downloads.py b/tartube/downloads.py
index 1788972..f2dac7c 100755
--- a/tartube/downloads.py
+++ b/tartube/downloads.py
@@ -341,6 +341,53 @@ class DownloadManager(threading.Thread):
                         break
 
 
+    def check_master_slave(self, media_data_obj):
+
+        """Called by VideoDownloader.do_download().
+
+        When two channels/playlists/folders share a download destination, we
+        don't want to download both of them at the same time.
+
+        This function is called when media_data_obj is about to be
+        downloaded.
+
+        Every worker is checked, to see if it's downloading to the same
+        destination. If so, this function returns True, and
+        VideoDownloader.do_download() waits a few seconds, before trying
+        again.
+
+        Otherwise, this function returns False, and
+        VideoDownloader.do_download() is free to start its download.
+
+        Args:
+
+            media_data_obj (media.Channel, media.Playlist, media.Folder):
+                The media data object that the calling function wants to
+                download
+
+        Returns:
+
+            True or False, as described above
+
+        """
+
+        for worker_obj in self.worker_list:
+
+            if not worker_obj.available_flag \
+            and worker_obj.download_item_obj:
+
+                other_obj = worker_obj.download_item_obj.media_data_obj
+
+                if other_obj.dbid != media_data_obj.dbid \
+                and (
+                    other_obj.dbid == media_data_obj.master_dbid \
+                    or other_obj.dbid in media_data_obj.slave_dbid_list
+                ):
+                    return True
+
+        return False
+                
+        
     def check_workers_all_finished(self):
 
         """Called by self.run().
@@ -737,7 +784,7 @@ class DownloadList(object):
 
         # Code
         # ----
-
+        
         # For each media data object to be downloaded, created a
         #   downloads.DownloadItem object, and update the IVs above
         if not media_data_obj:
@@ -763,7 +810,7 @@ class DownloadList(object):
         else:
 
             # Use the specified media data object. The True value tells
-            #   self.create_item to download media_data_obj, even if it is a
+            #   self.create_item() to download media_data_obj, even if it is a
             #   video in a channel or a playlist (which otherwise would be
             #   handled by downloading the channel/playlist)
             self.create_item(media_data_obj, True)
@@ -810,8 +857,13 @@ class DownloadList(object):
             - media.Video objects in any restricted folder
             - media.Video objects in the fixed 'Unsorted Videos' folder which
                 are already marked as downloaded
+            - media.Video objects which have an ancestor (e.g. a parent
+                media.Channel) for which checking/downloading is disabled
+            - media.Channel and media.Playlist objects for which checking/
+                downloading are disabled, or which have an ancestor (e.g. a
+                parent media.folder) for which checking/downloading is disabled
             - media.Folder objects
-
+            
         Adds the resulting downloads.DownloadItem object to this object's IVs.
 
         Args:
@@ -855,6 +907,22 @@ class DownloadList(object):
                 and not init_flag
             ):
                 return
+            
+        # Don't create a download.DownloadItem object if the media data object
+        #   has an ancestor for which checking/downloading is disabled
+        if isinstance(media_data_obj, media.Video):
+            dl_disable_flag = False
+        else:
+            dl_disable_flag = media_data_obj.dl_disable_flag
+
+        parent_obj = media_data_obj.parent_obj
+
+        while not dl_disable_flag and parent_obj is not None:
+            dl_disable_flag = parent_obj.dl_disable_flag
+            parent_obj = parent_obj.parent_obj
+
+        if dl_disable_flag:
+            return
 
         # Don't create a download.DownloadItem object for a media.Folder,
         #   obviously
@@ -1102,11 +1170,26 @@ class VideoDownloader(object):
         # The time (in seconds) between iterations of the loop in
         #   self.do_download()
         self.sleep_time = 0.1
+        # The time (in seconds) to wait for an existing download, which shares
+        #   a common download destination with this media data object, to
+        #   finish downloading
+        self.long_sleep_time = 10
 
         # Flag set to True if we are simulating downloads for this media data
         #   object, or False if we actually downloading videos (set below)
         self.dl_sim_flag = None
 
+        # Flag set to True by a call from any function to self.stop_soon()
+        # After being set to True, this VideoDownloader should give up after
+        #   the next call to self.confirm_new_video(), .confirm_old_video()
+        #   .confirm_sim_video()
+        self.stop_soon_flag = False
+        # When self.stop_soon_flag is True, the next call to
+        #   self.confirm_new_video(), .confirm_old_video() or
+        #   .confirm_sim_video() sets this flag to True, informing
+        #   self.do_download() that it can stop the child process
+        self.stop_now_flag = False
+
         # youtube-dl is passed a URL, which might represent an individual
         #   video, a channel or a playlist
         # Assume it's an individual video unless youtube-dl reports a
@@ -1177,17 +1260,20 @@ class VideoDownloader(object):
         #   download
         media_data_obj = self.download_item_obj.media_data_obj
 
-        # If the media data object is a video, channel or playlist, it can be
-        #   marked as a simulated download only
-        # If it's a video inside a folder and the folder itself is marked as
-        #   simulated downloads only, apply that to all videos in the folder
-        if self.download_manager_obj.force_sim_flag \
-        or media_data_obj.dl_sim_flag \
-        or (
-            isinstance(media_data_obj, media.Video) \
-            and isinstance(media_data_obj.parent_obj, media.Folder) \
-            and media_data_obj.parent_obj.dl_sim_flag
-        ):
+        # All media data objects can be marked as simulate downloads only. The
+        #   setting applies not just to the media data object, but all of its
+        #   descendants
+        if self.download_manager_obj.force_sim_flag:
+            dl_sim_flag = True
+        else:        
+            dl_sim_flag = media_data_obj.dl_sim_flag
+            parent_obj = media_data_obj.parent_obj
+
+            while not dl_sim_flag and parent_obj is not None:
+                dl_sim_flag = parent_obj.dl_sim_flag
+                parent_obj = parent_obj.parent_obj
+
+        if dl_sim_flag:
             self.dl_sim_flag = True
             self.video_num = 0
             self.video_total = 0
@@ -1229,6 +1315,18 @@ class VideoDownloader(object):
         #   time it was checked/downloaded
         self.download_item_obj.media_data_obj.reset_error_warning()
 
+        # If two channels/playlists/folders share a download destination, we
+        #   don't want to download both of them at the same time
+        # If this media data obj shares a download destination with another
+        #   downloads.DownloadWorker, wait until that download has finished
+        #   before starting this one
+        if not isinstance(self.download_item_obj.media_data_obj, media.Video):
+            
+            while self.download_manager_obj.check_master_slave(
+                self.download_item_obj.media_data_obj,
+            ):
+                time.sleep(self.long_sleep_time)
+            
         # Prepare a system command...
         cmd_list = self.get_system_cmd()
         # ...and create a new child process using that command
@@ -1307,6 +1405,12 @@ class VideoDownloader(object):
                     + ' to fetch a video\'s JSON data',
                 )
 
+            # Stop this video downloader, if required to do so, having just
+            #   finished checking/downloading a video
+            if self.stop_now_flag:
+                self.stop()
+
+
         # The child process has finished
         while not self.stderr_queue.empty():
 
@@ -1456,6 +1560,11 @@ class VideoDownloader(object):
                 options_manager_obj.options_dict['keep_thumbnail'],
             )
 
+        # This VideoDownloader can now stop, if required to do so after a video
+        #   has been checked/downloaded
+        if self.stop_soon_flag:
+            self.stop_now_flag = True
+            
 
     def confirm_old_video(self, dir_path, filename, extension):
 
@@ -1549,16 +1658,37 @@ class VideoDownloader(object):
                 = self.download_worker_obj.options_manager_obj
 
                 # Update the main window
-                GObject.timeout_add(
-                    0,
-                    app_obj.announce_video_download,
-                    self.download_item_obj,
-                    video_obj,
-                    options_manager_obj.options_dict['keep_description'],
-                    options_manager_obj.options_dict['keep_info'],
-                    options_manager_obj.options_dict['keep_thumbnail'],
-                )
+                if media_data_obj.master_dbid != media_data_obj.dbid:
 
+                    # The container is storing its videos in another
+                    #   container's sub-directory, which (probably) explains
+                    #   why we couldn't find a match. Don't add anything to the
+                    #   Results List
+                    GObject.timeout_add(
+                        0,
+                        app_obj.announce_video_clone,
+                        video_obj,
+                    )
+
+                else:
+
+                    # Do add an entry to the Results List (as well as updating
+                    #   the Video Catalogue, as normal)
+                    GObject.timeout_add(
+                        0,
+                        app_obj.announce_video_download,
+                        self.download_item_obj,
+                        video_obj,
+                        options_manager_obj.options_dict['keep_description'],
+                        options_manager_obj.options_dict['keep_info'],
+                        options_manager_obj.options_dict['keep_thumbnail'],
+                    )
+
+        # This VideoDownloader can now stop, if required to do so after a video
+        #   has been checked/downloaded
+        if self.stop_soon_flag:
+            self.stop_now_flag = True
+            
 
     def confirm_sim_video(self, json_dict):
 
@@ -1851,6 +1981,11 @@ class VideoDownloader(object):
         if stop_flag:
             self.stop()
 
+        # This VideoDownloader can now stop, if required to do so after a video
+        #   has been checked/downloaded
+        elif self.stop_soon_flag:
+            self.stop_now_flag = True
+            
 
     def create_child_process(self, cmd_list):
 
@@ -2243,6 +2378,9 @@ class VideoDownloader(object):
         if DEBUG_FUNC_FLAG:
             print('dl 1997 get_system_cmd')
 
+        # Import things for convenience
+        app_obj = self.download_manager_obj.app_obj
+        media_data_obj = self.download_item_obj.media_data_obj
         options_list = self.download_worker_obj.options_list
 
         # Simulate the download, rather than actually downloading videos, if
@@ -2250,13 +2388,30 @@ class VideoDownloader(object):
         if self.dl_sim_flag:
             options_list.append('--dump-json')
 
+        # If actually downloading videos, create an archive file so that, if
+        #   the user deletes the videos, youtube-dl won't try to download them
+        #   again
+        else:
+
+            # (Create the archive file in the media data object's own
+            #   sub-directory, not the alternative download destination, as
+            #   this helps youtube-dl to work the way we want it)
+            if isinstance(media_data_obj, media.Video):
+                dl_path = media_data_obj.parent_obj.get_dir(app_obj)
+            else:
+                dl_path = media_data_obj.get_dir(app_obj)
+
+            options_list.append('--download-archive')
+            options_list.append(
+                os.path.abspath(os.path.join(dl_path, 'ytdl-archive.txt')),
+            )
+
         # Show verbose output (youtube-dl debugging mode), if required
-        if self.download_manager_obj.app_obj.ytdl_write_verbose_flag:
+        if app_obj.ytdl_write_verbose_flag:
             options_list.append('--verbose')
 
         # Set the list
-        cmd_list = [self.download_manager_obj.app_obj.ytdl_path] \
-        + options_list + [self.download_item_obj.media_data_obj.source]
+        cmd_list = [app_obj.ytdl_path] + options_list + [media_data_obj.source]
 
         return cmd_list
 
@@ -2478,7 +2633,8 @@ class VideoDownloader(object):
 
     def stop(self):
 
-        """Called by downloads.DownloadWorker.close().
+        """Called by downloads.DownloadWorker.close() and also by
+        mainwin.MainWin.on_progress_list_stop_now().
 
         Terminates the child process and sets this object's return code to
         self.STOPPED.
@@ -2505,6 +2661,21 @@ class VideoDownloader(object):
             self.set_return_code(self.STOPPED)
 
 
+    def stop_soon(self):
+
+        """Can be called by anything. Currently called by
+        mainwin.MainWin.on_progress_list_stop_soon().
+
+        Sets the flag that causes this VideoDownloader to stop after the
+        current video.
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('dl 2224 stop_soon')
+
+        self.stop_soon_flag = True
+
+
 class PipeReader(threading.Thread):
 
     """Called by downloads.VideoDownloader.__init__().
diff --git a/tartube/formats.py b/tartube/formats.py
index 914fa31..596bf9e 100755
--- a/tartube/formats.py
+++ b/tartube/formats.py
@@ -457,15 +457,19 @@ SMALL_ICON_DICT = {
     'playlist_small': 'playlist.png',
     'folder_small': 'folder.png',
 
-    'download_small': 'download.png',
+    'archived_small': 'archived.png',
     'check_small': 'check.png',
+    'download_small': 'download.png',
+    'error_small': 'error.png',
+    'folder_black_small': 'folder_black.png',
+    'folder_blue_small': 'folder_blue.png',
+    'folder_green_small': 'folder_green.png',
+    'folder_red_small': 'folder_red.png',
     'have_file_small': 'have_file.png',
     'no_file_small': 'no_file.png',
-    'ok_small': 'ok.png',
-    'error_small': 'error.png',
-    'warning_small': 'warning.png',
     'system_error_small': 'system_error.png',
     'system_warning_small': 'system_warning.png',
+    'warning_small': 'warning.png',
 }
 
 WIN_ICON_LIST = [
diff --git a/tartube/mainapp.py b/tartube/mainapp.py
index 46f6fcd..b95dd0f 100755
--- a/tartube/mainapp.py
+++ b/tartube/mainapp.py
@@ -46,9 +46,13 @@ try:
 except:
     HAVE_MOVIEPY_FLAG = False
 
-from xdg.BaseDirectory import xdg_config_home
-
+try:
+    from xdg.BaseDirectory import xdg_config_home
+    HAVE_XDG_FLAG = True
+except:
+    HAVE_XDG_FLAG = False
 
+        
 # Import our modules
 import __main__
 import config
@@ -91,12 +95,6 @@ class TartubeApp(Gtk.Application):
             flags=Gio.ApplicationFlags.FLAGS_NONE,
             **kwargs)
 
-        # Debugging flags (can only be set by editing the source code)
-        # Force Tartube to use the pre-v1.1.014 location for the config file
-        self.debug_old_config_flag = False
-        # Delete the config file and the contents of Tartube's data directory
-        #   on startup
-        self.debug_delete_data_flag = False
         # After installation, don't show the dialogue windows prompting the
         #   user to choose Tartube's data directory; just use the default
         #   location
@@ -280,23 +278,23 @@ class TartubeApp(Gtk.Application):
 
         # Name of the Tartube config file
         self.config_file_name = 'settings.json'
-        # Path to the config file. Before v1.1.014, the Tartube config file was
-        #   stored in self.script_parent_dir; it is now stored in
-        #   $XDG_CONFIG_HOME/tartube
+        # The config file can be stored at one of two locations, depending on
+        #   the value of __main__.__debian_install_flag__
         self.config_file_path = os.path.abspath(
-            os.path.join(
-                xdg_config_home,
-                __main__.__packagename__,
-                self.config_file_name,
-               ),
-        )
-        self.config_file_old_path = os.path.abspath(
             os.path.join(self.script_parent_dir, self.config_file_name),
         )
 
-        if self.debug_old_config_flag:
-            self.config_file_path = self.config_file_old_path
-        
+        if not HAVE_XDG_FLAG:
+            self.config_file_xdg_path = None
+        else:
+            self.config_file_xdg_path = os.path.abspath(
+                os.path.join(
+                    xdg_config_home,
+                    __main__.__packagename__,
+                    self.config_file_name,
+                   ),
+            )
+                
         # Name of the Tartube database file (storing media data objects). The
         #   database file is always found in self.data_dir
         self.db_file_name = __main__.__packagename__ + '.db'
@@ -506,12 +504,12 @@ class TartubeApp(Gtk.Application):
         self.operation_save_flag = True
         # Flag set to True if a dialogue window should be shown at the end of
         #   each download/update/refresh operation
-        self.operation_dialogue_flag = True
+        self.operation_dialogue_flag = True        
         # Flag set to True if self.update_video_from_filesystem() should get
         #   the video duration, if not already known, using the moviepy.editor
-        #   module (which may be slow)
+        #   module (an optional dependency)
         self.use_module_moviepy_flag = True
-
+            
         # Flag set to True if dialogue windows for adding videos, channels and
         #   playlists should copy the contents of the system clipboard
         self.dialogue_copy_clipboard_flag = True
@@ -583,6 +581,20 @@ class TartubeApp(Gtk.Application):
         #   1-999
         self.match_ignore_chars = self.match_default_chars
 
+        # Automatic video deletion. Applies only to downloaded videos (not to
+        #   checked videos)
+        # Flag set to True if videos should be deleted after a certain time
+        self.auto_delete_flag = False
+        # Flag set to True if videos are automatically deleted after a certain
+        #   time, but only if they have been watched (media.Video.dl_flag is
+        #   True, media.Video.new_flag is False; ignored if
+        #   self.auto_delete_old_flag is False)
+        self.auto_delete_watched_flag = False
+        # Videos are automatically deleted after this many days (must be an
+        #   integer, minimum value 1; ignored if self.auto_delete_old_flag is
+        #   False)
+        self.auto_delete_days = 30
+        
         # How much information to show in the Video Index. False to show
         #   minimal video stats, True to show full video stats
         self.complex_index_flag = False
@@ -663,9 +675,19 @@ class TartubeApp(Gtk.Application):
         export_db_menu_action.connect('activate', self.on_menu_export_db)
         self.add_action(export_db_menu_action)
 
-        import_db_menu_action = Gio.SimpleAction.new('import_db_menu', None)
-        import_db_menu_action.connect('activate', self.on_menu_import_db)
-        self.add_action(import_db_menu_action)
+        import_json_menu_action = Gio.SimpleAction.new(
+            'import_json_menu',
+            None,
+        )
+        import_json_menu_action.connect('activate', self.on_menu_import_json)
+        self.add_action(import_json_menu_action)
+
+        import_text_menu_action = Gio.SimpleAction.new(
+            'import_text_menu',
+            None,
+        )
+        import_text_menu_action.connect('activate', self.on_menu_import_plain_text)
+        self.add_action(import_text_menu_action)
 
         switch_view_menu_action = Gio.SimpleAction.new(
             'switch_view_menu',
@@ -726,7 +748,11 @@ class TartubeApp(Gtk.Application):
         about_menu_action = Gio.SimpleAction.new('about_menu', None)
         about_menu_action.connect('activate', self.on_menu_about)
         self.add_action(about_menu_action)
-
+        
+        go_website_menu_action = Gio.SimpleAction.new('go_website_menu', None)
+        go_website_menu_action.connect('activate', self.on_menu_go_website)
+        self.add_action(go_website_menu_action)
+        
         # Main toolbar actions
         # --------------------
 
@@ -999,23 +1025,11 @@ class TartubeApp(Gtk.Application):
             self.main_win_obj,
         )
 
-        # Delete Tartube's config file and data directory, if the debugging
-        #   flag is set
-        if self.debug_delete_data_flag:
-            if os.path.isfile(self.config_file_path):
-                os.remove(self.config_file_path)
-
-            if os.path.isfile(self.config_file_old_path):
-                os.remove(self.config_file_old_path)
-
-            if os.path.isdir(self.data_dir):
-                shutil.rmtree(self.data_dir)
-
         # Give mainapp.TartubeApp IVs their initial values
         self.general_options_obj = options.OptionsManager()
 
         # Set youtube-dl IVs
-        if __main__.__disable_ytdl_update_flag__:
+        if __main__.__debian_install_flag__:
             self.ytdl_bin = 'youtube-dl'
             self.ytdl_path_default = os.path.abspath(
                 os.path.join(os.sep, 'usr', 'bin', self.ytdl_bin),
@@ -1103,43 +1117,70 @@ class TartubeApp(Gtk.Application):
             ]
             self.ytdl_update_current = 'Update using pip3 (recommended)'
 
-        # If the config file exists, load it (from either the default or the
-        #   pre v1.1.014 location). If not, create it
+        # If the config file exists, load it. If not, create it
         new_mswin_flag = False
-        if os.path.isfile(self.config_file_path) \
-        or os.path.isfile(self.config_file_old_path):
+        
+        if (
+            not __main__.__debian_install_flag__
+            and os.path.isfile(self.config_file_path)
+        ) or (
+            __main__.__debian_install_flag__
+            and self.config_file_xdg_path is not None
+            and os.path.isfile(self.config_file_xdg_path)
+        ):
             self.load_config()
+            
         elif self.debug_no_dialogue_flag:
             self.save_config()
+
         else:
 
-            # (Need to show an extra prompt at the end of this function)
+            # New Tartube installation
             if os.name == 'nt':
+
+                # On MS Windows, Cygwin creates a Tartube data directory at
+                #   C:\msys64\home\USERNAME\tartube-data, which is not very
+                #   convenient. Force the user to nominate the directory they
+                #   want
                 new_mswin_flag = True
-
-            # On MS Windows, Cygwin creates a Tartube data directory at
-            #   C:\msys64\home\USERNAME\tartube-data, which is not very
-            #   convenient
-            # Even on Linux/*BSD, the user might want to choose their own data
-            #   directory
-            # Therefore, on new installations, immediately ask the user to
-            #   choose a location for the data directory
-            dialogue_win = mainwin.SetDirectoryDialogue(
-                self.main_win_obj,
-                self.data_dir,
-            )
-
-            response = dialogue_win.run()
-
-            # Retrieve user choices from the dialogue window, before destroying
-            #   it
-            custom_flag = False
-            if dialogue_win.button2.get_active():
                 custom_flag = True
+                
+                dialogue_win = self.dialogue_manager_obj.show_msg_dialogue(
+                    'Click OK to create a folder in which\n' \
+                    + utils.upper_case_first(__main__.__packagename__) \
+                    + ' can store its videos\n\nIf you have used ' \
+                    + utils.upper_case_first(__main__.__packagename__) \
+                    + ' before,\nyou can select an existing folder\ninstead of'
+                    + ' creating a new one',
+                   'info',
+                   'ok',
+                   self.main_win_obj,
+                )
 
-            dialogue_win.destroy()
+                dialogue_win.set_modal(True)
 
-            if response == Gtk.ResponseType.OK and custom_flag:
+            else:
+
+                # On Linux/BSD, offer the user a choice between using the
+                #   default data directory specified by self.data_dir, or
+                #   specifying their own data directory                
+                dialogue_win = mainwin.SetDirectoryDialogue(
+                    self.main_win_obj,
+                    self.data_dir,
+                )
+
+                response = dialogue_win.run()
+
+                # Retrieve user choices from the dialogue window, before
+                #   destroying it
+                custom_flag = False
+                if response == Gtk.ResponseType.OK \
+                and dialogue_win.button2.get_active():
+                    custom_flag = True
+
+                dialogue_win.destroy()
+
+            if custom_flag:
 
                 if os.name == 'nt':
                     folder = 'folder'
@@ -1355,7 +1396,7 @@ class TartubeApp(Gtk.Application):
             currently assigned thus:
 
             100-199: mainapp.py     (in use: 101-128)
-            200-299: mainwin.py     (in use: 201-235)
+            200-299: mainwin.py     (in use: 201-236)
             300-399: downloads.py   (in use: 301-304)
             400-499: config.py      (in use: 401-404)
 
@@ -1411,14 +1452,13 @@ class TartubeApp(Gtk.Application):
         if DEBUG_FUNC_FLAG:
             print('ap 1012 load_config')
 
-        # Before v1.1.014, the Tartube config file was stored in
-        #   self.script_parent_dir; it is now stored in
-        #   $XDG_CONFIG_HOME/tartube
-        config_file_path = self.config_file_path
-        if not os.path.isfile(config_file_path):
-
-            # Try the old location        
-            config_file_path = self.config_file_old_path
+        # The config file can be stored at one of two locations, depending on
+        #   the value of __main__.__debian_install_flag__
+        if __main__.__debian_install_flag__ \
+        and self.config_file_xdg_path is not None:
+            config_file_path = self.config_file_xdg_path
+        else:
+            config_file_path = self.config_file_path
 
         # Sanity check
         if self.current_manager_obj \
@@ -1482,21 +1522,6 @@ class TartubeApp(Gtk.Application):
                 + ' config file is invalid',
             )
 
-        # If the config file was loaded from the pre-v1.1.014 location, move it
-        if config_file_path == self.config_file_old_path:
-
-            destination_dir = os.path.abspath(
-                os.path.join(
-                    xdg_config_home,
-                    __main__.__packagename__,
-                ),
-            )
-                    
-            if not os.path.isdir(destination_dir):
-                os.makedirs(destination_dir)
-                        
-            shutil.move(self.config_file_old_path, self.config_file_path)
-
         # Set IVs to their new values
         if version >= 5024:     # v0.5.024
             self.toolbar_squeeze_flag = json_dict['toolbar_squeeze_flag']
@@ -1551,6 +1576,7 @@ class TartubeApp(Gtk.Application):
         = json_dict['operation_auto_update_flag']
         self.operation_save_flag = json_dict['operation_save_flag']
         self.operation_dialogue_flag = json_dict['operation_dialogue_flag']
+        
         self.use_module_moviepy_flag = json_dict['use_module_moviepy_flag']
 #       # Removed v0.5.003
 #        self.use_module_validators_flag \
@@ -1590,6 +1616,12 @@ class TartubeApp(Gtk.Application):
         self.match_method = json_dict['match_method']
         self.match_first_chars = json_dict['match_first_chars']
         self.match_ignore_chars = json_dict['match_ignore_chars']
+        
+        if version >= 1001029:  # v1.1.029
+            self.auto_delete_flag = json_dict['auto_delete_flag']
+            self.auto_delete_watched_flag \
+            = json_dict['auto_delete_watched_flag']
+            self.auto_delete_days = json_dict['auto_delete_days']
 
         self.complex_index_flag = json_dict['complex_index_flag']
         if version >= 3019:  # v0.3.019
@@ -1610,7 +1642,15 @@ class TartubeApp(Gtk.Application):
 
         if DEBUG_FUNC_FLAG:
             print('ap 1117 save_config')
-        
+
+        # The config file can be stored at one of two locations, depending on
+        #   the value of __main__.__debian_install_flag__
+        if __main__.__debian_install_flag__ \
+        and self.config_file_xdg_path is not None:
+            config_file_path = self.config_file_xdg_path
+        else:
+            config_file_path = self.config_file_path
+                    
         # Sanity check
         if self.current_manager_obj or self.disable_load_save_flag:
             return
@@ -1677,6 +1717,10 @@ class TartubeApp(Gtk.Application):
             'match_method': self.match_method,
             'match_first_chars': self.match_first_chars,
             'match_ignore_chars': self.match_ignore_chars,
+            
+            'auto_delete_flag': self.auto_delete_flag,
+            'auto_delete_watched_flag': self.auto_delete_watched_flag,
+            'auto_delete_days': self.auto_delete_days,
 
             'complex_index_flag': self.complex_index_flag,
             'catalogue_mode': self.catalogue_mode,
@@ -1685,7 +1729,7 @@ class TartubeApp(Gtk.Application):
 
         # Try to save the file
         try:
-            with open(self.config_file_path, 'w') as outfile:
+            with open(config_file_path, 'w') as outfile:
                 json.dump(json_dict, outfile, indent=4)
 
         except:
@@ -1788,11 +1832,14 @@ class TartubeApp(Gtk.Application):
         self.fixed_misc_folder = load_dict['fixed_misc_folder']
         self.fixed_temp_folder = load_dict['fixed_temp_folder']
 
+        # Update the loaded data for this version of Tartube
+        self.update_db(version)
+
         # Empty any temporary folders
         self.delete_temp_folders()
 
-        # Update the loaded data for this version of Tartube
-        self.update_db(version)
+        # Auto-delete old downloaded videos
+        self.auto_delete_old_videos()
 
         # If the debugging flag is set, hide all fixed (system) folders
         if self.debug_hide_folders_flag:
@@ -2016,6 +2063,44 @@ class TartubeApp(Gtk.Application):
                             media_data_obj.nickname = json_dict['title']
 
 
+        if version < 1001031:  # v1.1.031
+
+            # This version adds the ability to disable checking/downloading for
+            #   media data objects
+            for dbid in self.media_name_dict.values():
+                media_data_obj = self.media_reg_dict[dbid]
+                media_data_obj.dl_disable_flag = False            
+
+        if version < 1001032:  # v1.1.032
+
+            # This version adds video archiving. Archived videos cannot be
+            #   auto-deleted
+            for media_data_obj in self.media_reg_dict.values():
+                if isinstance(media_data_obj, media.Video):
+                    media_data_obj.archive_flag = False  
+
+        if version < 1001037:  # v1.1.037
+
+            # This version adds alternative destination directories for a
+            #   channel's/playlist's/folder's videos, thumbnails (etc)
+            for dbid in self.media_name_dict.values():
+                media_data_obj = self.media_reg_dict[dbid]
+                media_data_obj.master_dbid = media_data_obj.dbid
+                media_data_obj.slave_dbid_list = []
+
+
+        if version < 1001045:  # v1.1.045
+
+            # This version adds a new option to options.OptionsManager
+            options_obj_list = [self.general_options_obj]
+            for media_data_obj in self.media_reg_dict.values():
+                if media_data_obj.options_obj is not None:
+                    options_obj_list.append(media_data_obj.options_obj)
+
+            for options_obj in options_obj_list:
+                options_obj.options_dict['use_fixed_folder'] = None
+                                                     
+
     def save_db(self):
 
         """Called by self.start(), .stop(), .switch_db(),
@@ -2429,7 +2514,7 @@ class TartubeApp(Gtk.Application):
             and media_data_obj.temp_flag:
 
                 # Delete all child objects
-                for child_obj in media_data_obj.child_list:
+                for child_obj in list(media_data_obj.child_list.copy()):
                     if isinstance(child_obj, media.Video):
                         self.delete_video(child_obj)
                     else:
@@ -2443,6 +2528,43 @@ class TartubeApp(Gtk.Application):
                 os.makedirs(dir_path)
 
 
+    def auto_delete_old_videos(self):
+
+        """Called by self.load_db().
+
+        After loading the Tartube database, auto-delete any old downloaded
+        videos (if auto-deletion is enabled)
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 1571 auto_delete_old_videos')
+            
+        if not self.auto_delete_flag:
+            return
+
+        # Calculate the system time before which any downloaded videos can be
+        #   deleted
+        time_limit = int(time.time()) - (self.auto_delete_days * 24 * 60 * 60)
+        
+        # Import a list of media data objects (as self.media_reg_dict will be
+        #   modified during this operation)
+        media_list = list(self.media_reg_dict.values())
+
+        # Auto-delete any videos as required
+        for media_data_obj in media_list:
+
+            if isinstance(media_data_obj, media.Video) \
+            and media_data_obj.dl_flag \
+            and not media_data_obj.archive_flag \
+            and media_data_obj.receive_time < time_limit \
+            and (
+                not self.auto_delete_watched_flag \
+                or not media_data_obj.new_flag
+            ):
+                # Ddelete this video
+                self.delete_video(media_data_obj, True, True, True)
+
+        
     def convert_version(self, version):
 
         """Can be called by anything, but mostly called by self.load_config()
@@ -2717,7 +2839,7 @@ class TartubeApp(Gtk.Application):
 
             return
 
-        elif __main__.__disable_ytdl_update_flag__:
+        elif __main__.__debian_install_flag__:
             # Update operation is disabled in the Debian package. It should not
             #   be possible to call this function, but we'll show an error
             #   message anyway
@@ -2982,7 +3104,25 @@ class TartubeApp(Gtk.Application):
             if video_obj is None:
 
                 # Create a new media data object for the video
-                video_obj = self.add_video(media_data_obj, None, no_sort_flag)
+                override_name = download_item_obj.options_manager_obj.options_dict['use_fixed_folder']
+                if override_name is not None \
+                and override_name in self.media_name_dict:                
+
+                    other_dbid = self.media_name_dict[override_name]
+                    other_parent_obj = self.media_reg_dict[other_dbid]
+
+                    video_obj = self.add_video(
+                        other_parent_obj,
+                        None,
+                        no_sort_flag,
+                    )
+
+                else:
+                    video_obj = self.add_video(
+                        media_data_obj,
+                        None,
+                        no_sort_flag,
+                    )
 
                 # Since we have them to hand, set the video's file path IVs
                 #   immediately
@@ -3155,6 +3295,52 @@ class TartubeApp(Gtk.Application):
         self.mark_video_downloaded(video_obj, True)
 
 
+    def announce_video_clone(self, video_obj):
+
+        """Called by downloads.VideoDownloader.confirm_old_video().
+
+        This is a modified version of self.update_video_when_file_found(),
+        called when a channel/playlist/folder is using an alternative
+        download destination for its videos (in which case,
+        self.update_video_when_file_found() can't be called).
+
+        Args:
+
+            video_obj (media.Video): The video which already exists on the
+                user's filesystem (in the alternative download destination)
+                
+        """
+        
+        video_path = os.path.abspath(
+            os.path.join(
+                video_obj.file_dir,
+                video_obj.file_name + video_obj.file_ext,
+            )
+        )
+
+        # Only set the .name IV if the video is currently unnamed
+        if video_obj.name == self.default_video_name:
+            video_obj.set_name(video_obj.file_name)
+            # (The video's title, stored in the .nickname IV, will be updated
+            #   from the JSON data in a momemnt)
+            video_obj.set_nickname(video_obj.file_name)
+
+        # Set the file size
+        video_obj.set_file_size(os.path.getsize(video_path))
+
+        # If the JSON file was downloaded, we can extract video statistics from
+        #   it
+        self.update_video_from_json(video_obj)
+
+        # For any of those statistics that haven't been set (because the JSON
+        #   file was missing or didn't contain the right statistics), set them
+        #   directly        
+        self.update_video_from_filesystem(video_obj, video_path)
+
+        # Mark the video as (fully) downloaded (and update everything else)
+        self.mark_video_downloaded(video_obj, True)
+        
+                    
     def update_video_from_json(self, video_obj):
 
         """Called by self.update_video_when_file_found() and
@@ -3811,12 +3997,12 @@ class TartubeApp(Gtk.Application):
     # (Delete media data objects)
 
 
-    def delete_video(self, video_obj, no_update_index_flag=False,
-    delete_files_flag=False):
+    def delete_video(self, video_obj, delete_files_flag=False,
+    no_update_index_flag=False, no_update_catalogue_flag=False):
 
-        """Called by self.delete_temp_folders(), .delete_container(),
-        mainwin.MainWin.video_catalogue_popup_menu() and a callback in
-        mainwin.MainWin.on_video_catalogue_delete_video().
+        """Called by self.delete_temp_folders(), .delete_old_videos(),
+        .delete_container(), mainwin.MainWin.video_catalogue_popup_menu() and a
+        callback in mainwin.MainWin.on_video_catalogue_delete_video().
 
         Deletes a video object from the media registry.
 
@@ -3824,15 +4010,19 @@ class TartubeApp(Gtk.Application):
 
             video_obj (media.Video): The media.Video object to delete
 
-            no_update_index_flag (True or False): True when called by
-                self.delete_container(), in which case the Video Index is not
-                updated (because the calling function wants to do that)
-
             delete_files_flag (True or False): True when called by
                 mainwin.MainWin.on_video_catalogue_delete_video, in which case
                 the video and its associated files are deleted from the
                 filesystem
 
+            no_update_index_flag (True or False): True when called by
+                self.delete_old_videos() or self.delete_container(), in which
+                case the Video Index is not updated
+                
+            no_update_catalogue_flag (True or False): True when called by
+                self.delete_old_videos(), in which case the Video Catalogue is
+                not updated
+
         """
 
         if DEBUG_FUNC_FLAG:
@@ -3901,7 +4091,9 @@ class TartubeApp(Gtk.Application):
                 os.remove(json_path)
 
         # Remove the video from the catalogue, if present
-        self.main_win_obj.video_catalogue_delete_row(video_obj)
+        if not no_update_catalogue_flag:
+            self.main_win_obj.video_catalogue_delete_row(video_obj)
+        
         # Update rows in the Video Index, first checking that the parent
         #   container object is currently drawn there (which it might not be,
         #   if emptying temporary folders on startup)
@@ -4094,7 +4286,7 @@ class TartubeApp(Gtk.Application):
         copy_list = media_data_obj.child_list.copy()
         for child_obj in copy_list:
             if isinstance(child_obj, media.Video):
-                self.delete_video(child_obj, True)
+                self.delete_video(child_obj, False, True)
             else:
                 self.delete_container_complete(child_obj, False, True)
 
@@ -4105,6 +4297,9 @@ class TartubeApp(Gtk.Application):
             if media_data_obj.parent_obj:
                 media_data_obj.parent_obj.del_child(media_data_obj)
 
+            # Reset alternative download destinations
+            media_data_obj.set_master_dbid(self, media_data_obj.dbid)
+
             # Remove the media data object from our IVs
             del self.media_reg_dict[media_data_obj.dbid]
             del self.media_name_dict[media_data_obj.name]
@@ -4300,6 +4495,8 @@ class TartubeApp(Gtk.Application):
 
                 # Update the video object's IVs
                 video_obj.set_dl_flag(False)
+                # (A video that is not downloaded cannot be marked archived)
+                video_obj.set_archive_flag(False)
                 # Update the parent container object
                 video_obj.parent_obj.dec_dl_count()
                 # Update private folders
@@ -4515,13 +4712,99 @@ class TartubeApp(Gtk.Application):
                 self.main_win_obj.video_index_delete_row(folder_obj)
 
 
+    def mark_container_archived(self, media_data_obj, flag,
+    only_child_videos_flag):
+
+        """Called by mainwin.MainWin.on_video_index_mark_archived() and
+        .on_video_index_mark_not_archived().
+
+        Marks any descedant videos as archived.
+
+        Args:
+
+            media_data_obj (media.Channel, media.Playlist or media.Folder):
+                The container object to update
+
+            flag (True or False): True to mark as archived, False to mark as
+                not archived
+
+            only_child_videos_flag (True or False): Set to True if only child
+                video objects should be marked; False if the container object
+                and all its descendants should be marked
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 3483 mark_container_archived')
+
+        if isinstance(media_data_obj, media.Video):
+            return self.system_error(
+                121,
+                'Mark container as archived request failed sanity check',
+            )
+
+        # Special arrangements for private folders
+        if media_data_obj == self.fixed_all_folder:
+
+            # Check every video
+            for other_obj in list(self.media_reg_dict.values()):
+
+                if isinstance(other_obj, media.Video) and other_obj.dl_flag:
+                    other_obj.set_archive_flag(flag)
+
+        elif media_data_obj == self.fixed_new_folder:
+
+            # Check videos in this folder
+            for other_obj in self.fixed_new_folder.child_list:
+
+                if isinstance(other_obj, media.Video) and other_obj.dl_flag \
+                and other_obj.new_flag:
+                    other_obj.set_archive_flag(flag)
+
+        elif not flag and media_data_obj == self.fixed_fav_folder:
+
+            # Check videos in this folder
+            for other_obj in self.fixed_fav_folder.child_list:
+
+                if isinstance(other_obj, media.Video) and other_obj.dl_flag \
+                and other_obj.fav_flag:
+                    other_obj.set_archive_flag(flag)
+
+        elif only_child_videos_flag:
+
+            # Check videos in this channel/playlist/folder
+            for other_obj in media_data_obj.child_list:
+
+                if isinstance(other_obj, media.Video):
+                    other_obj.set_archive_flag(flag)
+
+        else:
+
+            # Check videos in this channel/playlist/folder, and in any
+            #   descendant channels/playlists/folders
+            for other_obj in media_data_obj.compile_all_videos( [] ):
+
+                if isinstance(other_obj, media.Video) and other_obj.dl_flag:
+                    other_obj.set_archive_flag(flag)
+
+        # In all cases, update the row on the Video Index 
+        self.main_win_obj.video_index_update_row_icon(media_data_obj)
+        self.main_win_obj.video_index_update_row_text(media_data_obj)
+        # If this container is the one visible in the Video Catalogue, redraw
+        #   the Video Catalogue
+        if self.main_win_obj.video_index_current == media_data_obj.name:
+            self.main_win_obj.video_catalogue_redraw_all(
+                self.main_win_obj.video_index_current,
+            )
+            
+
     def mark_container_favourite(self, media_data_obj, flag,
     only_child_videos_flag):
 
         """Called by mainwin.MainWin.on_video_index_mark_favourite() and
         .on_video_index_mark_not_favourite().
 
-        Mark this channel, playlist or folder as favourite. Also mark any
+        Marks this channel, playlist or folder as favourite. Also marks any
         descendant videos as favourite (but not descendent channels, playlists
         or folders).
 
@@ -5054,24 +5337,29 @@ class TartubeApp(Gtk.Application):
         )
 
 
-    def import_into_db(self):
+    def import_into_db(self, json_flag):
 
-        """Called by self.on_menu_import_db() or by any other function.
+        """Called by self.on_menu_import_json() or by any other function.
 
-        Imports the contents of an export file generated by a call to
-        self.export_from_db().
-
-        (Only imports JSON files; doesn't import the plain text files generated
-        by that function.)
+        Imports the contents of a JSON export file or a plain text export file
+        generated by a call to self.export_from_db().
 
         After prompting the user, creates new media.Video, media.Channel,
         media.Playlist and/or media.Folder objects. Checks for duplicates and
         handles them appropriately.
 
-        The export file contains a dictionary, 'db_dict', containing further
+        A JSON export file contains a dictionary, 'db_dict', containing further
         dictionaries, 'mini_dict', whose formats are described in the comments
         in self.export_from_db().
 
+        A plain text export file contains lines in groups of three, in the
+        format described in the comments in self.export_from_db().
+
+        Args:
+
+            json_flag (bool): True if a JSON export file should be imported,
+                False if a plain text export file should be imported
+
         """
 
         if DEBUG_FUNC_FLAG:
@@ -5099,42 +5387,62 @@ class TartubeApp(Gtk.Application):
             return
 
         # Try to load the export file
-        try:
-            with open(file_path) as infile:
-                json_dict = json.load(infile)
+        if not json_flag:
 
-        except:
+            text = self.file_manager_obj.load_text(file_path)
+            if text is None:
+                return self.dialogue_manager_obj.show_msg_dialogue(
+                    'Failed to load the database export file',
+                    'error',
+                    'ok',
+                )
+
+            # Parse the text file, creating a db_dict in the form described in
+            #   the comments in self.export_from_db()
+            db_dict = self.parse_text_import(text)
+
+        else:
+
+            json_dict = self.file_manager_obj.load_json(file_path)
+            if not json_dict:
+                return self.dialogue_manager_obj.show_msg_dialogue(
+                    'Failed to load the database export file',
+                    'error',
+                    'ok',
+                )
+
+            # Do some basic checks on the loaded data
+            # (At the moment, JSON export files are compatible with all
+            #   versions of Tartube after v1.0.0; this may change in future)        
+            if not json_dict \
+            or not 'script_name' in json_dict \
+            or not 'script_version' in json_dict \
+            or not 'save_date' in json_dict \
+            or not 'save_time' in json_dict \
+            or not 'file_type' in json_dict \
+            or json_dict['script_name'] != __main__.__packagename__ \
+            or json_dict['file_type'] != 'db_export':
+                return self.dialogue_manager_obj.show_msg_dialogue(
+                    'The database export file is invalid',
+                    'error',
+                    'ok',
+                )
+
+            # Retrieve the database data itself. db_dict is in the form
+            #   described in the comments in self.export_from_db()
+            db_dict = json_dict['db_dict']
+
+        if not db_dict:
             return self.dialogue_manager_obj.show_msg_dialogue(
-                'Failed to load the database export file',
+                'The database export file\nis invalid (or empty)',
                 'error',
                 'ok',
             )
-
-        # Do some basic checks on the loaded data
-        if not json_dict \
-        or not 'script_name' in json_dict \
-        or not 'script_version' in json_dict \
-        or not 'save_date' in json_dict \
-        or not 'save_time' in json_dict \
-        or not 'file_type' in json_dict \
-        or json_dict['script_name'] != __main__.__packagename__ \
-        or json_dict['file_type'] != 'db_export':
-            return self.dialogue_manager_obj.show_msg_dialogue(
-                'The database export file is invalid',
-                'error',
-                'ok',
-            )
-
-        # (At the moment, export files are compatible with all versions of
-        #   Tartube after v1.0.0; this may change in future)
-
+                                
         # Prompt the user to allow them to select which videos/channels/
         #   playlists/folders to actually import, and how to deal with
         #   duplicate channels/playlists/folders
-        dialogue_win = mainwin.ImportDialogue(
-            self.main_win_obj,
-            json_dict['db_dict'],
-        )
+        dialogue_win = mainwin.ImportDialogue(self.main_win_obj, db_dict)
         response = dialogue_win.run()
 
         # Retrieve user choices from the dialogue window, before destroying the
@@ -5156,7 +5464,7 @@ class TartubeApp(Gtk.Application):
         #   any duplicates
         (video_count, channel_count, playlist_count, folder_count) \
         = self.process_import(
-            json_dict['db_dict'],   # The imported 'db_dict'
+            db_dict,                # The imported data
             flat_db_dict,           # The flattened version of that dictionary
             None,                   # No parent 'mini_dict' yet
             import_videos_flag,
@@ -5170,7 +5478,7 @@ class TartubeApp(Gtk.Application):
         if not video_count and not channel_count and not playlist_count \
         and not folder_count:
             self.dialogue_manager_obj.show_msg_dialogue(
-                'Nothing was imported from the database export file',
+                'Nothing was imported from\nthe database export file',
                 'error',
                 'ok',
             )
@@ -5193,6 +5501,104 @@ class TartubeApp(Gtk.Application):
             self.dialogue_manager_obj.show_msg_dialogue(msg, 'info', 'ok')
 
 
+    def parse_text_import(self, text):
+
+        """Called by self.import_into_db().
+
+        Given the contents of a plain text database export, which has been
+        loaded into memory, convert the contents into the db_dict format
+        described in the comments in self.export_from_db(), as if a JSON
+        database export had been loaded.
+
+        The text file contains lines, in groups of three, in the following
+        format:
+
+            @type
+            <name>
+            <url>
+            
+        ...where '@type' is one of '@video', '@channel' or '@playlist' (the
+        folder structure is never preserved in a plain text export).
+
+        A video belongs to the channel/playlist above it.
+
+        Args:
+
+            text (str): The contents of the loaded plain text file
+
+        Returns:
+
+            db_dict (dict): The converted data in the form described in the
+                comments in self.export_from_db()
+                
+        """
+
+        db_dict = {}
+        dbid = 0
+        last_container_mini_dict = None
+        
+        # Split text into separate lines
+        line_list = text.split('\n')
+        
+        # Remove all empty lines (including those containing only whitespace)
+        mod_list = []
+        for line in line_list:
+            if re.match('\S', line):
+                mod_list.append(line)
+
+        # Extract each group of three lines, and check they are valid
+        # If a group of three is invalid (or if we reach the end of the file
+        #   in the middle of a group of 3), ignore that group and any
+        #   subsequent groups, and just use the data already extracted
+        while len(mod_list) > 2:
+            
+            media_type = mod_list[0]
+            name = mod_list[1]
+            source = mod_list[2]
+
+            mod_list = mod_list[3:]
+            
+            if media_type is None \
+            or (
+                media_type != '@video' and media_type != '@channel' \
+                and media_type != '@playlist'
+            ) \
+            or name is None or name == '' \
+            or source is None or not utils.check_url(source):
+                break
+
+            # A valid group of three; add an entry to db_dict using a fake dbid
+            dbid += 1
+
+            mini_dict = {
+                'type': None,
+                'dbid': dbid,
+                'name': name,
+                'nickname': name,
+                'source': source,
+                'db_dict': {},
+            }
+
+            if media_type == '@video':
+                mini_dict['type'] = 'video'
+                # A video belongs to the previous channel or playlist (if any)
+                if last_container_mini_dict is not None:
+                    last_container_mini_dict['db_dict'][dbid] = mini_dict
+
+            elif media_type == '@channel':
+                mini_dict['type'] = 'channel'
+                last_container_mini_dict = mini_dict
+                
+            else:
+                mini_dict['type'] = 'playlist'
+                last_container_mini_dict = mini_dict
+
+            db_dict[dbid] = mini_dict
+
+        # Procedure complete
+        return db_dict
+
+        
     def process_import(self, db_dict, flat_db_dict, parent_obj,
     import_videos_flag, merge_duplicates_flag, video_count, channel_count,
     playlist_count, folder_count):
@@ -5265,6 +5671,8 @@ class TartubeApp(Gtk.Application):
         #   self.media_reg_dict)
         for dbid in db_dict.keys():
 
+            media_data_obj = None
+
             # Each 'mini_dict' contains details for a single video/channel/
             #   playlist/folder
             mini_dict = db_dict[dbid]
@@ -5317,6 +5725,10 @@ class TartubeApp(Gtk.Application):
                             mini_dict['name'],
                         )
 
+                        mini_dict['nickname'] = self.rename_imported_container(
+                            mini_dict['nickname'],
+                        )
+
                     else:
 
                         # Use the existing channel/playlist/folder of the same
@@ -5324,48 +5736,43 @@ class TartubeApp(Gtk.Application):
                         old_dbid = self.media_name_dict[mini_dict['name']]
                         media_data_obj = self.media_reg_dict[old_dbid]
 
-                else:
-
-                    # Import the channel/playlist/folder
-                    media_data_obj = None
-
-                    if mini_dict['type'] == 'channel':
-                        media_data_obj = self.add_channel(
-                            mini_dict['name'],
-                            parent_obj,
-                            mini_dict['source'],
-                        )
-
-                        if media_data_obj:
-                            channel_count += 1
-
-                    elif mini_dict['type'] == 'playlist':
-                        media_data_obj = self.add_playlist(
-                            mini_dict['name'],
-                            parent_obj,
-                            mini_dict['source'],
-                        )
-
-                        if media_data_obj:
-                            playlist_count += 1
-
-                    elif mini_dict['type'] == 'folder':
-                        media_data_obj = self.add_folder(
-                            mini_dict['name'],
-                            parent_obj,
-                        )
-
-                        if media_data_obj:
-                            folder_count += 1
+                # Import the channel/playlist/folder
+                if mini_dict['type'] == 'channel':
+                    media_data_obj = self.add_channel(
+                        mini_dict['name'],
+                        parent_obj,
+                        mini_dict['source'],
+                    )
 
                     if media_data_obj:
-                        media_data_obj.set_nickname(mini_dict['nickname'])
+                        channel_count += 1
+
+                elif mini_dict['type'] == 'playlist':
+                    media_data_obj = self.add_playlist(
+                        mini_dict['name'],
+                        parent_obj,
+                        mini_dict['source'],
+                    )
+
+                    if media_data_obj:
+                        playlist_count += 1
+
+                elif mini_dict['type'] == 'folder':
+                    media_data_obj = self.add_folder(
+                        mini_dict['name'],
+                        parent_obj,
+                    )
+
+                    if media_data_obj:
+                        folder_count += 1
 
                 # If the channel/playlist/folder was successfully imported,
-                #   update the Video Index, then deal with any children by
-                #   calling this function recursively
+                #   set its nickname, update the Video Index, then deal with
+                #   any children by calling this function recursively
                 if media_data_obj is not None:
 
+                    media_data_obj.set_nickname(mini_dict['nickname'])
+
                     self.main_win_obj.video_index_add_row(media_data_obj)
 
                     if mini_dict['db_dict']:
@@ -6254,11 +6661,11 @@ class TartubeApp(Gtk.Application):
         self.export_from_db( [] )
 
 
-    def on_menu_import_db(self, action, par):
+    def on_menu_go_website(self, action, par):
 
         """Called from a callback in self.do_startup().
 
-        Imports data into the Tartube database.
+        Opens the Tartube website.
 
         Args:
 
@@ -6269,9 +6676,50 @@ class TartubeApp(Gtk.Application):
         """
 
         if DEBUG_FUNC_FLAG:
-            print('ap 4159 on_menu_import_db')
+            print('ap 3782 on_menu_go_website')
 
-        self.import_into_db()
+        utils.open_file(__main__.__website__)
+
+
+    def on_menu_import_json(self, action, par):
+
+        """Called from a callback in self.do_startup().
+
+        Imports data into from a JSON export file into the Tartube database.
+
+        Args:
+
+            action (Gio.SimpleAction): Object generated by Gio
+
+            par (None): Ignored
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 4159 on_menu_import_json')
+
+        self.import_into_db(True)
+
+
+    def on_menu_import_plain_text(self, action, par):
+
+        """Called from a callback in self.do_startup().
+
+        Imports data into from a plain text export file into the Tartube
+        database.
+
+        Args:
+
+            action (Gio.SimpleAction): Object generated by Gio
+
+            par (None): Ignored
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 4160 on_menu_import_plain_text')
+
+        self.import_into_db(False)
 
 
     def on_menu_general_options(self, action, par):
@@ -6523,6 +6971,36 @@ class TartubeApp(Gtk.Application):
             self.apply_json_timeout_flag = True
 
 
+    def set_auto_delete_flag(self, flag):
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 4414 set_auto_delete_flag')
+
+        if not flag:
+            self.auto_delete_flag = False
+        else:
+            self.auto_delete_flag = True
+
+
+    def set_auto_delete_watched_flag(self, flag):
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 4415 set_auto_delete_watched_flag')
+
+        if not flag:
+            self.auto_delete_watched_flag = False
+        else:
+            self.auto_delete_watched_flag = True
+
+
+    def set_auto_delete_days(self, days):
+
+        if DEBUG_FUNC_FLAG:
+            print('ap 4415 set_auto_delete_days')
+
+        self.auto_delete_days = days
+
+
     def set_bandwidth_default(self, value):
 
         """Called by mainwin.MainWin.on_spinbutton2_changed().
diff --git a/tartube/mainwin.py b/tartube/mainwin.py
index 0a676c8..484566a 100755
--- a/tartube/mainwin.py
+++ b/tartube/mainwin.py
@@ -424,6 +424,14 @@ class MainWin(Gtk.ApplicationWindow):
                     )
                     self.icon_dict[key] = full_path
 
+                # (At the moment, the system preference window only uses one
+                #   flag, but more may be added later)
+                full_path = os.path.abspath(
+                    os.path.join(icon_dir_path, 'locale', 'flag_uk.png'),
+                )
+                self.icon_dict['flag_uk'] = full_path
+
+                # Now create the pixbufs themselves
                 for key in self.icon_dict:
                     full_path = self.icon_dict[key]
 
@@ -526,8 +534,7 @@ class MainWin(Gtk.ApplicationWindow):
         file_sub_menu.append(self.save_db_menu_item)
         self.save_db_menu_item.set_action_name('app.save_db_menu')
 
-        separator_item = Gtk.SeparatorMenuItem()
-        file_sub_menu.append(separator_item)
+        file_sub_menu.append(Gtk.SeparatorMenuItem())
 
         quit_menu_item = Gtk.MenuItem.new_with_mnemonic('_Quit')
         file_sub_menu.append(quit_menu_item)
@@ -583,8 +590,7 @@ class MainWin(Gtk.ApplicationWindow):
         media_sub_menu.append(add_folder_menu_item)
         add_folder_menu_item.set_action_name('app.add_folder_menu')
 
-        separator_item2 = Gtk.SeparatorMenuItem()
-        media_sub_menu.append(separator_item2)
+        media_sub_menu.append(Gtk.SeparatorMenuItem())
 
         self.export_db_menu_item = Gtk.MenuItem.new_with_mnemonic(
             '_Export from database',
@@ -592,14 +598,27 @@ class MainWin(Gtk.ApplicationWindow):
         media_sub_menu.append(self.export_db_menu_item)
         self.export_db_menu_item.set_action_name('app.export_db_menu')
 
-        self.import_db_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Import into database',
-        )
-        media_sub_menu.append(self.import_db_menu_item)
-        self.import_db_menu_item.set_action_name('app.import_db_menu')
+        import_sub_menu = Gtk.Menu()
 
-        separator_item3 = Gtk.SeparatorMenuItem()
-        media_sub_menu.append(separator_item3)
+        import_json_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_JSON export file',
+        )
+        import_sub_menu.append(import_json_menu_item)
+        import_json_menu_item.set_action_name('app.import_json_menu')
+
+        import_text_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Plain _text export file',
+        )
+        import_sub_menu.append(import_text_menu_item)
+        import_text_menu_item.set_action_name('app.import_text_menu')
+        
+        self.import_db_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Import into database'        
+        )
+        self.import_db_menu_item.set_submenu(import_sub_menu)
+        media_sub_menu.append(self.import_db_menu_item)
+
+        media_sub_menu.append(Gtk.SeparatorMenuItem())
 
         switch_view_menu_item = \
         Gtk.MenuItem.new_with_mnemonic('_Switch between views')
@@ -613,8 +632,7 @@ class MainWin(Gtk.ApplicationWindow):
 
         if self.app_obj.debug_test_media_menu_flag:
 
-            separator_item4 = Gtk.SeparatorMenuItem()
-            media_sub_menu.append(separator_item4)
+            media_sub_menu.append(Gtk.SeparatorMenuItem())
 
             self.test_menu_item = Gtk.MenuItem.new_with_mnemonic(
                 '_Add test media',
@@ -644,8 +662,7 @@ class MainWin(Gtk.ApplicationWindow):
         self.stop_download_menu_item.set_sensitive(False)
         self.stop_download_menu_item.set_action_name('app.stop_download_menu')
 
-        separator_item5 = Gtk.SeparatorMenuItem()
-        ops_sub_menu.append(separator_item5)
+        ops_sub_menu.append(Gtk.SeparatorMenuItem())
 
         self.refresh_db_menu_item = Gtk.MenuItem.new_with_mnemonic(
             '_Refresh database',
@@ -653,15 +670,14 @@ class MainWin(Gtk.ApplicationWindow):
         ops_sub_menu.append(self.refresh_db_menu_item)
         self.refresh_db_menu_item.set_action_name('app.refresh_db_menu')
 
-        separator_item6 = Gtk.SeparatorMenuItem()
-        ops_sub_menu.append(separator_item6)
+        ops_sub_menu.append(Gtk.SeparatorMenuItem())
 
         self.update_ytdl_menu_item = Gtk.MenuItem.new_with_mnemonic(
             '_Update youtube-dl',
         )
         ops_sub_menu.append(self.update_ytdl_menu_item)
         self.update_ytdl_menu_item.set_action_name('app.update_ytdl_menu')
-        if __main__.__disable_ytdl_update_flag__:
+        if __main__.__debian_install_flag__:
             self.update_ytdl_menu_item.set_sensitive(False)
 
         # Help column
@@ -675,6 +691,10 @@ class MainWin(Gtk.ApplicationWindow):
         help_sub_menu.append(about_menu_item)
         about_menu_item.set_action_name('app.about_menu')
 
+        go_website_menu_item = Gtk.MenuItem.new_with_mnemonic('Go to _website')
+        help_sub_menu.append(go_website_menu_item)
+        go_website_menu_item.set_action_name('app.go_website_menu')
+
 
     def setup_main_toolbar(self):
 
@@ -1172,11 +1192,16 @@ class MainWin(Gtk.ApplicationWindow):
         self.progress_list_treeview = Gtk.TreeView()
         self.progress_list_frame.add(self.progress_list_treeview)
         self.progress_list_treeview.set_can_focus(False)
-
+        # (Detect right-clicks on the treeview)
+        self.progress_list_treeview.connect(
+            'button-press-event',
+            self.on_progress_list_right_click,
+        )
+                
         for i, column_title in enumerate(
             [
-                '', 'Source', 'Videos', 'Status', 'Incoming file', 'Ext',
-                'Size', '%', 'ETA', 'Speed',
+                'hide', '', 'Source', 'Videos', 'Status', 'Incoming file',
+                'Ext', 'Size', '%', 'ETA', 'Speed',
             ]
         ):
             if not column_title:
@@ -1187,6 +1212,7 @@ class MainWin(Gtk.ApplicationWindow):
                     pixbuf=i,
                 )
                 self.progress_list_treeview.append_column(column_pixbuf)
+                column_pixbuf.set_resizable(False)
 
             else:
                 renderer_text = Gtk.CellRendererText()
@@ -1196,13 +1222,18 @@ class MainWin(Gtk.ApplicationWindow):
                     text=i,
                 )
                 self.progress_list_treeview.append_column(column_text)
-
+                column_text.set_resizable(True)
+                column_text.set_min_width(20)
+                if column_title == 'hide':
+                    column_text.set_visible(False)
+                    
         self.progress_list_liststore = Gtk.ListStore(
+            int,
             GdkPixbuf.Pixbuf,
             str, str, str, str, str, str, str, str, str,
         )
         self.progress_list_treeview.set_model(self.progress_list_liststore)
-
+        
         # Lower half
         self.results_list_scrolled = Gtk.ScrolledWindow()
         self.progress_paned.add2(self.results_list_scrolled)
@@ -1218,10 +1249,15 @@ class MainWin(Gtk.ApplicationWindow):
         self.results_list_treeview = Gtk.TreeView()
         self.results_list_frame.add(self.results_list_treeview)
         self.results_list_treeview.set_can_focus(False)
-
+        # (Detect right-clicks on the treeview)
+        self.results_list_treeview.connect(
+            'button-press-event',
+            self.on_results_list_right_click,
+        )
+        
         for i, column_title in enumerate(
             [
-                '', 'New videos', 'Duration', 'Size', 'Date', 'File',
+                'hide', '', 'New videos', 'Duration', 'Size', 'Date', 'File',
                 '', 'Downloaded to',
             ]
         ):
@@ -1233,6 +1269,7 @@ class MainWin(Gtk.ApplicationWindow):
                     pixbuf=i,
                 )
                 self.results_list_treeview.append_column(column_pixbuf)
+                column_pixbuf.set_resizable(False)
 
             elif column_title == 'File':
                 renderer_toggle = Gtk.CellRendererToggle()
@@ -1242,6 +1279,7 @@ class MainWin(Gtk.ApplicationWindow):
                     active=i,
                 )
                 self.results_list_treeview.append_column(column_toggle)
+                column_toggle.set_resizable(False)
 
             else:
                 renderer_text = Gtk.CellRendererText()
@@ -1251,8 +1289,13 @@ class MainWin(Gtk.ApplicationWindow):
                     text=i,
                 )
                 self.results_list_treeview.append_column(column_text)
-
+                column_text.set_resizable(True)
+                column_text.set_min_width(20)
+                if column_title == 'hide':
+                    column_text.set_visible(False)
+                  
         self.results_list_liststore = Gtk.ListStore(
+            int,
             GdkPixbuf.Pixbuf,
             str, str, str, str,
             bool,
@@ -1401,7 +1444,7 @@ class MainWin(Gtk.ApplicationWindow):
         self.check_all_toolbutton.set_sensitive(flag)
         self.download_all_toolbutton.set_sensitive(flag)
 
-        if not __main__.__disable_ytdl_update_flag__:
+        if not __main__.__debian_install_flag__:
             self.update_ytdl_menu_item.set_sensitive(flag)
 
         # (The 'Save database' menu item must remain desensitised if file load/
@@ -1863,6 +1906,960 @@ class MainWin(Gtk.ApplicationWindow):
             return 0
 
 
+    # (Popup menu functions for main window widgets)
+
+
+    def video_index_popup_menu(self, event, name):
+
+        """Called by self.video_index_treeview_click_event().
+
+        When the user right-clicks on the Video Index, show a context-sensitive
+        popup menu.
+
+        Args:
+
+            event (Gdk.EventButton): The mouse click event
+
+            name (string): The name of the clicked media data object
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 2174 video_index_popup_menu')
+
+        # Find the right-clicked media data object (and a string to describe
+        #   its type)
+        dbid = self.app_obj.media_name_dict[name]
+        media_data_obj = self.app_obj.media_reg_dict[dbid]
+
+        if isinstance(media_data_obj, media.Channel):
+            media_type = 'channel'
+        elif isinstance(media_data_obj, media.Playlist):
+            media_type = 'playlist'
+        else:
+            media_type = 'folder'
+
+        # Set up the popup menu
+        popup_menu = Gtk.Menu()
+
+        # Check/download/refresh items
+        check_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Check ' + media_type,
+        )
+        check_menu_item.connect(
+            'activate',
+            self.on_video_index_check,
+            media_data_obj,
+        )
+        if self.app_obj.current_manager_obj \
+        or (
+            isinstance(media_data_obj, media.Folder) \
+            and media_data_obj.priv_flag
+        ):
+            check_menu_item.set_sensitive(False)
+        popup_menu.append(check_menu_item)
+
+        download_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Download ' + media_type,
+        )
+        download_menu_item.connect(
+            'activate',
+            self.on_video_index_download,
+            media_data_obj,
+        )
+        if self.app_obj.current_manager_obj \
+        or (
+            isinstance(media_data_obj, media.Folder) \
+            and media_data_obj.priv_flag
+        ):
+            download_menu_item.set_sensitive(False)
+        popup_menu.append(download_menu_item)
+
+        refresh_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Refresh ' + media_type,
+        )
+        refresh_menu_item.connect(
+            'activate',
+            self.on_video_index_refresh,
+            media_data_obj,
+        )
+        if self.app_obj.current_manager_obj \
+        or (
+            isinstance(media_data_obj, media.Folder) \
+            and media_data_obj.priv_flag
+        ):
+            refresh_menu_item.set_sensitive(False)
+        popup_menu.append(refresh_menu_item)
+
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+
+        # Contents
+        contents_submenu = Gtk.Menu()
+
+        if not isinstance(media_data_obj, media.Folder):
+
+            self.video_index_setup_contents_submenu(
+                contents_submenu,
+                media_data_obj,
+                False,
+            )
+
+        else:
+
+            # All contents
+            all_contents_submenu = Gtk.Menu()
+
+            self.video_index_setup_contents_submenu(
+                all_contents_submenu,
+                media_data_obj,
+                False,
+            )
+
+            # Separator
+            all_contents_submenu.append(Gtk.SeparatorMenuItem())
+
+            empty_folder_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                '_Empty folder',
+            )
+            empty_folder_menu_item.connect(
+                'activate',
+                self.on_video_index_empty_folder,
+                media_data_obj,
+            )
+            all_contents_submenu.append(empty_folder_menu_item)
+            if not media_data_obj.child_list or media_data_obj.priv_flag:
+                empty_folder_menu_item.set_sensitive(False)
+
+            all_contents_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                '_All contents',
+            )
+            all_contents_menu_item.set_submenu(all_contents_submenu)
+            contents_submenu.append(all_contents_menu_item)
+
+            # Just folder videos
+            just_videos_submenu = Gtk.Menu()
+
+            self.video_index_setup_contents_submenu(
+                just_videos_submenu,
+                media_data_obj,
+                True,
+            )
+
+            # Separator
+            just_videos_submenu.append(Gtk.SeparatorMenuItem())
+
+            empty_videos_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                '_Remove videos',
+            )
+            empty_videos_menu_item.connect(
+                'activate',
+                self.on_video_index_remove_videos,
+                media_data_obj,
+            )
+            just_videos_submenu.append(empty_videos_menu_item)
+            if not media_data_obj.child_list or media_data_obj.priv_flag:
+                empty_videos_menu_item.set_sensitive(False)
+
+            just_videos_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                '_Just folder videos',
+            )
+            just_videos_menu_item.set_submenu(just_videos_submenu)
+            contents_submenu.append(just_videos_menu_item)
+
+        contents_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            utils.upper_case_first(media_type) + ' co_ntents',
+        )
+        contents_menu_item.set_submenu(contents_submenu)
+        popup_menu.append(contents_menu_item)
+
+        # Actions
+        actions_submenu = Gtk.Menu()
+
+        move_top_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Move to _top level',
+        )
+        move_top_menu_item.connect(
+            'activate',
+            self.on_video_index_move_to_top,
+            media_data_obj,
+        )
+        actions_submenu.append(move_top_menu_item)
+        if not media_data_obj.parent_obj \
+        or self.app_obj.current_manager_obj:
+            move_top_menu_item.set_sensitive(False)
+
+        # Separator
+        actions_submenu.append(Gtk.SeparatorMenuItem())
+            
+        if isinstance(media_data_obj, media.Folder):
+
+            hide_folder_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                '_Hide folder',
+            )
+            hide_folder_menu_item.connect(
+                'activate',
+                self.on_video_index_hide_folder,
+                media_data_obj,
+            )
+            actions_submenu.append(hide_folder_menu_item)
+
+        set_nickname_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Set _nickname...',
+        )
+        set_nickname_menu_item.connect(
+            'activate',
+            self.on_video_index_set_nickname,
+            media_data_obj,
+        )
+        actions_submenu.append(set_nickname_menu_item)
+        if isinstance(media_data_obj, media.Folder) \
+        and media_data_obj.priv_flag:
+            set_nickname_menu_item.set_sensitive(False)
+
+        set_destination_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Set _download destination...',
+        )
+        set_destination_menu_item.connect(
+            'activate',
+            self.on_video_index_set_destination,
+            media_data_obj,
+        )
+        actions_submenu.append(set_destination_menu_item)
+        if isinstance(media_data_obj, media.Folder) \
+        and media_data_obj.fixed_flag:
+            set_destination_menu_item.set_sensitive(False)
+
+        # Separator
+        actions_submenu.append(Gtk.SeparatorMenuItem())
+        
+        export_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Export ' + media_type + '...',
+        )
+        export_menu_item.connect(
+            'activate',
+            self.on_video_index_export,
+            media_data_obj,
+        )
+        actions_submenu.append(export_menu_item)
+        if self.app_obj.current_manager_obj:
+            export_menu_item.set_sensitive(False)
+
+        actions_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            utils.upper_case_first(media_type) + ' _actions',
+        )
+        actions_menu_item.set_submenu(actions_submenu)
+        popup_menu.append(actions_menu_item)
+
+        # Apply/remove/edit download options, disable downloads
+        downloads_submenu = Gtk.Menu()
+
+        # (Desensitise these menu items, if an edit window is already open)
+        no_options_flag = False
+        for win_obj in self.config_win_list:
+            if isinstance(win_obj, config.OptionsEditWin) \
+            and media_data_obj.options_obj == win_obj.edit_obj:
+                no_options_flag = True
+                break
+
+        if not media_data_obj.options_obj:
+
+            apply_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'Apply download _options...',
+            )
+            apply_options_menu_item.connect(
+                'activate',
+                self.on_video_index_apply_options,
+                media_data_obj,
+            )
+            downloads_submenu.append(apply_options_menu_item)
+            if no_options_flag or self.app_obj.current_manager_obj \
+            or (
+                isinstance(media_data_obj, media.Folder)
+                and media_data_obj.priv_flag
+            ):
+                apply_options_menu_item.set_sensitive(False)
+
+        else:
+
+            remove_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'Remove download _options',
+            )
+            remove_options_menu_item.connect(
+                'activate',
+                self.on_video_index_remove_options,
+                media_data_obj,
+            )
+            downloads_submenu.append(remove_options_menu_item)
+            if no_options_flag or self.app_obj.current_manager_obj \
+            or (
+                isinstance(media_data_obj, media.Folder)
+                and media_data_obj.priv_flag
+            ):
+                remove_options_menu_item.set_sensitive(False)
+
+        edit_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Edit download options...',
+        )
+        edit_options_menu_item.connect(
+            'activate',
+            self.on_video_index_edit_options,
+            media_data_obj,
+        )
+        downloads_submenu.append(edit_options_menu_item)
+        if no_options_flag or self.app_obj.current_manager_obj \
+        or not media_data_obj.options_obj:
+            edit_options_menu_item.set_sensitive(False)
+
+        # Separator
+        downloads_submenu.append(Gtk.SeparatorMenuItem())
+
+        disable_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
+            'D_isable checking/downloading',
+        )
+        disable_menu_item.set_active(media_data_obj.dl_disable_flag)
+        disable_menu_item.connect(
+            'activate',
+            self.on_video_index_dl_disable,
+            media_data_obj,
+        )
+        downloads_submenu.append(disable_menu_item)
+        # (Widget sensitivity set below)
+            
+        enforce_check_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
+            '_Just disable downloading',
+        )
+        enforce_check_menu_item.set_active(media_data_obj.dl_sim_flag)
+        enforce_check_menu_item.connect(
+            'activate',
+            self.on_video_index_enforce_check,
+            media_data_obj,
+        )
+        downloads_submenu.append(enforce_check_menu_item)
+        if self.app_obj.current_manager_obj or media_data_obj.dl_disable_flag \
+        or (
+            isinstance(media_data_obj, media.Folder) \
+            and media_data_obj.fixed_flag
+        ):
+            enforce_check_menu_item.set_sensitive(False)
+
+        # (Widget sensitivity from above)
+        if self.app_obj.current_manager_obj \
+        or (
+            isinstance(media_data_obj, media.Folder) \
+            and media_data_obj.fixed_flag
+        ):
+            disable_menu_item.set_sensitive(False)
+            enforce_check_menu_item.set_sensitive(False)
+
+        downloads_menu_item = Gtk.MenuItem.new_with_mnemonic('_Downloads')
+        downloads_menu_item.set_submenu(downloads_submenu)
+        popup_menu.append(downloads_menu_item)
+            
+        # Filesystem
+        filesystem_submenu = Gtk.Menu()
+
+        show_location_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Show default location',
+        )
+        show_location_menu_item.connect(
+            'activate',
+            self.on_video_index_show_location,
+            media_data_obj,
+        )
+        filesystem_submenu.append(show_location_menu_item)
+        if isinstance(media_data_obj, media.Folder) \
+        and media_data_obj.priv_flag:
+            show_location_menu_item.set_sensitive(False)
+
+        rename_location_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Rename default location...',
+        )
+        rename_location_menu_item.connect(
+            'activate',
+            self.on_video_index_rename_location,
+            media_data_obj,
+        )
+        filesystem_submenu.append(rename_location_menu_item)
+        if self.app_obj.current_manager_obj or self.config_win_list \
+        or (
+            isinstance(media_data_obj, media.Folder) \
+            and media_data_obj.fixed_flag
+        ):
+            rename_location_menu_item.set_sensitive(False)
+
+        # Separator
+        filesystem_submenu.append(Gtk.SeparatorMenuItem())
+
+        show_destination_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Show download destination',
+        )
+        show_destination_menu_item.connect(
+            'activate',
+            self.on_video_index_show_destination,
+            media_data_obj,
+        )
+        filesystem_submenu.append(show_destination_menu_item)
+        if isinstance(media_data_obj, media.Folder) \
+        and media_data_obj.priv_flag:
+            show_destination_menu_item.set_sensitive(False)
+
+        filesystem_menu_item = Gtk.MenuItem.new_with_mnemonic('_Filesystem')
+        filesystem_menu_item.set_submenu(filesystem_submenu)
+        popup_menu.append(filesystem_menu_item)
+
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+
+        show_properties_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Show _properties...',
+        )
+        show_properties_menu_item.connect(
+            'activate',
+            self.on_video_index_show_properties,
+            media_data_obj,
+        )
+        popup_menu.append(show_properties_menu_item)
+        if self.app_obj.current_manager_obj:
+            show_properties_menu_item.set_sensitive(False)
+
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+            
+        # Delete items
+        delete_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'De_lete ' + media_type,
+        )
+        delete_menu_item.connect(
+            'activate',
+            self.on_video_index_delete_container,
+            media_data_obj,
+        )
+        if self.app_obj.current_manager_obj:
+            delete_menu_item.set_sensitive(False)
+        popup_menu.append(delete_menu_item)
+
+        # Create the popup menu
+        popup_menu.show_all()
+        popup_menu.popup(None, None, None, None, event.button, event.time)
+
+
+    def video_catalogue_popup_menu(self, event, video_obj):
+
+        """Called by mainwin.SimpleCatalogueItem.on_right_click() and
+        mainwin.ComplexCatalogueItem.on_right_click().
+
+        When the user right-clicks on the Video Catalogue, show a context-
+        sensitive popup menu.
+
+        Args:
+
+            event (Gdk.EventButton): The mouse click event
+
+            video_obj (media.Video): The video object displayed in the clicked
+                row
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 2735 video_catalogue_popup_menu')
+
+        # Set up the popup menu
+        popup_menu = Gtk.Menu()
+
+        # Check/download videos
+        check_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Check video'
+        )
+        check_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_check,
+            video_obj,
+        )
+        if self.app_obj.current_manager_obj:
+            check_menu_item.set_sensitive(False)
+        popup_menu.append(check_menu_item)
+
+        if not video_obj.dl_flag:
+
+            download_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                '_Download video'
+            )
+            download_menu_item.connect(
+                'activate',
+                self.on_video_catalogue_download,
+                video_obj,
+            )
+            if self.app_obj.current_manager_obj:
+                download_menu_item.set_sensitive(False)
+            popup_menu.append(download_menu_item)
+
+        else:
+
+            download_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'Re-_download this video'
+            )
+            download_menu_item.connect(
+                'activate',
+                self.on_video_catalogue_re_download,
+                video_obj,
+            )
+            if self.app_obj.current_manager_obj:
+                download_menu_item.set_sensitive(False)
+            popup_menu.append(download_menu_item)
+
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+
+        # Watch video
+        self.add_watch_video_menu_items(popup_menu, video_obj)
+        
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+
+        # New/favourite videos
+        new_video_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
+            'Video is _new',
+        )
+        new_video_menu_item.set_active(video_obj.new_flag)
+        new_video_menu_item.connect(
+            'toggled',
+            self.on_video_catalogue_toggle_new_video,
+            video_obj,
+        )
+        popup_menu.append(new_video_menu_item)
+        if not video_obj.dl_flag:
+            new_video_menu_item.set_sensitive(False)
+
+        fav_video_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
+            'Video is _favourite',
+        )
+        fav_video_menu_item.set_active(video_obj.fav_flag)
+        fav_video_menu_item.connect(
+            'toggled',
+            self.on_video_catalogue_toggle_favourite_video,
+            video_obj,
+        )
+        popup_menu.append(fav_video_menu_item)
+        if not video_obj.dl_flag:
+            fav_video_menu_item.set_sensitive(False)
+
+        archive_video_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
+            'Video is _archived',
+        )
+        archive_video_menu_item.set_active(video_obj.archive_flag)
+        archive_video_menu_item.connect(
+            'toggled',
+            self.on_video_catalogue_toggle_archived_video,
+            video_obj,
+        )
+        popup_menu.append(archive_video_menu_item)
+        if not video_obj.dl_flag:
+            archive_video_menu_item.set_sensitive(False)
+
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+
+        # Apply/remove/edit download options, disable downloads
+        downloads_submenu = Gtk.Menu()
+        
+        # (Desensitise these menu items, if an edit window is already open)
+        no_options_flag = False
+        for win_obj in self.config_win_list:
+            if isinstance(win_obj, config.OptionsEditWin) \
+            and video_obj.options_obj == win_obj.edit_obj:
+                no_options_flag = True
+                break
+
+        if not video_obj.options_obj:
+
+            apply_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'Apply download _options...',
+            )
+            apply_options_menu_item.connect(
+                'activate',
+                self.on_video_catalogue_apply_options,
+                video_obj,
+            )
+            downloads_submenu.append(apply_options_menu_item)
+            if no_options_flag or self.app_obj.current_manager_obj:
+                apply_options_menu_item.set_sensitive(False)
+
+        else:
+
+            remove_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'Remove download _options',
+            )
+            remove_options_menu_item.connect(
+                'activate',
+                self.on_video_catalogue_remove_options,
+                video_obj,
+            )
+            downloads_submenu.append(remove_options_menu_item)
+            if no_options_flag or self.app_obj.current_manager_obj:
+                remove_options_menu_item.set_sensitive(False)
+
+        edit_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Edit download options...',
+        )
+        edit_options_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_edit_options,
+            video_obj,
+        )
+        downloads_submenu.append(edit_options_menu_item)
+        if no_options_flag or self.app_obj.current_manager_obj \
+        or not video_obj.options_obj:
+            edit_options_menu_item.set_sensitive(False)
+
+        # Separator
+        downloads_submenu.append(Gtk.SeparatorMenuItem())
+        
+        enforce_check_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
+            'D_isable downloads',
+        )
+        enforce_check_menu_item.set_active(video_obj.dl_sim_flag)
+        enforce_check_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_enforce_check,
+            video_obj,
+        )
+        downloads_submenu.append(enforce_check_menu_item)
+        # (Don't allow the user to change the setting of
+        #   media.Video.dl_sim_flag if the video is in a channel or playlist,
+        #   since media.Channel.dl_sim_flag or media.Playlist.dl_sim_flag
+        #   applies instead)
+        if self.app_obj.current_manager_obj \
+        or not isinstance(video_obj.parent_obj, media.Folder):
+            enforce_check_menu_item.set_sensitive(False)
+
+        downloads_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Downloads',
+        )
+        downloads_menu_item.set_submenu(downloads_submenu)
+        popup_menu.append(downloads_menu_item)
+
+        # Show properties
+        show_properties_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Show _properties...',
+        )
+        show_properties_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_show_properties,
+            video_obj,
+        )
+        popup_menu.append(show_properties_menu_item)
+        if self.app_obj.current_manager_obj:
+            show_properties_menu_item.set_sensitive(False)
+
+        # Separator
+        popup_menu.append(Gtk.SeparatorMenuItem())
+
+        # Delete video
+        delete_menu_item = Gtk.MenuItem.new_with_mnemonic('De_lete video')
+        delete_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_delete_video,
+            video_obj,
+        )
+        popup_menu.append(delete_menu_item)
+
+        # Create the popup menu
+        popup_menu.show_all()
+        popup_menu.popup(None, None, None, None, event.button, event.time)
+
+
+    def progress_list_popup_menu(self, event, dbid):
+
+        """Called by self.on_progress_list_right_click().
+
+        When the user right-clicks on the Progress List, show a context-
+        sensitive popup menu.
+
+        Args:
+
+            event (Gdk.EventButton): The mouse click event
+
+            dbid (int): The dbid of the clicked media data object
+
+        """
+        
+        if DEBUG_FUNC_FLAG:
+            print('mw 3372 progress_list_popup_menu')
+
+        # Find the downloads.VideoDownloader which is currently handling the
+        #   clicked media data object (if any)
+        worker_obj = None
+        video_downloader_obj = None
+        
+        if self.app_obj.download_manager_obj:
+            for this_worker_obj \
+            in self.app_obj.download_manager_obj.worker_list:
+                if this_worker_obj.running_flag \
+                and this_worker_obj.download_item_obj.dbid == dbid \
+                and this_worker_obj.video_downloader_obj is not None:
+                    worker_obj = this_worker_obj
+                    video_downloader_obj = this_worker_obj.video_downloader_obj
+                    break
+
+        # Set up the popup menu
+        popup_menu = Gtk.Menu()
+
+        # Stop check/download
+        stop_now_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Stop now',
+        )
+        stop_now_menu_item.connect(
+            'activate',
+            self.on_progress_list_stop_now,
+            dbid,
+            worker_obj,
+            video_downloader_obj,
+        )
+        popup_menu.append(stop_now_menu_item)
+        if not self.app_obj.download_manager_obj \
+        or video_downloader_obj is None:
+            stop_now_menu_item.set_sensitive(False)
+
+        stop_soon_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Stop after this _video',
+        )
+        stop_soon_menu_item.connect(
+            'activate',
+            self.on_progress_list_stop_soon,
+            dbid,
+            worker_obj,
+            video_downloader_obj,
+        )
+        popup_menu.append(stop_soon_menu_item)
+        if not self.app_obj.download_manager_obj \
+        or video_downloader_obj is None:
+            stop_soon_menu_item.set_sensitive(False)
+
+                    
+        # Create the popup menu
+        popup_menu.show_all()
+        popup_menu.popup(None, None, None, None, event.button, event.time)
+
+
+    def results_list_popup_menu(self, event, path, dbid):
+
+        """Called by self.on_results_list_right_click().
+
+        When the user right-clicks on the Results List, show a context-
+        sensitive popup menu.
+
+        Args:
+
+            event (Gdk.EventButton): The mouse click event
+
+            path (Gtk.TreePath): Path to the clicked row in the treeview
+
+            dbid (int): The dbid of the clicked video object
+
+        """
+        
+        if DEBUG_FUNC_FLAG:
+            print('mw 3372 video_index_popup_menu')
+
+        # Find the right-clicked video object, and check it still exists
+        if not dbid in self.app_obj.media_reg_dict:
+            return
+
+        video_obj = self.app_obj.media_reg_dict[dbid]
+        if not isinstance(video_obj, media.Video):
+            return
+
+        # Set up the popup menu
+        popup_menu = Gtk.Menu()
+
+        # Watch video
+        self.add_watch_video_menu_items(popup_menu, video_obj)
+
+        # Separator
+        separator_item = Gtk.SeparatorMenuItem()
+        popup_menu.append(separator_item)
+        
+        # Delete video
+        delete_menu_item = Gtk.MenuItem.new_with_mnemonic('_Delete video')
+        delete_menu_item.connect(
+            'activate',
+            self.on_results_list_delete_video,
+            video_obj,
+            path,
+        )
+        popup_menu.append(delete_menu_item)
+        if self.app_obj.current_manager_obj:
+            delete_menu_item.set_sensitive(False)
+        
+        # Create the popup menu
+        popup_menu.show_all()
+        popup_menu.popup(None, None, None, None, event.button, event.time)
+
+
+    def video_index_setup_contents_submenu(self, submenu, media_data_obj,
+    only_child_videos_flag=False):
+
+        """Called by self.video_index_popup_menu().
+
+        Sets up a submenu for handling the contents of a channel, playlist
+        or folder.
+
+        Args:
+
+            submenu (Gtk.Menu): The submenu to set up, currently empty
+
+            media_data_obj (media.Channel, media.Playlist, media.Folder): The
+                channel, playlist or folder whose contents should be modified
+                by items in the sub-menu
+
+            only_child_videos_flag (True or False): Set to True when only a
+                folder's child videos (not anything in its child channels,
+                playlists or folders) should be modified by items in the
+                sub-menu; False if all child objects should be modified
+
+        """
+
+        mark_new_menu_item = Gtk.MenuItem.new_with_mnemonic('Mark as _new')
+        mark_new_menu_item.connect(
+            'activate',
+            self.on_video_index_mark_new,
+            media_data_obj,
+            only_child_videos_flag,
+        )
+        submenu.append(mark_new_menu_item)
+        if media_data_obj == self.app_obj.fixed_new_folder:
+            mark_new_menu_item.set_sensitive(False)
+
+        mark_old_menu_item = Gtk.MenuItem.new_with_mnemonic('Mark as n_ot new')
+        mark_old_menu_item.connect(
+            'activate',
+            self.on_video_index_mark_not_new,
+            media_data_obj,
+            only_child_videos_flag,
+        )
+        submenu.append(mark_old_menu_item)
+
+        # Separator
+        submenu.append(Gtk.SeparatorMenuItem())
+        
+        mark_fav_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Mark as _favourite',
+        )
+        mark_fav_menu_item.connect(
+            'activate',
+            self.on_video_index_mark_favourite,
+            media_data_obj,
+            only_child_videos_flag,
+        )
+        submenu.append(mark_fav_menu_item)
+        if media_data_obj == self.app_obj.fixed_fav_folder:
+            mark_fav_menu_item.set_sensitive(False)
+
+        mark_not_fav_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Mark as not f_avourite',
+        )
+        mark_not_fav_menu_item.connect(
+            'activate',
+            self.on_video_index_mark_not_favourite,
+            media_data_obj,
+            only_child_videos_flag,
+        )
+        submenu.append(mark_not_fav_menu_item)
+
+        # Separator
+        submenu.append(Gtk.SeparatorMenuItem())
+        
+        mark_archived_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Mark videos as _archived',
+        )
+        mark_archived_menu_item.connect(
+            'activate',
+            self.on_video_index_mark_archived,
+            media_data_obj,
+            only_child_videos_flag,
+        )
+        submenu.append(mark_archived_menu_item)
+
+        mark_not_archive_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            'Mark videos as not ar_chived',
+        )
+        mark_not_archive_menu_item.connect(
+            'activate',
+            self.on_video_index_mark_not_archived,
+            media_data_obj,
+            only_child_videos_flag,
+        )
+        submenu.append(mark_not_archive_menu_item)
+
+
+    def add_watch_video_menu_items(self, popup_menu, video_obj):
+
+        """Called by self.video_catalogue_popup_menu() and
+        self.results_list_popup_menu().
+
+        Adds common menu items to the popup menu.
+
+        Args:
+
+            popup_menu (Gtk.Menu): The popup menu
+
+            video_obj (media.Video): The video object that was right-clicked
+            
+        """
+
+        # Watch video in player
+        watch_player_menu_item = Gtk.MenuItem.new_with_mnemonic(
+            '_Watch in _player',
+        )
+        watch_player_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_watch_video,
+            video_obj,
+        )
+        if not video_obj.dl_flag:
+            watch_player_menu_item.set_sensitive(False)
+        popup_menu.append(watch_player_menu_item)
+
+        # Watch video online. For YouTube URLs, offer an alternative website
+        if not video_obj.source:
+            watch_website_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'W_atch on website',
+            )
+        else:
+            mod_source = utils.convert_youtube_to_hooktube(video_obj.source)
+            if video_obj.source != mod_source:
+                watch_website_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                    'W_atch on YouTube',
+                )
+
+            else:
+                watch_website_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                    'W_atch on website',
+                )
+
+        watch_website_menu_item.connect(
+            'activate',
+            self.on_video_catalogue_watch_website,
+            video_obj,
+        )
+        if not video_obj.source:
+            watch_website_menu_item.set_sensitive(False)
+        popup_menu.append(watch_website_menu_item)
+
+        if video_obj.source and video_obj.source != mod_source:
+
+            watch_hooktube_menu_item = Gtk.MenuItem.new_with_mnemonic(
+                'Watch on _HookTube',
+            )
+            watch_hooktube_menu_item.connect(
+                'activate',
+                self.on_video_catalogue_watch_hooktube,
+                video_obj,
+            )
+            popup_menu.append(watch_hooktube_menu_item)
+            
+    
     # (Video Index)
 
 
@@ -2573,446 +3570,15 @@ class MainWin(Gtk.ApplicationWindow):
             renderer.set_property('weight', Pango.Weight.NORMAL)
 
         # If downloads disabled, show as italic text
-        if media_data_obj.dl_sim_flag:
+        if media_data_obj.dl_disable_flag:
             renderer.set_property('style', Pango.Style.ITALIC)
+            renderer.set_property('underline', True)
+        elif media_data_obj.dl_sim_flag:
+            renderer.set_property('style', Pango.Style.ITALIC)
+            renderer.set_property('underline', False)
         else:
             renderer.set_property('style', Pango.Style.NORMAL)
-
-
-    def video_index_popup_menu(self, event, name):
-
-        """Called by self.video_index_treeview_click_event().
-
-        When the user right-clicks on the Video Index, show a context-sensitive
-        popup menu.
-
-        Args:
-
-            event (Gdk.EventButton): The mouse click event
-
-            name (string): The name of the clicked media data object
-
-        """
-
-        if DEBUG_FUNC_FLAG:
-            print('mw 2174 video_index_popup_menu')
-
-        # Find the right-clicked media data object (and a string to describe
-        #   its type)
-        dbid = self.app_obj.media_name_dict[name]
-        media_data_obj = self.app_obj.media_reg_dict[dbid]
-
-        if isinstance(media_data_obj, media.Channel):
-            media_type = 'channel'
-        elif isinstance(media_data_obj, media.Playlist):
-            media_type = 'playlist'
-        else:
-            media_type = 'folder'
-
-        # Set up the popup menu
-        popup_menu = Gtk.Menu()
-
-        # Check/download/refresh items
-        check_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Check ' + media_type,
-        )
-        check_menu_item.connect(
-            'activate',
-            self.on_video_index_check,
-            media_data_obj,
-        )
-        if self.app_obj.current_manager_obj \
-        or (
-            isinstance(media_data_obj, media.Folder) \
-            and media_data_obj.priv_flag
-        ):
-            check_menu_item.set_sensitive(False)
-        popup_menu.append(check_menu_item)
-
-        download_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Download ' + media_type,
-        )
-        download_menu_item.connect(
-            'activate',
-            self.on_video_index_download,
-            media_data_obj,
-        )
-        if self.app_obj.current_manager_obj \
-        or (
-            isinstance(media_data_obj, media.Folder) \
-            and media_data_obj.priv_flag
-        ):
-            download_menu_item.set_sensitive(False)
-        popup_menu.append(download_menu_item)
-
-        refresh_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Refresh ' + media_type,
-        )
-        refresh_menu_item.connect(
-            'activate',
-            self.on_video_index_refresh,
-            media_data_obj,
-        )
-        if self.app_obj.current_manager_obj \
-        or (
-            isinstance(media_data_obj, media.Folder) \
-            and media_data_obj.priv_flag
-        ):
-            refresh_menu_item.set_sensitive(False)
-        popup_menu.append(refresh_menu_item)
-
-        # Separator
-        popup_menu.append(Gtk.SeparatorMenuItem())
-
-        # Apply/remove/edit download options, disable downloads
-
-        # (Desensitise these menu items, if an edit window is already open)
-        no_options_flag = False
-        for win_obj in self.config_win_list:
-            if isinstance(win_obj, config.OptionsEditWin) \
-            and media_data_obj.options_obj == win_obj.edit_obj:
-                no_options_flag = True
-                break
-
-        if not media_data_obj.options_obj:
-
-            apply_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'Apply download _options...',
-            )
-            apply_options_menu_item.connect(
-                'activate',
-                self.on_video_index_apply_options,
-                media_data_obj,
-            )
-            popup_menu.append(apply_options_menu_item)
-            if no_options_flag or self.app_obj.current_manager_obj \
-            or (
-                isinstance(media_data_obj, media.Folder)
-                and media_data_obj.priv_flag
-            ):
-                apply_options_menu_item.set_sensitive(False)
-
-        else:
-
-            remove_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'Remove download _options',
-            )
-            remove_options_menu_item.connect(
-                'activate',
-                self.on_video_index_remove_options,
-                media_data_obj,
-            )
-            popup_menu.append(remove_options_menu_item)
-            if no_options_flag or self.app_obj.current_manager_obj \
-            or (
-                isinstance(media_data_obj, media.Folder)
-                and media_data_obj.priv_flag
-            ):
-                remove_options_menu_item.set_sensitive(False)
-
-        edit_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Edit download options...',
-        )
-        edit_options_menu_item.connect(
-            'activate',
-            self.on_video_index_edit_options,
-            media_data_obj,
-        )
-        popup_menu.append(edit_options_menu_item)
-        if no_options_flag or self.app_obj.current_manager_obj \
-        or not media_data_obj.options_obj:
-            edit_options_menu_item.set_sensitive(False)
-
-        enforce_check_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
-            'D_isable video downloads',
-        )
-        enforce_check_menu_item.set_active(media_data_obj.dl_sim_flag)
-        enforce_check_menu_item.connect(
-            'activate',
-            self.on_video_index_enforce_check,
-            media_data_obj,
-        )
-        popup_menu.append(enforce_check_menu_item)
-        if self.app_obj.current_manager_obj:
-            enforce_check_menu_item.set_sensitive(False)
-
-        # Separator
-        popup_menu.append(Gtk.SeparatorMenuItem())
-
-        # Contents
-        contents_submenu = Gtk.Menu()
-
-        if not isinstance(media_data_obj, media.Folder):
-
-            self.video_index_setup_contents_submenu(
-                contents_submenu,
-                media_data_obj,
-                False,
-            )
-
-        else:
-
-            # All contents
-            all_contents_submenu = Gtk.Menu()
-
-            self.video_index_setup_contents_submenu(
-                all_contents_submenu,
-                media_data_obj,
-                False,
-            )
-
-            # Separator
-            all_contents_submenu.append(Gtk.SeparatorMenuItem())
-
-            empty_folder_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                '_Empty folder',
-            )
-            empty_folder_menu_item.connect(
-                'activate',
-                self.on_video_index_empty_folder,
-                media_data_obj,
-            )
-            all_contents_submenu.append(empty_folder_menu_item)
-            if not media_data_obj.child_list or media_data_obj.priv_flag:
-                empty_folder_menu_item.set_sensitive(False)
-
-            all_contents_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                '_All contents',
-            )
-            all_contents_menu_item.set_submenu(all_contents_submenu)
-            contents_submenu.append(all_contents_menu_item)
-
-            # Just folder videos
-            just_videos_submenu = Gtk.Menu()
-
-            self.video_index_setup_contents_submenu(
-                just_videos_submenu,
-                media_data_obj,
-                True,
-            )
-
-            # Separator
-            just_videos_submenu.append(Gtk.SeparatorMenuItem())
-
-            empty_videos_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                '_Remove videos',
-            )
-            empty_videos_menu_item.connect(
-                'activate',
-                self.on_video_index_remove_videos,
-                media_data_obj,
-            )
-            just_videos_submenu.append(empty_videos_menu_item)
-            if not media_data_obj.child_list or media_data_obj.priv_flag:
-                empty_videos_menu_item.set_sensitive(False)
-
-            just_videos_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                '_Just folder videos',
-            )
-            just_videos_menu_item.set_submenu(just_videos_submenu)
-            contents_submenu.append(just_videos_menu_item)
-
-        contents_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            utils.upper_case_first(media_type) + ' co_ntents',
-        )
-        contents_menu_item.set_submenu(contents_submenu)
-        popup_menu.append(contents_menu_item)
-
-        # Actions
-        actions_submenu = Gtk.Menu()
-
-        move_top_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            'Move to _top level',
-        )
-        move_top_menu_item.connect(
-            'activate',
-            self.on_video_index_move_to_top,
-            media_data_obj,
-        )
-        actions_submenu.append(move_top_menu_item)
-        if not media_data_obj.parent_obj \
-        or self.app_obj.current_manager_obj:
-            move_top_menu_item.set_sensitive(False)
-
-        if isinstance(media_data_obj, media.Folder):
-
-            hide_folder_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                '_Hide folder',
-            )
-            hide_folder_menu_item.connect(
-                'activate',
-                self.on_video_index_hide_folder,
-                media_data_obj,
-            )
-            actions_submenu.append(hide_folder_menu_item)
-
-        set_nickname_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            'Set _nickname...',
-        )
-        set_nickname_menu_item.connect(
-            'activate',
-            self.on_video_index_set_nickname,
-            media_data_obj,
-        )
-        actions_submenu.append(set_nickname_menu_item)
-        if isinstance(media_data_obj, media.Folder) \
-        and media_data_obj.priv_flag:
-            set_nickname_menu_item.set_sensitive(False)
-
-        export_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Export ' + media_type + '...',
-        )
-        export_menu_item.connect(
-            'activate',
-            self.on_video_index_export,
-            media_data_obj,
-        )
-        actions_submenu.append(export_menu_item)
-        if self.app_obj.current_manager_obj:
-            export_menu_item.set_sensitive(False)
-
-        # Separator
-        actions_submenu.append(Gtk.SeparatorMenuItem())
-
-        show_properties_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            'Show _properties...',
-        )
-        show_properties_menu_item.connect(
-            'activate',
-            self.on_video_index_show_properties,
-            media_data_obj,
-        )
-        actions_submenu.append(show_properties_menu_item)
-        if self.app_obj.current_manager_obj:
-            show_properties_menu_item.set_sensitive(False)
-
-        actions_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            utils.upper_case_first(media_type) + ' _actions',
-        )
-        actions_menu_item.set_submenu(actions_submenu)
-        popup_menu.append(actions_menu_item)
-
-        # Filesystem
-        filesystem_submenu = Gtk.Menu()
-
-        show_destination_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Show location',
-        )
-        show_destination_menu_item.connect(
-            'activate',
-            self.on_video_index_show_location,
-            media_data_obj,
-        )
-        filesystem_submenu.append(show_destination_menu_item)
-        if isinstance(media_data_obj, media.Folder) \
-        and media_data_obj.priv_flag:
-            show_destination_menu_item.set_sensitive(False)
-
-        rename_destination_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Rename location...',
-        )
-        rename_destination_menu_item.connect(
-            'activate',
-            self.on_video_index_rename_location,
-            media_data_obj,
-        )
-        filesystem_submenu.append(rename_destination_menu_item)
-        if self.app_obj.current_manager_obj or self.config_win_list \
-        or (
-            isinstance(media_data_obj, media.Folder) \
-            and media_data_obj.fixed_flag
-        ):
-            rename_destination_menu_item.set_sensitive(False)
-
-        filesystem_menu_item = Gtk.MenuItem.new_with_mnemonic('_Filesystem')
-        filesystem_menu_item.set_submenu(filesystem_submenu)
-        popup_menu.append(filesystem_menu_item)
-
-        # Separator
-        popup_menu.append(Gtk.SeparatorMenuItem())
-
-        # Delete items
-        delete_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            'De_lete ' + media_type,
-        )
-        delete_menu_item.connect(
-            'activate',
-            self.on_video_index_delete_container,
-            media_data_obj,
-        )
-        if self.app_obj.current_manager_obj:
-            delete_menu_item.set_sensitive(False)
-        popup_menu.append(delete_menu_item)
-
-        # Create the popup menu
-        popup_menu.show_all()
-        popup_menu.popup(None, None, None, None, event.button, event.time)
-
-
-    def video_index_setup_contents_submenu(self, submenu, media_data_obj,
-    only_child_videos_flag=False):
-
-        """Called by self.video_index_popup_menu().
-
-        Sets up a submenu for handling the contents of a channel, playlist
-        or folder.
-
-        Args:
-
-            submenu (Gtk.Menu): The submenu to set up, currently empty
-
-            media_data_obj (media.Channel, media.Playlist, media.Folder): The
-                channel, playlist or folder whose contents should be modified
-                by items in the sub-menu
-
-            only_child_videos_flag (True or False): Set to True when only a
-                folder's child videos (not anything in its child channels,
-                playlists or folders) should be modified by items in the
-                sub-menu; False if all child objects should be modified
-
-        """
-
-        mark_new_menu_item = Gtk.MenuItem.new_with_mnemonic('Mark as _new')
-        mark_new_menu_item.connect(
-            'activate',
-            self.on_video_index_mark_new,
-            media_data_obj,
-            only_child_videos_flag,
-        )
-        submenu.append(mark_new_menu_item)
-        if media_data_obj == self.app_obj.fixed_new_folder:
-            mark_new_menu_item.set_sensitive(False)
-
-        mark_old_menu_item = Gtk.MenuItem.new_with_mnemonic('Mark as n_ot new')
-        mark_old_menu_item.connect(
-            'activate',
-            self.on_video_index_mark_not_new,
-            media_data_obj,
-            only_child_videos_flag,
-        )
-        submenu.append(mark_old_menu_item)
-
-        mark_fav_menu_item = Gtk.MenuItem.new_with_mnemonic('Mark _favourite')
-        mark_fav_menu_item.connect(
-            'activate',
-            self.on_video_index_mark_favourite,
-            media_data_obj,
-            only_child_videos_flag,
-        )
-        submenu.append(mark_fav_menu_item)
-        if media_data_obj == self.app_obj.fixed_fav_folder:
-            mark_fav_menu_item.set_sensitive(False)
-
-        mark_not_fav_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            'Mark not f_avourite',
-        )
-        mark_not_fav_menu_item.connect(
-            'activate',
-            self.on_video_index_mark_not_favourite,
-            media_data_obj,
-            only_child_videos_flag,
-        )
-        submenu.append(mark_not_fav_menu_item)
+            renderer.set_property('underline', False)
 
 
     # (Video Catalogue)
@@ -3499,264 +4065,6 @@ class MainWin(Gtk.ApplicationWindow):
             self.catalogue_listbox.show_all()
 
 
-    def video_catalogue_popup_menu(self, event, video_obj):
-
-        """Called by mainwin.SimpleCatalogueItem.on_right_click() and
-        mainwin.ComplexCatalogueItem.on_right_click().
-
-        When the user right-clicks on the Video Catalogue, show a context-
-        sensitive popup menu.
-
-        Args:
-
-            event (Gdk.EventButton): The mouse click event
-
-            video_obj (media.Video): The video object displayed in the clicked
-                row
-
-        """
-
-        if DEBUG_FUNC_FLAG:
-            print('mw 2735 video_catalogue_popup_menu')
-
-        # Set up the popup menu
-        popup_menu = Gtk.Menu()
-
-        # Check/download videos
-        check_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Check video'
-        )
-        check_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_check,
-            video_obj,
-        )
-        if self.app_obj.current_manager_obj:
-            check_menu_item.set_sensitive(False)
-        popup_menu.append(check_menu_item)
-
-        if not video_obj.dl_flag:
-
-            download_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                '_Download video'
-            )
-            download_menu_item.connect(
-                'activate',
-                self.on_video_catalogue_download,
-                video_obj,
-            )
-            if self.app_obj.current_manager_obj:
-                download_menu_item.set_sensitive(False)
-            popup_menu.append(download_menu_item)
-
-        else:
-
-            download_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'Re-_download this video'
-            )
-            download_menu_item.connect(
-                'activate',
-                self.on_video_catalogue_re_download,
-                video_obj,
-            )
-            if self.app_obj.current_manager_obj:
-                download_menu_item.set_sensitive(False)
-            popup_menu.append(download_menu_item)
-
-        # Separator
-        separator_item = Gtk.SeparatorMenuItem()
-        popup_menu.append(separator_item)
-
-        # Watch video in player
-        watch_player_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Watch in _player',
-        )
-        watch_player_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_watch_video,
-            video_obj,
-        )
-        if not video_obj.dl_flag:
-            watch_player_menu_item.set_sensitive(False)
-        popup_menu.append(watch_player_menu_item)
-
-        # Watch video online. For YouTube URLs, offer an alternative website
-        if not video_obj.source:
-            watch_website_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'W_atch on website',
-            )
-        else:
-            mod_source = utils.convert_youtube_to_hooktube(video_obj.source)
-            if video_obj.source != mod_source:
-                watch_website_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                    'W_atch on YouTube',
-                )
-
-            else:
-                watch_website_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                    'W_atch on website',
-                )
-
-        watch_website_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_watch_website,
-            video_obj,
-        )
-        if not video_obj.source:
-            watch_website_menu_item.set_sensitive(False)
-        popup_menu.append(watch_website_menu_item)
-
-        if video_obj.source and video_obj.source != mod_source:
-
-            watch_hooktube_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'Watch on _HookTube',
-            )
-            watch_hooktube_menu_item.connect(
-                'activate',
-                self.on_video_catalogue_watch_hooktube,
-                video_obj,
-            )
-            popup_menu.append(watch_hooktube_menu_item)
-
-        # Separator
-        separator_item2 = Gtk.SeparatorMenuItem()
-        popup_menu.append(separator_item2)
-
-        # New/favourite videos
-        new_video_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
-            'Video is _new',
-        )
-        new_video_menu_item.set_active(video_obj.new_flag)
-        new_video_menu_item.connect(
-            'toggled',
-            self.on_video_catalogue_toggle_new_video,
-            video_obj,
-        )
-        popup_menu.append(new_video_menu_item)
-        if not video_obj.dl_flag:
-            new_video_menu_item.set_sensitive(False)
-
-        fav_video_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
-            'Video is _favourite',
-        )
-        fav_video_menu_item.set_active(video_obj.fav_flag)
-        fav_video_menu_item.connect(
-            'toggled',
-            self.on_video_catalogue_toggle_favourite_video,
-            video_obj,
-        )
-        popup_menu.append(fav_video_menu_item)
-        if not video_obj.dl_flag:
-            fav_video_menu_item.set_sensitive(False)
-
-        # Separator
-        separator_item3 = Gtk.SeparatorMenuItem()
-        popup_menu.append(separator_item3)
-
-        # Apply/remove/edit download options, disable downloads
-
-        # (Desensitise these menu items, if an edit window is already open)
-        no_options_flag = False
-        for win_obj in self.config_win_list:
-            if isinstance(win_obj, config.OptionsEditWin) \
-            and video_obj.options_obj == win_obj.edit_obj:
-                no_options_flag = True
-                break
-
-        if not video_obj.options_obj:
-
-            apply_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'Apply download _options...',
-            )
-            apply_options_menu_item.connect(
-                'activate',
-                self.on_video_catalogue_apply_options,
-                video_obj,
-            )
-            popup_menu.append(apply_options_menu_item)
-            if no_options_flag or self.app_obj.current_manager_obj:
-                apply_options_menu_item.set_sensitive(False)
-
-        else:
-
-            remove_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
-                'Remove download _options',
-            )
-            remove_options_menu_item.connect(
-                'activate',
-                self.on_video_catalogue_remove_options,
-                video_obj,
-            )
-            popup_menu.append(remove_options_menu_item)
-            if no_options_flag or self.app_obj.current_manager_obj:
-                remove_options_menu_item.set_sensitive(False)
-
-        edit_options_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            '_Edit download options...',
-        )
-        edit_options_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_edit_options,
-            video_obj,
-        )
-        popup_menu.append(edit_options_menu_item)
-        if no_options_flag or self.app_obj.current_manager_obj \
-        or not video_obj.options_obj:
-            edit_options_menu_item.set_sensitive(False)
-
-        enforce_check_menu_item = Gtk.CheckMenuItem.new_with_mnemonic(
-            'D_isable downloads',
-        )
-        enforce_check_menu_item.set_active(video_obj.dl_sim_flag)
-        enforce_check_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_enforce_check,
-            video_obj,
-        )
-        popup_menu.append(enforce_check_menu_item)
-        # (Don't allow the user to change the setting of
-        #   media.Video.dl_sim_flag if the video is in a channel or playlist,
-        #   since media.Channel.dl_sim_flag or media.Playlist.dl_sim_flag
-        #   applies instead)
-        if self.app_obj.current_manager_obj \
-        or not isinstance(video_obj.parent_obj, media.Folder):
-            enforce_check_menu_item.set_sensitive(False)
-
-        # Separator
-        separator_item4 = Gtk.SeparatorMenuItem()
-        popup_menu.append(separator_item4)
-
-        # Show properties
-        show_properties_menu_item = Gtk.MenuItem.new_with_mnemonic(
-            'Show _properties...',
-        )
-        show_properties_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_show_properties,
-            video_obj,
-        )
-        popup_menu.append(show_properties_menu_item)
-        if self.app_obj.current_manager_obj:
-            show_properties_menu_item.set_sensitive(False)
-
-        # Separator
-        separator_item5 = Gtk.SeparatorMenuItem()
-        popup_menu.append(separator_item5)
-
-        # Delete video
-        delete_menu_item = Gtk.MenuItem.new_with_mnemonic('De_lete video')
-        delete_menu_item.connect(
-            'activate',
-            self.on_video_catalogue_delete_video,
-            video_obj,
-        )
-        popup_menu.append(delete_menu_item)
-
-        # Create the popup menu
-        popup_menu.show_all()
-        popup_menu.popup(None, None, None, None, event.button, event.time)
-
-
     def video_catalogue_toolbar_reset(self):
 
         """Called by self.video_catalogue_redraw_all().
@@ -3856,6 +4164,7 @@ class MainWin(Gtk.ApplicationWindow):
 
         # Reset widgets
         self.progress_list_liststore = Gtk.ListStore(
+            int,
             GdkPixbuf.Pixbuf,
             str, str, str, str, str, str, str, str, str,
         )
@@ -3913,6 +4222,7 @@ class MainWin(Gtk.ApplicationWindow):
             # Prepare the new row in the treeview
             row_list = []
 
+            row_list.append(dbid)
             row_list.append(pixbuf)
             row_list.append(
                 utils.shorten_string(
@@ -4026,9 +4336,9 @@ class MainWin(Gtk.ApplicationWindow):
             tree_path = Gtk.TreePath(self.progress_list_row_dict[dbid])
 
             # Update statistics displayed in that row
-            # (Columns 0 and 1 are not modified, once the row has been added to
-            #   the treeview)
-            column = 1
+            # (Columns 0, 1 and 2 are not modified, once the row has been added
+            #   to the treeview)
+            column = 2
 
             for key in (
                 'playlist_index',
@@ -4092,6 +4402,7 @@ class MainWin(Gtk.ApplicationWindow):
 
         # Reset widgets
         self.results_list_liststore = Gtk.ListStore(
+            int,
             GdkPixbuf.Pixbuf,
             str, str, str, str,
             bool,
@@ -4163,6 +4474,7 @@ class MainWin(Gtk.ApplicationWindow):
         row_list = []
 
         # Set the row's initial contents
+        row_list.append(video_obj.dbid)
         row_list.append(pixbuf)
         row_list.append(
             utils.shorten_string(video_obj.nickname, self.string_max_len),
@@ -4314,7 +4626,7 @@ class MainWin(Gtk.ApplicationWindow):
 
                 self.results_list_liststore.set(
                     row_iter,
-                    1,
+                    2,
                     utils.shorten_string(
                         video_obj.nickname,
                         self.string_max_len,
@@ -4324,7 +4636,7 @@ class MainWin(Gtk.ApplicationWindow):
                 if video_obj.duration is not None:
                     self.results_list_liststore.set(
                         row_iter,
-                        2,
+                        3,
                         utils.convert_seconds_to_string(
                             video_obj.duration,
                         ),
@@ -4333,23 +4645,23 @@ class MainWin(Gtk.ApplicationWindow):
                 if video_obj.file_size:
                     self.results_list_liststore.set(
                         row_iter,
-                        3,
+                        4,
                         video_obj.get_file_size_string(),
                     )
 
                 if video_obj.upload_time:
                     self.results_list_liststore.set(
                         row_iter,
-                        4,
+                        5,
                         video_obj.get_upload_date_string(),
                     )
 
-                self.results_list_liststore.set(row_iter, 5, video_obj.dl_flag)
-                self.results_list_liststore.set(row_iter, 6, pixbuf)
+                self.results_list_liststore.set(row_iter, 6, video_obj.dl_flag)
+                self.results_list_liststore.set(row_iter, 7, pixbuf)
 
                 self.results_list_liststore.set(
                     row_iter,
-                    7,
+                    8,
                     utils.shorten_string(
                         video_obj.parent_obj.name,
                         self.string_max_len,
@@ -4713,6 +5025,38 @@ class MainWin(Gtk.ApplicationWindow):
         )
 
 
+    def on_video_index_dl_disable(self, menu_item, media_data_obj):
+
+        """Called from a callback in self.video_index_popup_menu().
+
+        Set the media data object's flag to disable checking and downloading.
+        
+        Args:
+
+            menu_item (Gtk.MenuItem): The clicked menu item
+
+            media_data_obj (media.Channel, media.Playlist or media.Channel):
+                The clicked media data object
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 3740 on_video_index_dl_disable')
+
+        if self.app_obj.current_manager_obj:
+            return self.app_obj.system_error(
+                236,
+                'Callback request denied due to current conditions',
+            )
+
+        if not media_data_obj.dl_disable_flag:
+            media_data_obj.set_dl_disable_flag(True)
+        else:
+            media_data_obj.set_dl_disable_flag(False)
+
+        self.video_index_update_row_text(media_data_obj)
+
+
     def on_video_index_check(self, menu_item, media_data_obj):
 
         """Called from a callback in self.video_index_popup_menu().
@@ -4987,13 +5331,75 @@ class MainWin(Gtk.ApplicationWindow):
         self.app_obj.export_from_db( [media_data_obj] )
 
 
+    def on_video_index_mark_archived(self, menu_item, media_data_obj,
+    only_child_videos_flag):
+
+        """Called from a callback in self.video_index_popup_menu().
+
+        Mark all of the children of this channel, playlist or folder (and all
+        of their chidlren, and so on) as archived.
+
+        Args:
+
+            menu_item (Gtk.MenuItem): The clicked menu item
+
+            media_data_obj (media.Channel, media.Playlist or media.Channel):
+                The clicked media data object
+
+            only_child_videos_flag (True or False): Set to True if only child
+                video objects should be marked; False if all descendants should
+                be marked
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4010 on_video_index_mark_archived')
+
+        self.app_obj.mark_container_archived(
+            media_data_obj,
+            True,
+            only_child_videos_flag,
+        )
+
+
+    def on_video_index_mark_not_archived(self, menu_item, media_data_obj,
+    only_child_videos_flag):
+
+        """Called from a callback in self.video_index_popup_menu().
+
+        Mark all videos in this folder (and in any child channels, playlists
+        and folders) as not archived.
+
+        Args:
+
+            menu_item (Gtk.MenuItem): The clicked menu item
+
+            media_data_obj (media.Channel, media.Playlist or media.Channel):
+                The clicked media data object
+
+            only_child_videos_flag (True or False): Set to True if only child
+                video objects should be marked; False if all descendants should
+                be marked
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4032 on_video_index_mark_not_archived')
+
+        self.app_obj.mark_container_archived(
+            media_data_obj,
+            False,
+            only_child_videos_flag,
+        )
+
+
     def on_video_index_mark_favourite(self, menu_item, media_data_obj,
     only_child_videos_flag):
 
         """Called from a callback in self.video_index_popup_menu().
 
         Mark all of the children of this channel, playlist or folder (and all
-        of their chidlren, and so on ) as favourite.
+        of their chidlren, and so on) as favourite.
 
         Args:
 
@@ -5024,7 +5430,7 @@ class MainWin(Gtk.ApplicationWindow):
         """Called from a callback in self.video_index_popup_menu().
 
         Mark all videos in this folder (and in any child channels, playlists
-        and folders) as not new.
+        and folders) as not favourite.
 
         Args:
 
@@ -5411,6 +5817,44 @@ class MainWin(Gtk.ApplicationWindow):
                 self.video_catalogue_redraw_all(name, 1, True)
 
 
+    def on_video_index_set_destination(self, menu_item, media_data_obj):
+
+        """Called from a callback in self.video_index_popup_menu().
+
+        Sets (or resets) the alternative download destination for the selected
+        channel, playlist or folder.
+
+        Args:
+
+            menu_item (Gtk.MenuItem): The clicked menu item
+
+            media_data_obj (media.Channel, media.Playlist or media.Channel):
+                The clicked media data object
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4293 on_video_index_set_destination')
+
+        if isinstance(media_data_obj, media.Video):
+            return self.app_obj.system_error(
+                235,
+                'Cannot set the download destination of a video',
+            )
+
+        dialogue_win = SetDestinationDialogue(self, media_data_obj)
+        response = dialogue_win.run()
+
+        # Retrieve user choices from the dialogue window, before destroying it
+        dbid = dialogue_win.choice
+        dialogue_win.destroy()
+
+        if response == Gtk.ResponseType.OK:
+
+            if dbid != media_data_obj.master_dbid:
+                media_data_obj.set_master_dbid(self.app_obj, dbid)
+
+
     def on_video_index_set_nickname(self, menu_item, media_data_obj):
 
         """Called from a callback in self.video_index_popup_menu().
@@ -5453,12 +5897,38 @@ class MainWin(Gtk.ApplicationWindow):
             self.video_index_update_row_text(media_data_obj)
 
 
+    def on_video_index_show_destination(self, menu_item, media_data_obj):
+
+        """Called from a callback in self.video_index_popup_menu().
+
+        Opens the sub-directory in which downloads for the specified media data
+        object are stored (which might be the default sub-directory for
+        another media data object, if the media data object's .master_dbid has
+        been modified).
+
+        Args:
+
+            menu_item (Gtk.MenuItem): The clicked menu item
+
+            media_data_obj (media.Channel, media.Playlist or media.Channel):
+                The clicked media data object
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4330 on_video_index_show_destination')
+
+        other_obj = self.app_obj.media_reg_dict[media_data_obj.master_dbid]
+        path = other_obj.get_dir(self.app_obj)
+        utils.open_file(path)
+
+
     def on_video_index_show_location(self, menu_item, media_data_obj):
 
         """Called from a callback in self.video_index_popup_menu().
 
-        Opens the sub-directory folder in which downloads for the specified
-        media data object are stored.
+        Opens the sub-directory in which downloads for the specified media data
+        object are stored (by default).
 
         Args:
 
@@ -5581,7 +6051,7 @@ class MainWin(Gtk.ApplicationWindow):
         if DEBUG_FUNC_FLAG:
             print('mw 4439 on_video_catalogue_delete_video')
 
-        self.app_obj.delete_video(media_data_obj, False, True)
+        self.app_obj.delete_video(media_data_obj, True)
 
 
     def on_video_catalogue_download(self, menu_item, media_data_obj):
@@ -5849,6 +6319,32 @@ class MainWin(Gtk.ApplicationWindow):
         config.VideoEditWin(self.app_obj, media_data_obj)
 
 
+    def on_video_catalogue_toggle_archived_video(self, menu_item, \
+    media_data_obj):
+
+        """Called from a callback in self.video_catalogue_popup_menu().
+
+        Marks the video as archived or not archived.
+
+        Args:
+
+            menu_item (Gtk.MenuItem): The clicked menu item
+
+            media_data_obj (media.Video) - The clicked video object
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4658 on_video_catalogue_toggle_archived_video')
+
+        if not media_data_obj.archive_flag:
+            media_data_obj.set_archive_flag(True)
+        else:
+            media_data_obj.set_archive_flag(False)
+
+        self.video_catalogue_update_row(media_data_obj)
+
+
     def on_video_catalogue_toggle_favourite_video(self, menu_item, \
     media_data_obj):
 
@@ -5974,6 +6470,197 @@ class MainWin(Gtk.ApplicationWindow):
             self.app_obj.mark_video_new(media_data_obj, False)
 
 
+    def on_progress_list_right_click(self, treeview, event):
+
+        """Called from callback in self.setup_progress_tab().
+
+        When the user right-clicks an item in the Progress List, create a
+        context-sensitive popup menu.
+
+        Args:
+
+            treeview (Gtk.TreeView): The Progress List's treeview
+
+            event (Gdk.EventButton): The event emitting the Gtk signal
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4758 on_progress_list_right_click')
+        
+        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
+
+            # If the user right-clicked on empty space, the call to
+            #   .get_path_at_pos returns None (or an empty list)
+            if not treeview.get_path_at_pos(
+                int(event.x),
+                int(event.y),
+            ):
+                return
+
+            path, column, cellx, celly = treeview.get_path_at_pos(
+                int(event.x),
+                int(event.y),
+            )
+
+            iter = self.progress_list_liststore.get_iter(path)
+            if iter is not None:
+                self.progress_list_popup_menu(
+                    event,
+                    self.progress_list_liststore[iter][0],
+                )
+
+
+    def on_progress_list_stop_now(self, menu_item, dbid, worker_obj,
+    video_downloader_obj):
+
+        """Called from a callback in self.progress_list_popup_menu().
+
+        Halts checking/downloading the selected media data object.
+        
+        Args:
+
+            menu_item (Gtk.MenuItem): The menu item that was clicked
+
+            dbid (int): The dbid of the selected media data object
+
+            worker_obj (downloads.DownloadWorker): The worker currently
+                handling checking/downloading this media data object
+
+            video_downloader_obj (downloads.VideoDownloader): The video
+                downloader handling checking/downloading this media data object
+            
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4439 on_progress_list_stop_now')
+
+        # Check that, since the popup menu was created, the video downloader
+        #   hasn't already finished checking/downloading the selected media
+        #   data object
+        if not self.app_obj.download_manager_obj \
+        or not worker_obj.running_flag \
+        or worker_obj.download_item_obj.dbid != dbid \
+        or worker_obj.video_downloader_obj is None:
+            # Do nothing
+            return
+            
+        # Stop the video downloader (causing the worker to be assigned a new
+        #   downloads.DownloadItem, if there are any left)
+        video_downloader_obj.stop()
+ 
+
+    def on_progress_list_stop_soon(self, menu_item, dbid, worker_obj,
+    video_downloader_obj):
+
+        """Called from a callback in self.progress_list_popup_menu().
+
+        Halts checking/downloading the selected media data object, after the
+        current video check/download has finished.
+        
+        Args:
+
+            menu_item (Gtk.MenuItem): The menu item that was clicked
+
+            dbid (int): The dbid of the selected media data object
+
+            worker_obj (downloads.DownloadWorker): The worker currently
+                handling checking/downloading this media data object
+
+            video_downloader_obj (downloads.VideoDownloader): The video
+                downloader handling checking/downloading this media data object
+            
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4439 on_progress_list_stop_soon')
+
+        # Check that, since the popup menu was created, the video downloader
+        #   hasn't already finished checking/downloading the selected media
+        #   data object
+        if not self.app_obj.download_manager_obj \
+        or not worker_obj.running_flag \
+        or worker_obj.download_item_obj.dbid != dbid \
+        or worker_obj.video_downloader_obj is None:
+            # Do nothing
+            return
+
+        # Tell the video downloader to stop after the current video check/
+        #   download has finished
+        video_downloader_obj.stop_soon()
+ 
+
+    def on_results_list_right_click(self, treeview, event):
+
+        """Called from callback in self.setup_progress_tab().
+
+        When the user right-clicks an item in the Results List, create a
+        context-sensitive popup menu.
+
+        Args:
+
+            treeview (Gtk.TreeView): The Results List's treeview
+
+            event (Gdk.EventButton): The event emitting the Gtk signal
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 3713 on_results_list_right_click')
+        
+        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
+
+            # If the user right-clicked on empty space, the call to
+            #   .get_path_at_pos returns None (or an empty list)
+            if not treeview.get_path_at_pos(
+                int(event.x),
+                int(event.y),
+            ):
+                return
+
+            path, column, cellx, celly = treeview.get_path_at_pos(
+                int(event.x),
+                int(event.y),
+            )
+
+            iter = self.results_list_liststore.get_iter(path)
+            if iter is not None:
+                self.results_list_popup_menu(
+                    event,
+                    path,
+                    self.results_list_liststore[iter][0],
+                )
+
+
+    def on_results_list_delete_video(self, menu_item, media_data_obj, path):
+
+        """Called from a callback in self.video_catalogue_popup_menu() and
+        self.results_list_popup_menu().
+
+        Deletes the video, and removes a row from the Results List.
+
+        Args:
+
+            menu_item (Gtk.MenuItem): The menu item that was clicked
+
+            media_data_obj (media.Video): The video displayed on the clicked
+                row
+
+            path (Gtk.TreePath): Path to the clicked row in the treeview
+            
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 4439 on_results_list_delete_video')
+
+        # Delete the video
+        self.app_obj.delete_video(media_data_obj, True)
+
+        # Remove the row from the Results List
+        iter = self.results_list_liststore.get_iter(path)
+        self.results_list_liststore.remove(iter)
+        
+        
     def on_spinbutton_changed(self, spinbutton):
 
         """Called from callback in self.setup_progress_tab().
@@ -6282,9 +6969,14 @@ class SimpleCatalogueItem(object):
 
         # Set the download status
         if self.video_obj.dl_flag:
-            self.status_image.set_from_pixbuf(
-                self.main_win_obj.pixbuf_dict['have_file_small'],
-            )
+            if self.video_obj.archive_flag:
+                self.status_image.set_from_pixbuf(
+                    self.main_win_obj.pixbuf_dict['archived_small'],
+                )
+            else:
+                self.status_image.set_from_pixbuf(
+                    self.main_win_obj.pixbuf_dict['have_file_small'],
+                )
         else:
             self.status_image.set_from_pixbuf(
                 self.main_win_obj.pixbuf_dict['no_file_small'],
@@ -6776,9 +7468,14 @@ class ComplexCatalogueItem(object):
 
         # Set the download status
         if self.video_obj.dl_flag:
-            self.status_image.set_from_pixbuf(
-                self.main_win_obj.pixbuf_dict['have_file_small'],
-            )
+            if self.video_obj.archive_flag:
+                self.status_image.set_from_pixbuf(
+                    self.main_win_obj.pixbuf_dict['archived_small'],
+                )
+            else:
+                self.status_image.set_from_pixbuf(
+                    self.main_win_obj.pixbuf_dict['have_file_small'],
+                )
         else:
             self.status_image.set_from_pixbuf(
                 self.main_win_obj.pixbuf_dict['no_file_small'],
@@ -7261,6 +7958,7 @@ class AddVideoDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         label = Gtk.Label('Copy and paste the links to one or more videos')
@@ -7323,11 +8021,7 @@ class AddVideoDialogue(Gtk.Dialog):
 
         image = Gtk.Image()
         box.add(image)
-        image.set_from_pixbuf(
-            main_win_obj.app_obj.file_manager_obj.load_to_pixbuf(
-                main_win_obj.icon_dict['folder_small']
-            ),
-        )
+        image.set_from_pixbuf(main_win_obj.pixbuf_dict['folder_small'])
 
         listmodel = Gtk.ListStore(str)
         for item in self.folder_list:
@@ -7415,6 +8109,7 @@ class AddChannelDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         label = Gtk.Label('Enter the channel name')
@@ -7490,11 +8185,7 @@ class AddChannelDialogue(Gtk.Dialog):
 
         image = Gtk.Image()
         box.add(image)
-        image.set_from_pixbuf(
-            main_win_obj.app_obj.file_manager_obj.load_to_pixbuf(
-                main_win_obj.icon_dict['folder_small']
-            ),
-        )
+        image.set_from_pixbuf(main_win_obj.pixbuf_dict['folder_small'])
 
         listmodel = Gtk.ListStore(str)
         for item in self.folder_list:
@@ -7580,6 +8271,7 @@ class AddPlaylistDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         label = Gtk.Label('Enter the playlist name')
@@ -7654,12 +8346,8 @@ class AddPlaylistDialogue(Gtk.Dialog):
 
         image = Gtk.Image()
         box.add(image)
-        image.set_from_pixbuf(
-            main_win_obj.app_obj.file_manager_obj.load_to_pixbuf(
-                main_win_obj.icon_dict['folder_small']
-            ),
-        )
-
+        image.set_from_pixbuf(main_win_obj.pixbuf_dict['folder_small'])
+        
         listmodel = Gtk.ListStore(str)
         for item in self.folder_list:
             listmodel.append([item])
@@ -7744,6 +8432,7 @@ class AddFolderDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         label = Gtk.Label('Enter the folder name')
@@ -7799,12 +8488,8 @@ class AddFolderDialogue(Gtk.Dialog):
 
         image = Gtk.Image()
         box.add(image)
-        image.set_from_pixbuf(
-            main_win_obj.app_obj.file_manager_obj.load_to_pixbuf(
-                main_win_obj.icon_dict['folder_small']
-            ),
-        )
-
+        image.set_from_pixbuf(main_win_obj.pixbuf_dict['folder_small'])
+        
         self.folder_list.sort()
 
         listmodel = Gtk.ListStore(str)
@@ -7918,6 +8603,7 @@ class DeleteContainerDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         if not total_count:
@@ -8067,6 +8753,7 @@ class SetDirectoryDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         if os.name == 'nt':
@@ -8137,6 +8824,7 @@ class ExportDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         if not whole_flag:
@@ -8268,6 +8956,7 @@ class ImportDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         label = Gtk.Label('Choose which items to import')
@@ -8364,7 +9053,7 @@ class ImportDialogue(Gtk.Dialog):
         #       structure)
         #   - 'import_flag': bool (True if this channel/playlist/folder should
         #       be imported, False if not)
-        converted_list = self.convert_to_list(db_dict)
+        converted_list = self.convert_to_list( db_dict, [] )
 
         # Add a line to the textview for each channel, playlist and folder
         for mini_dict in converted_list:
@@ -8399,7 +9088,7 @@ class ImportDialogue(Gtk.Dialog):
     # Public class methods
 
 
-    def convert_to_list(self, db_dict, converted_list=[],
+    def convert_to_list(self, db_dict, converted_list,
     parent_mini_dict=None, recursion_level=0):
 
         """Called by self.__init__(), and then recursively by this function.
@@ -8588,6 +9277,7 @@ class SetNicknameDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         if isinstance(media_data_obj, media.Channel):
@@ -8614,6 +9304,248 @@ class SetNicknameDialogue(Gtk.Dialog):
         self.show_all()
 
 
+class SetDestinationDialogue(Gtk.Dialog):
+
+    """Python class handling a dialogue window that prompts the user to set the
+    alternative download destination for a channel, playlist or folder.
+
+    Args:
+
+        main_win_obj (mainwin.MainWin): The parent main window
+
+        media_data_obj (media.Channel, media.Playlist, media.Folder): The media
+            data object whose download destination is to be changed
+
+    """
+
+
+    # Standard class methods
+
+
+    def __init__(self, main_win_obj, media_data_obj):
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 8711 __init__')
+
+        Gtk.Dialog.__init__(
+            self,
+            'Set download destination',
+            main_win_obj,
+            Gtk.DialogFlags.DESTROY_WITH_PARENT,
+            (
+                Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+                Gtk.STOCK_OK, Gtk.ResponseType.OK,
+            )
+        )
+
+        self.set_modal(False)
+
+        # Set up the dialogue window
+        box = self.get_content_area()
+
+        grid = Gtk.Grid()
+        box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
+        grid.set_row_spacing(main_win_obj.spacing_size)
+
+        if os.name == 'nt':
+            dir_name = 'folder'
+        else:
+            dir_name = 'directory'
+
+        if isinstance(media_data_obj, media.Channel):
+            obj_type = 'channel'
+        elif isinstance(media_data_obj, media.Playlist):
+            obj_type = 'playlist'
+        else:
+            obj_type = 'folder'
+
+        label = Gtk.Label(          
+            'This ' + obj_type + ' can store its videos in its own ' \
+            + dir_name + ', or it can store\nthem in a different ' \
+            + dir_name \
+            + '\n\nChoose a different ' + dir_name + ' if:' \
+            + '\n\n1. You want to add a channel and its playlists, without' \
+            + ' downloading\nthe same video twice' \
+            + '\n\n2. A video creator has channels on both YouTube and' \
+            + ' BitChute, and\nyou want to add both without downloading the' \
+            + ' same video twice',            
+        )
+        grid.attach(label, 0, 0, 1, 1)
+
+        separator = Gtk.HSeparator()
+        grid.attach(separator, 0, 1, 1, 1)
+
+        radiobutton = Gtk.RadioButton.new_with_label_from_widget(
+            None,
+            'Use this ' + obj_type + '\'s own ' + dir_name,
+        )
+        grid.attach(radiobutton, 0, 2, 1, 1)
+        # Signal connect appears below
+
+        radiobutton2 = Gtk.RadioButton.new_from_widget(radiobutton)
+        radiobutton2.set_label('Choose a different ' + dir_name + ':')
+        grid.attach(radiobutton2, 0, 3, 1, 1)
+        # Signal connect appears below
+
+        # Get a sorted list of channels/playlists/folders
+        app_obj = main_win_obj.app_obj
+        name_list = list(app_obj.media_name_dict.keys())
+        name_list.sort(key=lambda x: x.lower())
+
+        # Add a combo
+        store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
+
+        count = -1
+        select_index = 0
+        
+        for name in name_list:
+            dbid = app_obj.media_name_dict[name]
+            obj = app_obj.media_reg_dict[dbid]
+
+            if isinstance(obj, media.Channel):
+                icon_name = 'channel_small'
+            elif isinstance(obj, media.Playlist):
+                icon_name = 'playlist_small'
+            else:
+                icon_name = 'folder_small'
+
+            store.append( [main_win_obj.pixbuf_dict[icon_name], name] )
+
+            count += 1
+            if dbid == media_data_obj.master_dbid:
+                select_index = count 
+
+        combo = Gtk.ComboBox.new_with_model(store)
+        grid.attach(combo, 0, 4, 1, 1)
+        combo.set_hexpand(True)
+
+        renderer_pixbuf = Gtk.CellRendererPixbuf()
+        combo.pack_start(renderer_pixbuf, False)
+        combo.add_attribute(renderer_pixbuf, 'pixbuf', 0)
+
+        renderer_text = Gtk.CellRendererText()
+        combo.pack_start(renderer_text, True)
+        combo.add_attribute(renderer_text, 'text', 1)
+
+        combo.set_active(select_index)
+        # Signal connect appears below
+
+        if media_data_obj.master_dbid == media_data_obj.dbid:
+            combo.set_sensitive(False)
+        else:
+            radiobutton2.set_active(True)
+            combo.set_sensitive(True)
+
+        # (Store function arguments as IVs, so callback functions can retrieve
+        #   them)
+        self.main_win_obj = main_win_obj
+        self.media_data_obj = media_data_obj
+        # (Store the user's choice as an IV, so the calling function can
+        #   retrieve it)
+        self.choice = media_data_obj.master_dbid
+
+        # Signal connects from above
+        radiobutton.connect(
+            'toggled',
+            self.on_radiobutton_toggled,
+            combo,
+        )
+                    
+        radiobutton2.connect(
+            'toggled',
+            self.on_radiobutton2_toggled,
+            combo,
+        )
+                    
+        combo.connect('changed', self.on_combo_changed)
+
+        # Display the dialogue window
+        self.show_all()
+
+
+    def on_combo_changed(self, combo):
+
+        """Called from callback in self.__init__().
+
+        Store the combobox's selected item, so the calling function can
+        retrieve it.
+
+        Args:
+
+            combo (Gtk.ComboBox): The clicked widget
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 6014 on_combo_changed')
+
+        tree_iter = combo.get_active_iter()
+        model = combo.get_model()
+        pixbuf, name = model[tree_iter][:2]
+
+        # (Allow for the possibility that the media data object might have
+        #   been deleted, since the dialogue window opened)
+        if name in self.main_win_obj.app_obj.media_name_dict:
+            dbid = self.main_win_obj.app_obj.media_name_dict[name]
+            obj = self.main_win_obj.app_obj.media_reg_dict[dbid]
+            self.choice = obj.dbid
+
+
+    def on_radiobutton_toggled(self, radiobutton, combo):
+
+        """Called from callback in self.__init__().
+
+        When the specified radiobutton is toggled, modify other widgets in the
+        dialogue window, and set self.choice (the value to be retrieved by the
+        calling function)
+
+        Args:
+
+            radiobutton (Gtk.RadioButton): The clicked widget
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 7974 on_radiobutton_toggled')
+
+        if radiobutton.get_active():
+            combo.set_sensitive(False)
+            self.choice = self.media_data_obj.dbid
+            
+
+    def on_radiobutton2_toggled(self, radiobutton2, combo):
+
+        """Called from callback in self.__init__().
+
+        When the specified radiobutton is toggled, modify other widgets in the
+        dialogue window, and set self.choice (the value to be retrieved by the
+        calling function)
+
+        Args:
+
+            radiobutton2 (Gtk.RadioButton): The clicked widget
+
+        """
+
+        if DEBUG_FUNC_FLAG:
+            print('mw 7974 on_radiobutton2_toggled')
+
+        if radiobutton2.get_active():
+            combo.set_sensitive(True)
+            
+            tree_iter = combo.get_active_iter()
+            model = combo.get_model()
+            pixbuf, name = model[tree_iter][:2]
+
+            # (Allow for the possibility that the media data object might have
+            #   been deleted, since the dialogue window opened)
+            if name in self.main_win_obj.app_obj.media_name_dict:
+                dbid = self.main_win_obj.app_obj.media_name_dict[name]
+                obj = self.main_win_obj.app_obj.media_reg_dict[dbid]
+                self.choice = obj.dbid
+            
+
 class RenameContainerDialogue(Gtk.Dialog):
 
     """Python class handling a dialogue window that prompts the user to rename
@@ -8662,6 +9594,7 @@ class RenameContainerDialogue(Gtk.Dialog):
 
         grid = Gtk.Grid()
         box.add(grid)
+        grid.set_border_width(main_win_obj.spacing_size)
         grid.set_row_spacing(main_win_obj.spacing_size)
 
         label = Gtk.Label(
diff --git a/tartube/media.py b/tartube/media.py
index 598d61e..c122be0 100755
--- a/tartube/media.py
+++ b/tartube/media.py
@@ -541,6 +541,14 @@ class GenericContainer(GenericMedia):
     # Set accessors
 
 
+    def set_dl_disable_flag(self, flag):
+
+        if flag:
+            self.dl_disable_flag = True
+        else:
+            self.dl_disable_flag = False
+
+
     def reset_counts(self, vid_count, new_count, fav_count, dl_count):
 
         """Called by mainapp.TartubeApp.update_db().
@@ -588,6 +596,56 @@ class GenericContainer(GenericMedia):
         self.new_count -= 1
 
 
+    def set_master_dbid(self, app_obj, dbid):
+
+        if dbid == self.master_dbid:
+            # No change to the current value
+            return
+
+        else:
+
+            # Update the old alternative download destination
+            if self.master_dbid != self.dbid:
+                old_dest_obj = app_obj.media_reg_dict[self.master_dbid]
+                old_dest_obj.del_slave_dbid(self.dbid)
+
+            # Update this object's IV
+            self.master_dbid = dbid
+
+            if self.master_dbid != self.dbid:
+
+                # Update the new alternative download destination
+                new_dest_obj = app_obj.media_reg_dict[self.master_dbid]
+                new_dest_obj.add_slave_dbid(self.dbid)
+
+
+    def add_slave_dbid(self, dbid):
+
+        """Called by self.set_master_dbid() only."""
+
+        # (Failsafe: don't add the same value to self.slave_dbid_list)
+        match_flag = False
+        for slave_dbid in self.slave_dbid_list:
+            if slave_dbid == dbid:
+                match_flag = True
+                break
+
+        if not match_flag:
+            self.slave_dbid_list.append(dbid)
+
+
+    def del_slave_dbid(self, dbid):
+
+        """Called by self.set_master_dbid() only."""
+        new_list = []
+        
+        for slave_dbid in self.slave_dbid_list:
+            if slave_dbid != dbid:
+                new_list.append(slave_dbid)
+
+        self.slave_dbid_list = new_list.copy()
+        
+        
     def set_name(self, name):
 
         """Must only be called by mainapp.TartubeApp.rename_container()."""
@@ -905,6 +963,9 @@ class Video(GenericMedia):
         #   it's marked as a favourite if the same IV in the parent channel,
         #   playlist or folder (also in the parent's parent, and so on) is True
         self.fav_flag = False
+        # Flag set to True if the video is archived, meaning that it can't be
+        #   auto-deleted (but it can still be deleted manually by the user)
+        self.archive_flag = False
 
         # The file's directory, name and extension
         self.file_dir = None
@@ -1019,6 +1080,13 @@ class Video(GenericMedia):
 
     # Set accessors
 
+    def set_archive_flag(self, flag):
+
+        if flag:
+            self.archive_flag = True
+        else:
+            self.archive_flag = False
+
 
     def set_dl_flag(self, flag=False):
 
@@ -1303,10 +1371,27 @@ class Channel(GenericRemoteContainer):
         # Download source (a URL)
         self.source = None
 
+        # Alternative download destination - the dbid of a channel, playlist or
+        #   folder in whose directory videos, thumbnails (etc) are downloaded.
+        #   By default, set to the dbid of this channel; but can be set to the
+        #   dbid of any other channel/playlist/folder
+        # Used for: (1) adding a channel and its playlists to the Tartube
+        #   database, so that duplicate videos don't exist on the user's
+        #   filesystem, (2) tying together, for example, a YouTube and a
+        #   BitChute account, so that duplicate videos don't exist on the
+        #   user's filesystem
+        self.master_dbid = dbid
+        # A list of dbids for any channel, playlist or folder that uses this
+        #   channel as its alternative destination
+        self.slave_dbid_list = []
+
         # Flag set to True if Tartube should always simulate the download of
         #   videos in this channel, or False if the downloads.DownloadManager
         #   object should decide whether to simulate, or not
         self.dl_sim_flag = False
+        # Flag set to True if this channel should never be checked or
+        #   downloaded
+        self.dl_disable_flag = False
         # Flag set to True if this channel is marked as favourite, meaning
         #   that all child video objects are automatically marked as
         #   favourites
@@ -1440,10 +1525,27 @@ class Playlist(GenericRemoteContainer):
         # Download source (a URL)
         self.source = None
 
+        # Alternative download destination - the dbid of a channel, playlist or
+        #   folder in whose directory videos, thumbnails (etc) are downloaded.
+        #   By default, set to the dbid of this playlist; but can be set to the
+        #   dbid of any other channel/playlist/folder
+        # Used for: (1) adding a channel and its playlists to the Tartube
+        #   database, so that duplicate videos don't exist on the user's
+        #   filesystem, (2) tying together, for example, a YouTube and a
+        #   BitChute account, so that duplicate videos don't exist on the
+        #   user's filesystem
+        self.master_dbid = dbid
+        # A list of dbids for any channel, playlist or folder that uses this
+        #   playlist as its alternative destination
+        self.slave_dbid_list = []
+        
         # Flag set to True if Tartube should always simulate the download of
         #   videos in this playlist, or False if the downloads.DownloadManager
         #   object should decide whether to simulate, or not
         self.dl_sim_flag = False
+        # Flag set to True if this playlist should never be checked or
+        #   downloaded
+        self.dl_disable_flag = False
         # Flag set to True if this playlist is marked as favourite, meaning
         #   that all child video objects are automatically marked as
         #   favourites
@@ -1592,6 +1694,21 @@ class Folder(GenericContainer):
         #   folder can't be changed
         self.nickname = name
 
+        # Alternative download destination - the dbid of a channel, playlist or
+        #   folder in whose directory videos, thumbnails (etc) are downloaded.
+        #   By default, set to the dbid of this folder; but can be set to the
+        #   dbid of any other channel/playlist/folder
+        # Used for: (1) adding a channel and its playlists to the Tartube
+        #   database, so that duplicate videos don't exist on the user's
+        #   filesystem, (2) tying together, for example, a YouTube and a
+        #   BitChute account, so that duplicate videos don't exist on the
+        #   user's filesystem
+        # NB Fixed folders cannot have an alternative download destination
+        self.master_dbid = dbid
+        # A list of dbids for any channel, playlist or folder that uses this
+        #   folder as its alternative destination
+        self.slave_dbid_list = []
+        
         # Flag set to False if the folder can be deleted by the user, or True
         #   if it can't be deleted by the user
         self.fixed_flag = fixed_flag
@@ -1611,6 +1728,10 @@ class Folder(GenericContainer):
         #   videos in this folder, or False if the downloads.DownloadManager
         #   object should decide whether to simulate, or not
         self.dl_sim_flag = False
+        # Flag set to True if this folder should never be checked or
+        #   downloaded. If True, the setting applies to any descendant
+        #   channels, playlists and folders
+        self.dl_disable_flag = False        
         # Flag set to True if this folder is hidden (not visible in the Video
         #   Index). Note that only folders can be hidden; channels and
         #   playlists cannot
diff --git a/tartube/options.py b/tartube/options.py
index fd8a296..de3987b 100755
--- a/tartube/options.py
+++ b/tartube/options.py
@@ -268,6 +268,13 @@ class OptionsManager(object):
             temporary directories.
 
             The same applies to the JSON and thumbnail files.
+
+        use_fixed_folder (str or None): If not None, then all videos are
+            downloaded to one of Tartube's fixed folders (not including private
+            folders) - currently, that group consists of only 'Temporary
+            Videos' and 'Unsorted Videos'. The value should match the name of
+            the folder
+        
     """
 
 
@@ -371,6 +378,7 @@ class OptionsManager(object):
            'sim_keep_description': False,
            'sim_keep_info': False,
            'sim_keep_thumbnail': True,
+           'use_fixed_folder': None,
         }
 
 
@@ -518,6 +526,7 @@ class OptionsParser(object):
 #           OptionHolder('sim_keep_description', '', False),
 #           OptionHolder('sim_keep_info', '', False),
 #           OptionHolder('sim_keep_thumbnail', '', False),
+#           OptionHolder('use_fixed_folder', '', None),
         ]
 
 
@@ -721,16 +730,31 @@ class OptionsParser(object):
         """
 
         # Set the directory in which any downloaded videos will be saved
+        app_obj = self.download_manager_obj.app_obj
         media_data_obj = download_item_obj.media_data_obj
-        if isinstance(media_data_obj, media.Video):
-            save_path = media_data_obj.parent_obj.get_dir(
-                self.download_manager_obj.app_obj
-            )
+        override_name = copy_dict['use_fixed_folder']
 
+        if not isinstance(media_data_obj, media.Video) \
+        and override_name is not None \
+        and override_name in app_obj.media_name_dict:
+
+            # Because of the override, save all videos to a fixed folder
+            other_dbid = app_obj.media_name_dict[override_name]
+            other_obj = app_obj.media_reg_dict[other_dbid]
+            save_path = other_obj.get_dir(app_obj)
+                        
         else:
-            save_path = media_data_obj.get_dir(
-                self.download_manager_obj.app_obj
-            )
+            
+            if isinstance(media_data_obj, media.Video):
+                save_path = media_data_obj.parent_obj.get_dir(
+                    self.download_manager_obj.app_obj
+                )
+
+            else:
+                save_path = media_data_obj.get_dir(
+                    self.download_manager_obj.app_obj
+                )
+
 
         # Set the youtube-dl output template for the video's file
         template = formats.FILE_OUTPUT_CONVERT_DICT[copy_dict['output_format']]
diff --git a/tartube/tartube b/tartube/tartube
index 587350e..2409da2 100755
--- a/tartube/tartube
+++ b/tartube/tartube
@@ -35,8 +35,8 @@ import mainapp
 
 # 'Global' variables
 __packagename__ = 'tartube'
-__version__ = '1.1.015'
-__date__ = '22 Aug 2019'
+__version__ = '1.1.050'
+__date__ = '26 Aug 2019'
 __copyright__ = 'Copyright \xa9 2019 A S Lewis'
 __license__ = """
 Copyright \xc2\xa9 2019 A S Lewis.
@@ -64,7 +64,7 @@ __app_id__ = 'io.sourceforge.tartube'
 # This is the default executable, in which youtube-dl updates are enabled. For
 #   Debian packaging, use the 'tartube_debian' executable (see the comments in
 #   setup.py)
-__disable_ytdl_update_flag__ = False
+__debian_install_flag__ = False
 # Tartube's icons are stored in the ../icons directory. If packagers want to
 #   move them somewhere else, then adding the directory path to this list will
 #   tell mainwin.MainWin.setup_pixbufs() how to find them
diff --git a/tartube/tartube_debian b/tartube/tartube_debian
index 25bb198..79b40ea 100755
--- a/tartube/tartube_debian
+++ b/tartube/tartube_debian
@@ -35,8 +35,8 @@ import mainapp
 
 # 'Global' variables
 __packagename__ = 'tartube'
-__version__ = '1.1.015'
-__date__ = '22 Aug 2019'
+__version__ = '1.1.050'
+__date__ = '26 Aug 2019'
 __copyright__ = 'Copyright \xa9 2019 A S Lewis'
 __license__ = """
 Copyright \xc2\xa9 2019 A S Lewis.
@@ -64,7 +64,7 @@ __app_id__ = 'io.sourceforge.tartube'
 # This is a modified executable, in which youtube-dl updates are disabled
 #   (to meet the demands of Debian packaging). All other users should run the
 #   'tartube' executable (see the comments in setup.py)
-__disable_ytdl_update_flag__ = True
+__debian_install_flag__ = True
 # Tartube's icons are stored in the ../icons directory. If packagers want to
 #   move them somewhere else, then adding the directory path to this list will
 #   tell mainwin.MainWin.setup_pixbufs() how to find them