Git #271/281/288, add newbie dialogue

This commit is contained in:
A S Lewis 2021-05-07 16:50:11 +01:00
parent ed49d0d427
commit d9daa9b7aa
33 changed files with 14108 additions and 5092 deletions

View File

@ -49,10 +49,10 @@ Problems can be reported at `our GitHub page <https://github.com/axcore/tartube/
- Videos can now be displayed in a grid. This is the default mode for new installations. Existing users can click the **Switch** button near the top of the main window until they see the layout they prefer
- Livestream detection has been improved. You can now see the approximate start time of a livestream. Pre-recorded videos, which are released at a pre-determined time as if they were livestreams, are now shown in a different colour
- You can now create as many scheduled downloads as you like (earlier versions resticted you to just three; see `6.12 Scheduled downloads`_)
- **Tartube** includes a new GUI for processing videos and thumbnails with FFmpeg, replacing the simple dialogue window in the previous release (see `6.24.2 Using FFmpeg directly`_)
- **Tartube** includes a new GUI for processing videos and thumbnails with FFmpeg, replacing the simple dialogue window in the previous release (see `6.25.2 Using FFmpeg directly`_)
- You can now create as many sets of download options as you like, and as many sets of FFmpeg options as you like. They can now imported and exported between databases (see `6.11.3 Managing download options`_)
- The new **Hide** button at the top of the main window is a convenient way to hide system folders you don't need
- **Tartube** now supports `Youtube Stream Capture <https://github.com/mrwnwttk/youtube_stream_capture>`__, which can be used to download a livestream while it is broadcasting. This feature is **experimental**. It only works on **YouTube**. It doesn't work on MS Windows. It can't download continuous 24/7 livestreams. It might not be able to download some other livestreams. The feature is disabled by default. If you want to try it, see see `6.22.5 Youtube Stream Capture`_
- **Tartube** now supports `Youtube Stream Capture <https://github.com/mrwnwttk/youtube_stream_capture>`__, which can be used to download a livestream while it is broadcasting. This feature is **experimental**. It only works on **YouTube**. It doesn't work on MS Windows. It can't download continuous 24/7 livestreams. It might not be able to download some other livestreams. The feature is disabled by default. If you want to try it, see see `6.23.5 Youtube Stream Capture`_
For a full list of new features and fixes, see `recent changes <CHANGES>`__.
@ -411,26 +411,27 @@ The procedure used to create the MS Windows installers is described in full in t
* `6.17.2 Combining channels from different websites`_
* `6.17.3 Download all videos to a single folder`_
* `6.18 Archiving videos`_
* `6.19 Managing databases`_
* `6.19.1 Importing videos from other applications`_
* `6.19.2 Multiple databases`_
* `6.19.3 Multiple Tartubes`_
* `6.19.4 Exporting/importing the database`_
* `6.20 Converting to audio`_
* `6.21 Classic Mode`_
* `6.21.1 Customising Classic Mode`_
* `6.22 Livestreams`_
* `6.22.1 Detecting livestreams`_
* `6.22.2 Customising livestreams`_
* `6.22.3 Livestream notifications`_
* `6.22.4 Compatible websites`_
* `6.23 Detecting missing videos`_
* `6.24 More information about FFmpeg and AVConv`_
* `6.24.1 Using FFmpeg / AVConv with youtube-dl`_
* `6.24.2 Using FFmpeg directly`_
* `6.24.3 Using FFmpeg options`_
* `6.24.4 Advanced FFmpeg options`_
* `6.25 Using youtube-dl forks`_
* `6.19 Performance limits`_
* `6.20 Managing databases`_
* `6.20.1 Importing videos from other applications`_
* `6.20.2 Multiple databases`_
* `6.20.3 Multiple Tartubes`_
* `6.20.4 Exporting/importing the database`_
* `6.21 Converting to audio`_
* `6.22 Classic Mode`_
* `6.22.1 Customising Classic Mode`_
* `6.23 Livestreams`_
* `6.23.1 Detecting livestreams`_
* `6.23.2 Customising livestreams`_
* `6.23.3 Livestream notifications`_
* `6.23.4 Compatible websites`_
* `6.24 Detecting missing videos`_
* `6.25 More information about FFmpeg and AVConv`_
* `6.25.1 Using FFmpeg / AVConv with youtube-dl`_
* `6.25.2 Using FFmpeg directly`_
* `6.25.3 Using FFmpeg options`_
* `6.25.4 Advanced FFmpeg options`_
* `6.26 Using youtube-dl forks`_
6.1 Setting up Tartube
----------------------
@ -442,7 +443,7 @@ When you first start **Tartube**, you will be asked to choose a few settings.
Most users can use this window to download and install some packages. If not, those packages must be installed separately.
All of these settings can be changed later, if you want. For example, to change where **Tartube** stores its files, see `6.19 Managing databases`_.
All of these settings can be changed later, if you want. For example, to change where **Tartube** stores its files, see `6.20 Managing databases`_.
6.2 Updating the downloader
---------------------------
@ -489,7 +490,7 @@ On other systems, users can modify **Tartube**'s settings. There are several loc
**youtube-dl** uses FFmpeg by default, but it can use AVConv for certain tasks.
For more information about **Tartube**'s use of Ffmpeg and AVConv, see `6.24 More information about FFmpeg and AVConv`_.
For more information about **Tartube**'s use of Ffmpeg and AVConv, see `6.25 More information about FFmpeg and AVConv`_.
6.4.1 On MS Windows
~~~~~~~~~~~~~~~~~~~
@ -529,7 +530,7 @@ When you start **Tartube** for the first time, there are several folders already
- The **Bookmarks** folder shows videos you've bookmarked, because they're interesting or important (see `6.16.1 Bookmarked videos`_ )
- The **Favourite Videos** folder shows videos in a channel, playlist or folder that you've marked as favourite videos (see `6.16.2 Favourite channels, playlists and folders`_ )
- The **Livestreams** folder shows livestreams. Videos are automatically removed from this folder (but not from other folders) when the livestream is finished
- The **Missing videos** folder (see `6.23 Detecting missing videos`_ ) shows videos that you've downloaded, but which have since been removed from the website by their creator
- The **Missing videos** folder (see `6.24 Detecting missing videos`_ ) shows videos that you've downloaded, but which have since been removed from the website by their creator
- The **New Videos** folder shows videos that have been downloaded, but not yet watched
- The **Recent Videos** folder shows videos that were checked or downloaded, the last time you used **youtube-dl**
- The **Waiting Videos** folder shows videos that you want to watch soon. When you watch the video, it's automatically removed from the folder (but not from **Tartube**'s database)
@ -539,7 +540,7 @@ When you start **Tartube** for the first time, there are several folders already
6.6 Adding videos
-----------------
*If you want a simpler way to download videos, see* `6.21 Classic Mode`_.
*If you want a simpler way to download videos, see* `6.22 Classic Mode`_.
You can add individual videos by clicking the **Add new video(s)** button near the top of the window. A dialogue window will appear.
@ -642,12 +643,12 @@ Tidying up the filesytem:
Dealing with livestreams:
- **Livestream** - Checks whether any livestreams have started (or stopped), without fetching the full list of videos
- **Livestream capture** - When available, downloads a livestream that's broadcasting now; see `6.22.5 Youtube Stream Capture`_
- **Livestream capture** - When available, downloads a livestream that's broadcasting now; see `6.23.5 Youtube Stream Capture`_
- A **Livestream** check happens every few minutes (if **Tartube** detected livestreams during a **Check** or a **Download**). To force a check now, click **Livestreams > Update existing livestreams**
Processing videos with **FFmpeg**:
- **Process** - Processes videos and thumbnails with FFmpeg - see `6.24 More information about FFmpeg and AVConv`_
- **Process** - Processes videos and thumbnails with FFmpeg - see `6.25 More information about FFmpeg and AVConv`_
- To process video(s) and/or their thumbnails, right-click a video and select **Process with FFmpeg...**
6.11 Download options
@ -713,9 +714,9 @@ In fact, you can create as many sets of download options as you like.
.. image:: screenshots/example17.png
:alt: The list of download options
The first item in the list, **general**, is the default set of download options. The second item, **classic**, are the download options that apply in the **Classic Mode** Tab (see `6.21 Classic Mode`_).
The first item in the list, **general**, is the default set of download options. The second item, **classic**, are the download options that apply in the **Classic Mode** Tab (see `6.22 Classic Mode`_).
Download options are saved in the Tartube database, so if you switch databases (see `6.19.2 Multiple databases`_), a different selection of download options will apply. If you want to move a set of download options from one database to another, you can **Export** them, then switch databases, then **Import** them.
Download options are saved in the Tartube database, so if you switch databases (see `6.20.2 Multiple databases`_), a different selection of download options will apply. If you want to move a set of download options from one database to another, you can **Export** them, then switch databases, then **Import** them.
6.12 Scheduled downloads
------------------------
@ -968,7 +969,27 @@ You can also archive all the videos in a channel, playlist or folder.
- This action applies to *all* videos that are *currently* in the folder, including the contents of any channels and playlists in that folder
- It doesn't apply to any videos you might download in the future
6.19 Managing databases
6.19 Performance limits
-----------------------
By default, **Tartube** downloads two video, channels or playlists at a time, as quickly as possible (in other words, without bandwidth limits).
You can change this behaviour in the **Progress** tab, if you want.
- At the bottom of the tab, select the **Max downloads** button, and change the maximum number of simultaneous downloads
- Alternatively, select the **D/L speed** button, and set the maximum bandwidth you're willing to allocate to **Tartube**
These are the default settings. Many users might want lower download speeds during the day, but higher download speeds at night (and so on).
- Click **Edit > System preferences... > Operations > Limits**
- The settings in the top half of the tab are the ones visible in the **Progress** tab
- The settings in the bottom half of the tab apply only during certain times of the day, and on certain days
There is a third way to change **Tartube**'s behaviour. The maximum downloads and bandwidth limits can also be set for a scheduled download (see `6.12 Scheduled downloads`_).
**Tartube** honours most requests to change the maximum downloads and the bandwidth limit, so it's not a good idea to set lots of different values.
6.20 Managing databases
-----------------------
**Tartube** downloads all of its videos into a single directory (folder) - the **Tartube data directory**. The contents of this directory comprise the **Tartube database**.
@ -979,7 +1000,7 @@ You can also archive all the videos in a channel, playlist or folder.
It's fine to add new videos to the database, or to remove them. Just be careful that you don't delete any sub-directories (folders), including those which are hidden, and don't modify the **Tartube** database file, **tartube.db**.
6.19.1 Importing videos from other applications
6.20.1 Importing videos from other applications
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tartube** is a GUI front-end for **youtube-dl**, but it is not the only one. If you've downloaded videos using another application, this is how to add them to **Tartube**'s database.
@ -990,7 +1011,7 @@ It's fine to add new videos to the database, or to remove them. Just be careful
- In the **Tartube** menu, click **Operations > Refresh database...**. **Tartube** will search for video files, and try to match them with the list of videos you just compiled
- The whole process might some time, so be patient
6.19.2 Multiple databases
6.20.2 Multiple databases
~~~~~~~~~~~~~~~~~~~~~~~~~
**Tartube** can only use one database at a time, but you can create as many as you want.
@ -1007,7 +1028,7 @@ For example, you could create a new database on an external hard drive.
- In the list, click the original database to select it
- Click the **Switch** button
6.19.3 Multiple Tartubes
6.20.3 Multiple Tartubes
~~~~~~~~~~~~~~~~~~~~~~~~
**Tartube** can't load more than one database, but you can run as many instances of **Tartube** as you want.
@ -1016,7 +1037,7 @@ If you have added three databases to the list, and if you have three **Tartube**
By default, the databases are loaded in the order they appear in the list.
6.19.4 Exporting/importing the database
6.20.4 Exporting/importing the database
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can export the contents of **Tartube**'s database and, at any time in the future, import that information into a different **Tartube** database, perhaps on a different computer.
@ -1036,7 +1057,7 @@ This is how to import the data into a different **Tartube** database.
- Select the export file you created earlier
- A dialogue window will appear. You can choose how much of the database you want to import
6.20 Converting to audio
6.21 Converting to audio
------------------------
**Tartube** can automatically extract the audio from its downloaded videos, if that's what you want.
@ -1069,7 +1090,7 @@ Some websites, such as **YouTube**, allow you to download the audio (in **.m4a**
- Click the **Add format >>>** button to add it to the list on the right
- Click the **OK** button at the bottom of the window to apply your changes
6.21 Classic Mode
6.22 Classic Mode
-----------------
**Tartube** compiles a database of the videos, channels and playlists it has downloaded.
@ -1090,7 +1111,7 @@ If you want something simpler, then click the **Classic Mode** Tab, which has an
Because the videos aren't in a database, you can move them anywhere you want (once you've finished downloading them).
6.21.1 Customising Classic Mode
6.22.1 Customising Classic Mode
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you *only* use this tab, you can tell **Tartube** to open it automatically.
@ -1114,14 +1135,14 @@ In the same menu, custom downloads can be enabled (see `6.13 Custom downloads`_)
In the bottom half of the window, you can select one or more URLs by clicking them. The buttons in the bottom-left apply to the selected URLs. Let your mouse hover over a button, to see what it does.
6.22 Livestreams
6.23 Livestreams
----------------
**Tartube** can detect livestreams, and to notify you when they start.
At the moment, this feature only works on **YouTube**, and it doesn't work at all on 32-bit MS Windows.
6.22.1 Detecting livestreams
6.23.1 Detecting livestreams
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tartube** searches for livestreams whenever you check or download channels and playlists.
@ -1135,7 +1156,7 @@ Livestreams are easy to spot. A livestream that hasn't started yet has a red bac
Every few minutes, **Tartube** checks whether a livestream (or debut) has started or stopped. This happens automatically in the background; there is no need for you to do anything.
6.22.2 Customising livestreams
6.23.2 Customising livestreams
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can modify how often livestreams are checked (and whether they are checked at all). Click **Livestreams > Livestream preferences...**.
@ -1149,7 +1170,7 @@ By default, **Tartube** checks a livestream every three minutes, waiting for it
If you want to force a check, in the main window click **Livestreams > Update existing livestreams**. (Checks are silent, so don't worry if nothing seems to be happening).
6.22.3 Livestream notifications
6.23.3 Livestream notifications
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's really useful to be notified when a livestream is starting. In the same window, click the **Actions** tab.
@ -1173,9 +1194,9 @@ Most users will prefer to leave all of these checkboxes unselected, and instead
To disable any of these actions, simply click the same label again.
**NOTE:** At the time of writing (December 2020), **youtube-dl** often fails to download **YouTube** livestreams while they are broadcasting. For an alternative download method, see `6.22.5 Youtube Stream Capture`_. **youtube-dl** will often download livestreams from other websites successfully.
**NOTE:** At the time of writing (December 2020), **youtube-dl** often fails to download **YouTube** livestreams while they are broadcasting. For an alternative download method, see `6.23.5 Youtube Stream Capture`_. **youtube-dl** will often download livestreams from other websites successfully.
6.22.4 Compatible websites
6.23.4 Compatible websites
~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tartube**'s livestream detection has only been tested on **YouTube**. It's possible that it might work on other websites, if they behave in the same way. Here is how to set it up.
@ -1186,7 +1207,7 @@ Secondly, right-click the channel and select **Show > Channel properties...** (a
Now click the **RSS feed** tab. Enter the link (URL) to the RSS feed in the box. Click the **OK** button to close the window.
6.22.5 Youtube Stream Capture
6.23.5 Youtube Stream Capture
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tartube** v2.3.0 adds support for `Youtube Stream Capture <https://github.com/mrwnwttk/youtube_stream_capture>`__ (YTSC), a script that can download livestreams from **YouTube** independently of **youtube-dl**.
@ -1204,7 +1225,7 @@ Likewise, the download might continue after the livestream has stopped broadcast
YTSC downloads a livestream in segments. At the end of the download, the segments must be merged into a single video. Again, this might take several minutes.
6.23 Detecting missing videos
6.24 Detecting missing videos
-----------------------------
**Tartube** can detect videos you have downloaded, but which have been since deleted by the original uploader.
@ -1216,10 +1237,10 @@ Having enabled detection, removed videos will appear in the **Missing Videos** f
**Tartube** only detects missing videos when checking/downloading whole channels or playlists. If you interrupt a download, no detection occurs.
6.24 More information about FFmpeg and AVConv
6.25 More information about FFmpeg and AVConv
---------------------------------------------
6.24.1 Using FFmpeg / AVConv with youtube-dl
6.25.1 Using FFmpeg / AVConv with youtube-dl
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you explicitly set the location of the **FFmpeg** and/or **AVConv** executables, then those locations are passed on to **youtube-dl** when you check or download videos.
@ -1235,7 +1256,7 @@ If *both* locations are set, only one of them is passed on. Usually, that's the
For more information about download options, see `6.11 Download options`_.
6.24.2 Using FFmpeg directly
6.25.2 Using FFmpeg directly
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can process videos and thumbnails with **FFmpeg** directly, if you need to. This is useful for converting a file from one format to another, and for many other tasks.
@ -1258,7 +1279,7 @@ The box at the top allows you to add FFmpeg options directly. For example, to co
**-r 24**
6.24.3 Using FFmpeg options
6.25.3 Using FFmpeg options
~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the options windows, click the **File** tab.
@ -1269,7 +1290,7 @@ The **Videos** tab contains an optional list of videos. These are the videos tha
You can add videos to this list by dragging and dropping them. Dragging from an external application is allowed, if the videos are also visible somewhere in **Tartube**'s main window (for example, in its database, or in the **Classic Mode** tab).
6.24.4 Advanced FFmpeg options
6.25.4 Advanced FFmpeg options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now click the **Name** tab again. One box shows the **FFmpeg** system command that will be used to process the videos.
@ -1289,7 +1310,7 @@ When youtube-dl downloads a video, it often downloads the video and audio compon
If the *source file* is a thumbnail, then the *output file* must also be a thumbnail.
6.25 Using youtube-dl forks
6.26 Using youtube-dl forks
---------------------------
`youtube-dl <https://youtube-dl.org/>`__ is open-source software, and there are a number of forks available. Tartube officially supports both the original version and `yt-dlp <https://github.com/yt-dlp/yt-dlp>`__.
@ -1544,7 +1565,7 @@ A: Tartube can create files with names in different formats. The name of two vid
*Q: I want to convert the video files to audio files!*
A: See `6.20 Converting to audio`_
A: See `6.21 Converting to audio`_
7.14 Video is downloaded as separate video/audio files
------------------------------------------------------
@ -1564,7 +1585,7 @@ For some reason, youtube-dl ignores the download option unless the format is spe
.. image:: screenshots/example32.png
:alt: The Download options window
Tartube can merge a video and audio file together, long after they have been downloaded - see `6.24 More information about FFmpeg and AVConv`_.
Tartube can merge a video and audio file together, long after they have been downloaded - see `6.25 More information about FFmpeg and AVConv`_.
7.15 Too many folders in the main window
----------------------------------------
@ -1641,7 +1662,7 @@ A: Change your IP address and try again.
A: Subscribe to a `VPN <https://en.wikipedia.org/wiki/Virtual_private_network>`__.
A: You can specify a list of proxies (Edit > System preferences... > Operations > Proxies). During a download operation, **Tartube** will cycle between these proxies.
A: You can specify a list of proxies (**Edit > System preferences... > Operations > Proxies**). During a download operation, **Tartube** will cycle between these proxies.
Unfortunately, it is not possible to switch between proxies while downloading a channel (youtube-dl does not offer that functionality). But the proxy list will work well if you're trying to download ten different channels.

View File

@ -1 +1 @@
2.3.110
2.3.120

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
icons/large/limits_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
icons/large/limits_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
# Tartube v2.3.110 installer script for MS Windows
# Tartube v2.3.120 installer script for MS Windows
#
# Copyright (C) 2019-2021 A S Lewis
#
@ -249,7 +249,7 @@
;Name and file
Name "Tartube"
OutFile "install-tartube-2.3.110-32bit.exe"
OutFile "install-tartube-2.3.120-32bit.exe"
;Default installation folder
InstallDir "$LOCALAPPDATA\Tartube"
@ -352,7 +352,7 @@ Section "Tartube" SecClient
# "Publisher" "A S Lewis"
# WriteRegStr HKLM \
# "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \
# "DisplayVersion" "2.3.110"
# "DisplayVersion" "2.3.120"
# Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"

View File

@ -1,4 +1,4 @@
# Tartube v2.3.110 installer script for MS Windows
# Tartube v2.3.120 installer script for MS Windows
#
# Copyright (C) 2019-2021 A S Lewis
#
@ -249,7 +249,7 @@
;Name and file
Name "Tartube"
OutFile "install-tartube-2.3.110-64bit.exe"
OutFile "install-tartube-2.3.120-64bit.exe"
;Default installation folder
InstallDir "$LOCALAPPDATA\Tartube"
@ -352,7 +352,7 @@ Section "Tartube" SecClient
# "Publisher" "A S Lewis"
# WriteRegStr HKLM \
# "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \
# "DisplayVersion" "2.3.110"
# "DisplayVersion" "2.3.120"
# Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.110'
__date__ = '28 Feb 2021'
__version__ = '2.3.120'
__date__ = '7 May 2021'
__copyright__ = 'Copyright \xa9 2019-2021 A S Lewis'
__license__ = """
Copyright \xa9 2019-2021 A S Lewis.

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.110'
__date__ = '28 Feb 2021'
__version__ = '2.3.120'
__date__ = '7 May 2021'
__copyright__ = 'Copyright \xa9 2019-2021 A S Lewis'
__license__ = """
Copyright \xa9 2019-2021 A S Lewis.

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.110'
__date__ = '28 Feb 2021'
__version__ = '2.3.120'
__date__ = '7 May 2021'
__copyright__ = 'Copyright \xa9 2019-2021 A S Lewis'
__license__ = """
Copyright \xa9 2019-2021 A S Lewis.

View File

@ -1,4 +1,4 @@
.TH man 1 "28 Feb 2021" "2.3.110" "tartube man page"
.TH man 1 "7 May 2021" "2.3.120" "tartube man page"
.SH NAME
tartube \- GUI front-end for youtube-dl
.SH SYNOPSIS

View File

@ -1,6 +1,6 @@
[Desktop Entry]
Name=Tartube
Version=2.3.110
Version=2.3.120
Exec=tartube
Icon=tartube
Type=Application

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -182,7 +182,7 @@ for path in glob.glob('sounds/*'):
# Setup
setuptools.setup(
name='tartube',
version='2.3.110',
version='2.3.120',
description='GUI front-end for youtube-dl',
long_description=long_description,
long_description_content_type='text/plain',

View File

@ -9682,6 +9682,7 @@ class ScheduledEditWin(GenericEditWin):
self.setup_general_tab()
self.setup_media_tab()
self.setup_limits_tab()
def setup_general_tab(self):
@ -9994,6 +9995,70 @@ class ScheduledEditWin(GenericEditWin):
liststore.append([name])
def setup_limits_tab(self):
"""Called by self.setup_tabs().
Sets up the 'Limits' tab.
"""
tab, grid = self.add_notebook_tab(_('_Limits'))
grid_width = 3
self.add_label(grid,
'<u>' + _('Performance limits') + '</u>',
0, 0, grid_width, 1,
)
self.add_label(grid,
'<i>' + _('Limits are applied when you start downloading a' \
+ ' video/channel/playlist') + '</i>',
0, 1, grid_width, 1,
)
self.add_label(grid,
'<i>' + _('These limits override the default and alternative' \
+ ' limits specified elsewhere') + '</i>',
0, 2, grid_width, 1,
)
checkbutton = self.add_checkbutton(grid,
_('Limit simultaneous downloads to'),
'scheduled_num_worker_apply_flag',
0, 3, 1, 1,
)
checkbutton.set_hexpand(False)
spinbutton = self.add_spinbutton(grid,
self.app_obj.num_worker_min,
self.app_obj.num_worker_max,
1, # Step
'scheduled_num_worker',
1, 3, 1, 1,
)
checkbutton2 = self.add_checkbutton(grid,
_('Limit download speed to'),
'scheduled_bandwidth_apply_flag',
0, 4, 1, 1,
)
checkbutton2.set_hexpand(False)
spinbutton2 = self.add_spinbutton(grid,
self.app_obj.bandwidth_min,
self.app_obj.bandwidth_max,
1, # Step
'scheduled_bandwidth',
1, 4, 1, 1,
)
self.add_label(grid,
'KiB/s',
2, 3, 1, 1,
)
# Callback class methods
@ -10244,11 +10309,11 @@ class SystemPrefWin(GenericPrefWin):
app_obj (mainapp.TartubeApp): The main application object
init_mode (str or None): 'db' to open the tab with options for
switching the Tartube database, 'paths' to open the tab with
youtube-dl paths, 'live' to open the tab with livestream options,
'options' to open the tab with the list of download options,
'custom_dl' to open the tab with custom download preferences. Any
other value is ignored
switching the Tartube database, 'forks' to open the tab with
youtube-dl forks, 'paths' to open the tab with youtube-dl paths,
'live' to open the tab with livestream options, 'options' to open
the tab with the list of download options, 'custom_dl' to open the
tab with custom download preferences. Any other value is ignored
"""
@ -10273,6 +10338,9 @@ class SystemPrefWin(GenericPrefWin):
# ---------------------
self.grid = None # Gtk.Grid
self.notebook = None # Gtk.Notebook
self.files_inner_notebook = None # Gtk.Notebook
self.operations_inner_notebook = None # Gtk.Notebook
self.downloader_inner_notebook = None # Gtk.Notebook
self.ok_button = None # Gtk.Button
# (IVs used to handle widget changes in the 'General' tab)
self.radiobutton = None # Gtk.RadioButton
@ -10329,11 +10397,12 @@ class SystemPrefWin(GenericPrefWin):
app_obj (mainapp.TartubeApp): The main application object
init_mode (str or None): 'db' to open the tab with options for
switching the Tartube database, 'paths' to open the tab with
youtube-dl paths, 'live' to open the tab with livestream
options, 'options' to open the tab with the list of download
options, 'custom_dl' to open the tab with custom download
preferences. Any other value is ignored
switching the Tartube database, 'forks' to open the tab with
youtube-dl forks, 'paths' to open the tab with youtube-dl
paths, 'live' to open the tab with livestream options,
'options' to open the tab with the list of download options,
'custom_dl' to open the tab with custom download preferences.
Any other value is ignored
Return values:
@ -10383,11 +10452,11 @@ class SystemPrefWin(GenericPrefWin):
Args:
init_mode (str): 'db' to open the tab with options for switching
the Tartube database, 'paths' to open the tab with youtube-dl
paths, 'live' to open the tab with livestream options,
'options' to open the tab with the list of download options,
'custom_dl' to open the tab with custom download preferences.
Any other value is ignored
the Tartube database, 'forks' to open the tab with youtube-dl
forks, 'paths' to open the tab with youtube-dl paths, 'live' to
open the tab with livestream options, 'options' to open the tab
with the list of download options, 'custom_dl' to open the tab
with custom download preferences. Any other value is ignored
"""
@ -10395,6 +10464,8 @@ class SystemPrefWin(GenericPrefWin):
if init_mode == 'db':
self.select_switch_db_tab()
elif init_mode == 'forks':
self.select_forks_tab()
elif init_mode == 'paths':
self.select_paths_tab()
elif init_mode == 'live':
@ -10405,6 +10476,18 @@ class SystemPrefWin(GenericPrefWin):
self.select_custom_dl_tab()
def select_forks_tab(self):
"""Can be called by anything.
Makes the visible tab the one on which the user can set youtube-dl
forks.
"""
self.notebook.set_current_page(5)
self.downloader_inner_notebook.set_current_page(0)
def select_switch_db_tab(self):
"""Can be called by anything.
@ -10422,10 +10505,11 @@ class SystemPrefWin(GenericPrefWin):
"""Can be called by anything.
Makes the visible tab the one on which the user can set youtube-dl
options.
paths.
"""
self.notebook.set_current_page(5)
self.downloader_inner_notebook.set_current_page(1)
def select_livestream_tab(self):
@ -12284,76 +12368,87 @@ class SystemPrefWin(GenericPrefWin):
self.on_operation_warning_button_toggled,
)
checkbutton5 = self.add_checkbutton(grid,
_('Show dates of all messages'),
self.app_obj.system_msg_show_date_flag,
True, # Can be toggled by user
0, 3, 1, 1,
)
checkbutton5.connect(
'toggled',
self.on_system_dates_button_toggled,
)
# Downloader error/warning preferences
self.add_label(grid,
'<u>' + _('Downloader error/warning preferences') + '</u>',
0, 3, 1, 1,
0, 4, 1, 1,
)
translate_note = _(
'TRANSLATOR\'S NOTE: These error messages are always in English',
)
checkbutton5 = self.add_checkbutton(grid,
checkbutton6 = self.add_checkbutton(grid,
_('Ignore \'Child process exited with non-zero code\' errors'),
self.app_obj.ignore_child_process_exit_flag,
True, # Can be toggled by user
0, 4, grid_width, 1,
0, 5, grid_width, 1,
)
checkbutton5.connect('toggled', self.on_child_process_button_toggled)
checkbutton6.connect('toggled', self.on_child_process_button_toggled)
checkbutton6 = self.add_checkbutton(grid,
checkbutton7 = self.add_checkbutton(grid,
_(
'Ignore \'Unable to download video data\' and \'Unable to' \
+ ' extract video data\' errors',
),
self.app_obj.ignore_http_404_error_flag,
True, # Can be toggled by user
0, 5, grid_width, 1,
0, 6, grid_width, 1,
)
checkbutton6.connect('toggled', self.on_http_404_button_toggled)
checkbutton7.connect('toggled', self.on_http_404_button_toggled)
checkbutton7 = self.add_checkbutton(grid,
checkbutton8 = self.add_checkbutton(grid,
_('Ignore \'Did not get any data blocks\' errors'),
self.app_obj.ignore_data_block_error_flag,
True, # Can be toggled by user
0, 6, grid_width, 1,
0, 7, grid_width, 1,
)
checkbutton7.connect('toggled', self.on_data_block_button_toggled)
checkbutton8.connect('toggled', self.on_data_block_button_toggled)
checkbutton8 = self.add_checkbutton(grid,
checkbutton9 = self.add_checkbutton(grid,
_(
'Ignore \'Requested formats are incompatible for merge\' warnings',
),
self.app_obj.ignore_merge_warning_flag,
True, # Can be toggled by user
0, 7, grid_width, 1,
0, 8, grid_width, 1,
)
checkbutton8.connect('toggled', self.on_merge_button_toggled)
checkbutton9.connect('toggled', self.on_merge_button_toggled)
checkbutton9 = self.add_checkbutton(grid,
checkbutton10 = self.add_checkbutton(grid,
_('Ignore \'No video formats found\' errors'),
self.app_obj.ignore_missing_format_error_flag,
True, # Can be toggled by user
0, 8, grid_width, 1,
0, 9, grid_width, 1,
)
checkbutton9.connect('toggled', self.on_missing_format_button_toggled)
checkbutton10.connect('toggled', self.on_missing_format_button_toggled)
checkbutton10 = self.add_checkbutton(grid,
checkbutton11 = self.add_checkbutton(grid,
_('Ignore \'There are no annotations to write\' warnings'),
self.app_obj.ignore_no_annotations_flag,
True, # Can be toggled by user
0, 9, grid_width, 1,
0, 10, grid_width, 1,
)
checkbutton10.connect('toggled', self.on_no_annotations_button_toggled)
checkbutton11.connect('toggled', self.on_no_annotations_button_toggled)
checkbutton11 = self.add_checkbutton(grid,
checkbutton12 = self.add_checkbutton(grid,
_('Ignore \'Video doesn\'t have subtitles\' warnings'),
self.app_obj.ignore_no_subtitles_flag,
True, # Can be toggled by user
0, 10, grid_width, 1,
0, 11, grid_width, 1,
)
checkbutton11.connect('toggled', self.on_no_subtitles_button_toggled)
checkbutton12.connect('toggled', self.on_no_subtitles_button_toggled)
def setup_windows_websites_tab(self, inner_notebook):
@ -12810,7 +12905,8 @@ class SystemPrefWin(GenericPrefWin):
self.operations_inner_notebook = self.add_inner_notebook(grid)
# ...with its own tabs
self.setup_operations_performance_tab(self.operations_inner_notebook)
self.setup_operations_limits_tab(self.operations_inner_notebook)
self.setup_operations_stop_tab(self.operations_inner_notebook)
self.setup_operations_prefs_tab(self.operations_inner_notebook)
self.setup_operations_downloads_tab(self.operations_inner_notebook)
self.setup_operations_custom_tab(self.operations_inner_notebook)
@ -12819,15 +12915,15 @@ class SystemPrefWin(GenericPrefWin):
self.setup_operations_actions_tab(self.operations_inner_notebook)
def setup_operations_performance_tab(self, inner_notebook):
def setup_operations_limits_tab(self, inner_notebook):
"""Called by self.setup_operations_tab().
Sets up the 'Performance' inner notebook tab.
Sets up the 'Limits' inner notebook tab.
"""
tab, grid = self.add_inner_notebook_tab(
_('_Performance'),
_('_Limits'),
inner_notebook,
)
@ -12839,11 +12935,17 @@ class SystemPrefWin(GenericPrefWin):
0, 0, grid_width, 1,
)
self.add_label(grid,
'<i>' + _('Limits are applied when you start downloading a' \
+ ' video/channel/playlist') + '</i>',
0, 1, grid_width, 1,
)
checkbutton = self.add_checkbutton(grid,
_('Limit simultaneous downloads to'),
self.app_obj.num_worker_apply_flag,
True, # Can be toggled by user
0, 1, 1, 1,
0, 2, 1, 1,
)
checkbutton.set_hexpand(False)
checkbutton.connect('toggled', self.on_worker_button_toggled)
@ -12853,7 +12955,7 @@ class SystemPrefWin(GenericPrefWin):
self.app_obj.num_worker_max,
1, # Step
self.app_obj.num_worker_default,
1, 1, 1, 1,
1, 2, 1, 1,
)
spinbutton.connect('value-changed', self.on_worker_spinbutton_changed)
@ -12861,7 +12963,7 @@ class SystemPrefWin(GenericPrefWin):
_('Limit download speed to'),
self.app_obj.bandwidth_apply_flag,
True, # Can be toggled by user
0, 2, 1, 1,
0, 3, 1, 1,
)
checkbutton2.set_hexpand(False)
checkbutton2.connect('toggled', self.on_bandwidth_button_toggled)
@ -12871,7 +12973,7 @@ class SystemPrefWin(GenericPrefWin):
self.app_obj.bandwidth_max,
1, # Step
self.app_obj.bandwidth_default,
1, 2, 1, 1,
1, 3, 1, 1,
)
spinbutton2.connect(
'value-changed',
@ -12880,14 +12982,14 @@ class SystemPrefWin(GenericPrefWin):
self.add_label(grid,
'KiB/s',
2, 2, 1, 1,
2, 3, 1, 1,
)
checkbutton3 = self.add_checkbutton(grid,
_('Overriding video format options, limit video resolution to'),
self.app_obj.video_res_apply_flag,
True, # Can be toggled by user
0, 3, 1, 1,
0, 4, 1, 1,
)
checkbutton3.set_hexpand(False)
checkbutton3.connect('toggled', self.on_video_res_button_toggled)
@ -12895,7 +12997,7 @@ class SystemPrefWin(GenericPrefWin):
combo = self.add_combo(grid,
formats.VIDEO_RESOLUTION_LIST,
None,
1, 3, 1, 1,
1, 4, 1, 1,
)
combo.set_active(
formats.VIDEO_RESOLUTION_LIST.index(
@ -12904,13 +13006,176 @@ class SystemPrefWin(GenericPrefWin):
)
combo.connect('changed', self.on_video_res_combo_changed)
# Alternative performance limits
self.add_label(grid,
'<u>' + _('Alternative performance limits') + '</u>',
0, 5, grid_width, 1,
)
checkbutton4 = self.add_checkbutton(grid,
_('Limit simultaneous downloads to'),
self.app_obj.alt_num_worker_apply_flag,
True, # Can be toggled by user
0, 6, 1, 1,
)
checkbutton4.set_hexpand(False)
checkbutton4.connect('toggled', self.on_worker_button_toggled, True)
spinbutton3 = self.add_spinbutton(grid,
self.app_obj.num_worker_min,
self.app_obj.num_worker_max,
1, # Step
self.app_obj.alt_num_worker,
1, 6, 1, 1,
)
spinbutton3.connect(
'value-changed',
self.on_worker_spinbutton_changed,
True,
)
checkbutton5 = self.add_checkbutton(grid,
_('Limit download speed to'),
self.app_obj.alt_bandwidth_apply_flag,
True, # Can be toggled by user
0, 7, 1, 1,
)
checkbutton5.set_hexpand(False)
checkbutton5.connect('toggled', self.on_bandwidth_button_toggled, True)
spinbutton4 = self.add_spinbutton(grid,
self.app_obj.bandwidth_min,
self.app_obj.bandwidth_max,
1, # Step
self.app_obj.alt_bandwidth,
1, 7, 1, 1,
)
spinbutton4.connect(
'value-changed',
self.on_bandwidth_spinbutton_changed,
True,
)
self.add_label(grid,
'KiB/s',
2, 7, 1, 1,
)
# (Add a second grid, so widget positioning on the first one isn't
# messed up)
grid2 = Gtk.Grid()
grid.attach(grid2, 0, 8, (grid_width - 1), 1)
label = self.add_label(grid2,
_('Alternative limits apply between') + ' ',
0, 0, 1, 1,
)
# (Hours in format '00' to '23')
start_time = self.app_obj.alt_start_time
stop_time = self.app_obj.alt_stop_time
hour_list = []
for h in range(24):
hour_list.append('{:02d}'.format(h))
# (Minutes in format '00', '05', 10' .. '55')
minute_list = []
for n in range(12):
minute_list.append('{:02d}'.format(n*5))
combo2 = self.add_combo(grid2,
hour_list,
None,
1, 0, 1, 1,
)
combo2.set_active(hour_list.index(start_time[0:2]))
combo2.connect('changed', self.on_alt_time_combo_changed, 'start_hour')
label2 = self.add_label(grid2,
' : ',
2, 0, 1, 1,
)
label2.set_hexpand(False)
combo3 = self.add_combo(grid2,
minute_list,
None,
3, 0, 1, 1,
)
combo3.set_active(minute_list.index(start_time[3:5]))
combo3.connect('changed', self.on_alt_time_combo_changed, 'start_min')
label3 = self.add_label(grid2,
' ' + _('and') + ' ',
4, 0, 1, 1,
)
label3.set_hexpand(False)
combo4 = self.add_combo(grid2,
hour_list,
None,
5, 0, 1, 1,
)
combo4.set_active(hour_list.index(stop_time[0:2]))
combo4.connect('changed', self.on_alt_time_combo_changed, 'stop_hour')
label4 = self.add_label(grid2,
' : ',
6, 0, 1, 1,
)
label4.set_hexpand(False)
combo5 = self.add_combo(grid2,
minute_list,
None,
7, 0, 1, 1,
)
combo5.set_active(minute_list.index(stop_time[3:5]))
combo5.connect('changed', self.on_alt_time_combo_changed, 'stop_min')
label5 = self.add_label(grid2,
_('On days') + ' ',
0, 1, 1, 1,
)
combo6_list = []
for s in formats.SPECIFIED_DAYS_LIST:
combo6_list.append( [formats.SPECIFIED_DAYS_DICT[s], s] )
combo6 = self.add_combo_with_data(grid2,
combo6_list,
None,
1, 1, 7, 1,
)
combo6.set_hexpand(False)
combo6.set_active(
formats.SPECIFIED_DAYS_LIST.index(self.app_obj.alt_day_string),
)
combo6.connect('changed', self.on_alt_days_combo_changed)
def setup_operations_stop_tab(self, inner_notebook):
"""Called by self.setup_operations_tab().
Sets up the 'Performance' inner notebook tab.
"""
tab, grid = self.add_inner_notebook_tab(
_('S_top'),
inner_notebook,
)
grid_width = 2
# Time-saving settings
self.add_label(grid,
'<u>' + _('Time-saving settings') + '</u>',
0, 4, grid_width, 1,
)
checkbutton4 = self.add_checkbutton(grid,
checkbutton = self.add_checkbutton(grid,
_(
'Stop checking/downloading a channel/playlist when it starts' \
+ ' sending videos you already have',
@ -12919,7 +13184,7 @@ class SystemPrefWin(GenericPrefWin):
True, # Can be toggled by user
0, 5, grid_width, 1,
)
checkbutton4.set_hexpand(False)
checkbutton.set_hexpand(False)
# (Signal connect appears below)
self.add_label(grid,
@ -12953,7 +13218,7 @@ class SystemPrefWin(GenericPrefWin):
entry2.set_sensitive(False)
# (Signal connect from above)
checkbutton4.connect(
checkbutton.connect(
'toggled',
self.on_limit_button_toggled,
entry,
@ -13508,7 +13773,7 @@ class SystemPrefWin(GenericPrefWin):
"""
tab, grid = self.add_inner_notebook_tab(
_('_Livestreams'),
_('L_ivestreams'),
inner_notebook,
)
@ -13893,13 +14158,13 @@ class SystemPrefWin(GenericPrefWin):
tab, grid = self.add_notebook_tab('_Downloaders')
# ...and an inner notebook...
inner_notebook = self.add_inner_notebook(grid)
self.downloader_inner_notebook = self.add_inner_notebook(grid)
# ...with its own tabs
self.setup_downloader_forks_tab(inner_notebook)
self.setup_downloader_paths_tab(inner_notebook)
self.setup_downloader_ffmpeg_tab(inner_notebook)
self.setup_downloader_ytsc_tab(inner_notebook)
self.setup_downloader_forks_tab(self.downloader_inner_notebook)
self.setup_downloader_paths_tab(self.downloader_inner_notebook)
self.setup_downloader_ffmpeg_tab(self.downloader_inner_notebook)
self.setup_downloader_ytsc_tab(self.downloader_inner_notebook)
def setup_downloader_forks_tab(self, inner_notebook):
@ -15217,6 +15482,64 @@ class SystemPrefWin(GenericPrefWin):
self.app_obj.set_ignore_yt_age_restrict_flag(False)
def on_alt_time_combo_changed(self, combo, type_str):
"""Called from a callback in self.setup_operations_limits_tab().
Sets the hours or minutes portion of the start or stop time for
alternative performance limits.
Args:
combo (Gtk.ComboBox): The widget clicked
type_str (str): 'start_hour', 'start_min', 'stop_hour', 'stop_min'
"""
tree_iter = combo.get_active_iter()
model = combo.get_model()
value = model[tree_iter][0]
if type_str == 'start_hour' or type_str == 'start_min':
if type_str == 'start_hour':
start_time = value + self.app_obj.alt_start_time[2:5]
else:
start_time = self.app_obj.alt_start_time[0:3] + value
self.app_obj.set_alt_start_time(start_time)
elif type_str == 'stop_hour' or type_str == 'stop_min':
if type_str == 'stop_hour':
stop_time = value + self.app_obj.alt_stop_time[2:5]
else:
stop_time = self.app_obj.alt_stop_time[0:3] + value
self.app_obj.set_alt_stop_time(stop_time)
def on_alt_days_combo_changed(self, combo):
"""Called from a callback in self.setup_operations_limits_tab().
Sets the day(s) on which alternative performance limits apply.
Args:
combo (Gtk.ComboBox): The widget clicked
type_str (str): One of the values in formats.SPECIFIED_DAYS_LIST,
e.g. 'every_day'
"""
tree_iter = combo.get_active_iter()
model = combo.get_model()
self.app_obj.set_alt_day_string(model[tree_iter][1])
def on_alt_website_changed(self, entry):
"""Called from callback in self.setup_operations_custom_tab().
@ -15682,9 +16005,9 @@ class SystemPrefWin(GenericPrefWin):
self.app_obj.set_db_backup_mode(value)
def on_bandwidth_button_toggled(self, checkbutton):
def on_bandwidth_button_toggled(self, checkbutton, alt_flag=False):
"""Called from callback in self.setup_operations_performance_tab().
"""Called from callback in self.setup_operations_limits_tab().
Enables/disables the download speed limit. Toggling the corresponding
Gtk.CheckButton in the Progress Tab sets the IV (and makes sure the two
@ -15694,20 +16017,35 @@ class SystemPrefWin(GenericPrefWin):
checkbutton (Gtk.CheckButton): The widget clicked
alt_flag (bool): If True, the alternative limit is toggled
"""
other_flag \
= self.app_obj.main_win_obj.bandwidth_checkbutton.get_active()
main_win_obj = self.app_obj.main_win_obj
if (checkbutton.get_active() and not other_flag):
self.app_obj.main_win_obj.bandwidth_checkbutton.set_active(True)
elif (not checkbutton.get_active() and other_flag):
self.app_obj.main_win_obj.bandwidth_checkbutton.set_active(False)
if not alt_flag:
other_flag = main_win_obj.bandwidth_checkbutton.get_active()
if (checkbutton.get_active() and not other_flag):
main_win_obj.bandwidth_checkbutton.set_active(True)
elif (not checkbutton.get_active() and other_flag):
main_win_obj.bandwidth_checkbutton.set_active(False)
else:
# Alternative limits. There is no second widget to toggle
if checkbutton.get_active() \
and not self.app_obj.alt_bandwidth_apply_flag:
self.app_obj.set_alt_bandwidth_apply_flag(True)
elif not checkbutton.get_active() \
and self.app_obj.alt_bandwidth_apply_flag:
self.app_obj.set_alt_bandwidth_apply_flag(False)
def on_bandwidth_spinbutton_changed(self, spinbutton):
def on_bandwidth_spinbutton_changed(self, spinbutton, alt_flag=False):
"""Called from callback in self.setup_operations_performance_tab().
"""Called from callback in self.setup_operations_limits_tab().
Sets the simultaneous download limit. Setting the value of the
corresponding Gtk.SpinButton in the Progress Tab sets the IV (and
@ -15717,16 +16055,25 @@ class SystemPrefWin(GenericPrefWin):
spinbutton (Gtk.SpinButton): The widget clicked
alt_flag (bool): If True, the alternative limit is set
"""
self.app_obj.main_win_obj.bandwidth_spinbutton.set_value(
spinbutton.get_value(),
)
if not alt_flag:
self.app_obj.main_win_obj.bandwidth_spinbutton.set_value(
spinbutton.get_value(),
)
else:
# Alternative limits. There is no second widget to toggle
self.app_obj.set_alt_bandwidth(int(spinbutton.get_value()))
def on_check_limit_changed(self, entry):
"""Called from callback in self.setup_operations_time_saving_tab().
"""Called from callback in self.setup_operations_stop_tab().
Sets the limit at which a download operation will stop checking a
channel or playlist.
@ -16712,7 +17059,7 @@ class SystemPrefWin(GenericPrefWin):
def on_dl_limit_changed(self, entry):
"""Called from callback in self.setup_operations_time_saving_tab().
"""Called from callback in self.setup_operations_stop_tab().
Sets the limit at which a download operation will stop downloading a
channel or playlist.
@ -17363,7 +17710,7 @@ class SystemPrefWin(GenericPrefWin):
def on_limit_button_toggled(self, checkbutton, entry, entry2):
"""Called from callback in self.setup_operations_time_saving_tab().
"""Called from callback in self.setup_operations_stop_tab().
Sets the limit at which a download operation will stop downloading a
channel or playlist.
@ -17903,7 +18250,7 @@ class SystemPrefWin(GenericPrefWin):
"""Called from callback in self.setup_windows_errors_warnings_tab().
Enables/disables opeartion errors in the 'Errors/Warnings' tab.
Enables/disables operation errors in the 'Errors/Warnings' tab.
Toggling the corresponding Gtk.CheckButton in the Errors/Warnings tab
sets the IV (and makes sure the two checkbuttons have the same status).
@ -17948,7 +18295,7 @@ class SystemPrefWin(GenericPrefWin):
"""Called from callback in self.setup_windows_errors_warnings_tab().
Enables/disables opeartion warnings in the 'Errors/Warnings' tab.
Enables/disables operation warnings in the 'Errors/Warnings' tab.
Toggling the corresponding Gtk.CheckButton in the Errors/Warnings tab
sets the IV (and makes sure the two checkbuttons have the same status).
@ -19238,6 +19585,31 @@ class SystemPrefWin(GenericPrefWin):
self.app_obj.set_toolbar_squeeze_flag(False)
def on_system_dates_button_toggled(self, checkbutton):
"""Called from callback in self.setup_windows_errors_warnings_tab().
Enables/disables showing dates (as well as times) in the Errors/
Warnings tab. Toggling the corresponding Gtk.CheckButton in the Errors/
Warnings tab sets the IV (and makes sure the two checkbuttons have the
same status).
Args:
checkbutton (Gtk.CheckButton): The widget clicked
"""
main_win_obj = self.app_obj.main_win_obj
other_flag \
= main_win_obj.show_system_dates_checkbutton.get_active()
if (checkbutton.get_active() and not other_flag):
main_win_obj.show_system_dates_checkbutton.set_active(True)
elif (not checkbutton.get_active() and other_flag):
main_win_obj.show_system_dates_checkbutton.set_active(False)
def on_system_debug_toggled(self, checkbutton, debug_type, \
checkbutton2=None):
@ -19644,9 +20016,9 @@ class SystemPrefWin(GenericPrefWin):
self.app_obj.set_ytsc_write_verbose_flag(False)
def on_worker_button_toggled(self, checkbutton):
def on_worker_button_toggled(self, checkbutton, alt_flag=False):
"""Called from callback in self.setup_operations_performance_tab().
"""Called from callback in self.setup_operations_limits_tab().
Enables/disables the simultaneous download limit. Toggling the
corresponding Gtk.CheckButton in the Progress Tab sets the IV (and
@ -19656,15 +20028,30 @@ class SystemPrefWin(GenericPrefWin):
checkbutton (Gtk.CheckButton): The widget clicked
alt_flag (bool): If True, the alternative limit is toggled
"""
other_flag \
= self.app_obj.main_win_obj.num_worker_checkbutton.get_active()
main_win_obj = self.app_obj.main_win_obj
if (checkbutton.get_active() and not other_flag):
self.app_obj.main_win_obj.num_worker_checkbutton.set_active(True)
elif (not checkbutton.get_active() and other_flag):
self.app_obj.main_win_obj.num_worker_checkbutton.set_active(False)
if not alt_flag:
other_flag = main_win_obj.num_worker_checkbutton.get_active()
if (checkbutton.get_active() and not other_flag):
main_win_obj.num_worker_checkbutton.set_active(True)
elif (not checkbutton.get_active() and other_flag):
main_win_obj.num_worker_checkbutton.set_active(False)
else:
# Alternative limits. There is no second widget to toggle
if checkbutton.get_active() \
and not self.app_obj.alt_num_worker_apply_flag:
self.app_obj.set_alt_num_worker_apply_flag(True)
elif not checkbutton.get_active() \
and self.app_obj.alt_num_worker_apply_flag:
self.app_obj.set_alt_num_worker_apply_flag(False)
def on_worker_bypass_button_toggled(self, checkbutton):
@ -19682,15 +20069,15 @@ class SystemPrefWin(GenericPrefWin):
if checkbutton.get_active() \
and not self.app_obj.num_worker_bypass_flag:
self.app_obj.setnum_worker_bypass_flag(True)
self.app_obj.set_num_worker_bypass_flag(True)
elif not checkbutton.get_active() \
and self.app_obj.num_worker_bypass_flag:
self.app_obj.set_num_worker_bypass_flag(False)
def on_worker_spinbutton_changed(self, spinbutton):
def on_worker_spinbutton_changed(self, spinbutton, alt_flag=False):
"""Called from callback in self.setup_operations_performance_tab().
"""Called from callback in self.setup_operations_limits_tab().
Sets the simultaneous download limit. Setting the value of the
corresponding Gtk.SpinButton in the Progress Tab sets the IV (and
@ -19700,16 +20087,25 @@ class SystemPrefWin(GenericPrefWin):
spinbutton (Gtk.SpinButton): The widget clicked
alt_flag (bool): If True, the alternative limit is set
"""
self.app_obj.main_win_obj.num_worker_spinbutton.set_value(
spinbutton.get_value(),
)
if not alt_flag:
self.app_obj.main_win_obj.num_worker_spinbutton.set_value(
spinbutton.get_value(),
)
else:
# Alternative limits. There is no second widget to toggle
self.app_obj.set_alt_num_worker(int(spinbutton.get_value()))
def on_video_res_button_toggled(self, checkbutton):
"""Called from callback in self.setup_operations_performance_tab().
"""Called from callback in self.setup_operations_limits_tab().
Enables/disables the video resolution limit. Toggling the corresponding
Gtk.CheckButton in the Progress Tab sets the IV (and makes sure the two
@ -19732,7 +20128,7 @@ class SystemPrefWin(GenericPrefWin):
def on_video_res_combo_changed(self, combo):
"""Called from a callback in self.setup_operations_performance_tab().
"""Called from a callback in self.setup_operations_limits_tab().
Extracts the value visible in the combobox, converts it into another
value, and uses that value to update the main application's IV.

View File

@ -108,7 +108,7 @@ class DownloadManager(threading.Thread):
using 'classic_sim' is always followed by another one using
'classic_custom'
download_list_obj(downloads.DownloadManager): An ordered list of
download_list_obj (downloads.DownloadManager): An ordered list of
media data objects to download, each one represented by a
downloads.DownloadItem object
@ -215,6 +215,16 @@ class DownloadManager(threading.Thread):
# has been extracted
self.classic_extract_list = []
# Flag set to True when alternative performance limits currently apply,
# False when not. By checking the previous value (stored here)
# against the new one, we can see whether the period of alternative
# limits has started (or stopped)
self.alt_limits_flag = self.check_alt_limits()
# Alternative limits are checked every five minutes. The time (in
# minutes past the hour) at which the next check should be performed
self.alt_limits_check_time = None
# Code
# ----
@ -231,9 +241,34 @@ class DownloadManager(threading.Thread):
# Create a list of downloads.DownloadWorker objects, each one handling
# one of several simultaneous downloads
for i in range(1, self.app_obj.num_worker_default + 1):
# Note that if a downloads.DownloadItem was created by a
# media.Scheduled object that specifies more (or fewer) workers,
# then self.change_worker_count() will be called
if self.alt_limits_flag \
and self.app_obj.alt_num_worker_apply_flag:
worker_count = self.app_obj.alt_num_worker
elif (not self.alt_limits_flag) \
and self.app_obj.num_worker_apply_flag:
worker_count = self.app_obj.num_worker_default
else:
# Failsafe
worker_count = 2
for i in range(1, worker_count + 1):
self.worker_list.append(DownloadWorker(self))
# Set the time at which the first check for alternative limits is
# performed
local = utils.get_local_time()
self.alt_limits_check_time \
= (int(int(local.strftime('%M')) / 5) * 5) + 5
if self.alt_limits_check_time > 55:
self.alt_limits_check_time = 0
# (Also update the icon in the Progress tab)
self.app_obj.main_win_obj.toggle_alt_limits_image(self.alt_limits_flag)
# Let's get this party started!
self.start()
@ -303,6 +338,58 @@ class DownloadManager(threading.Thread):
if (time.time() - self.start_time) > time_limit:
break
# Every five minutes, check whether the period of alternative
# performance limits has started (or stopped)
local = utils.get_local_time()
if int(local.strftime('%M')) >= self.alt_limits_check_time:
self.alt_limits_check_time += 5
if self.alt_limits_check_time > 55:
self.alt_limits_check_time = 0
new_flag = self.check_alt_limits()
if new_flag != self.alt_limits_flag:
self.alt_limits_flag = new_flag
if not new_flag:
self.app_obj.main_win_obj.output_tab_write_stdout(
0,
_(
'Alternative performance limits no longer apply',
),
)
else:
self.app_obj.main_win_obj.output_tab_write_stdout(
0,
_('Alternative performance limits now apply'),
)
# Change the number of workers. Bandwidth changes are
# applied by OptionsParser.build_limit_rate()
if self.app_obj.num_worker_default \
!= self.app_obj.alt_num_worker \
and self.alt_num_worker_apply_flag:
if not new_flag:
self.change_worker_count(
self.app_obj.num_worker_default,
)
else:
self.change_worker_count(
self.app_obj.alt_num_worker,
)
# (Also update the icon in the Progress tab)
self.app_obj.main_win_obj.toggle_alt_limits_image(
self.alt_limits_flag,
)
# Fetch information about the next media data object to be
# downloaded (and store it in an IV, so the main window's
# progress bar can be updated at any time, by any code)
@ -435,6 +522,9 @@ class DownloadManager(threading.Thread):
True, # Don't update the Video Catalogue yet
)
# (Also update the icon in the Progress tab)
self.app_obj.main_win_obj.toggle_alt_limits_image(False)
# When youtube-dl reports it is finished, there is a short delay before
# the final downloaded video(s) actually exist in the filesystem
# Therefore, mainwin.MainWin.progress_list_display_dl_stats() may not
@ -481,9 +571,119 @@ class DownloadManager(threading.Thread):
download_item_obj.set_ignore_limits_flag()
def check_alt_limits(self):
"""Called by self.__init__() and .run().
Checks whether alternative performance limits apply right now, or not.
Returns:
True if alternative limits apply, False if not.
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('dld 475 check_alt_limits')
# Get the current time and day of the week
local = utils.get_local_time()
current_hours = int(local.strftime('%H'))
current_minutes = int(local.strftime('%M'))
# 0=Monday, 6=Sunday
current_day = local.today().weekday()
# The period of alternative performance limits have a start and stop
# time, stored as strings in the form '21:00'
start_hours = int(self.app_obj.alt_start_time[0:2])
start_minutes = int(self.app_obj.alt_start_time[3:5])
stop_hours = int(self.app_obj.alt_stop_time[0:2])
stop_minutes = int(self.app_obj.alt_stop_time[3:5])
# Is the current time before or after the start/stop times?
if current_hours < start_hours \
or (current_hours == start_hours and current_minutes < start_minutes):
start_before_flag = True
else:
start_before_flag = False
if current_hours < stop_hours \
or (current_hours == stop_hours and current_minutes < stop_minutes):
stop_before_flag = True
else:
stop_before_flag = False
# If the start time is earlier than the stop time, we assume they're on
# the same day
if start_hours < stop_hours \
or (start_hours == stop_hours and start_minutes < stop_minutes):
if not self.check_alt_limits_day(current_day) \
or start_before_flag \
or (not stop_before_flag):
return False
else:
return True
# Otherwise, we assume the stop time occurs the following day (e.g.
# 21:00 to 07:00)
else:
prev_day = current_day - 1
if prev_day < 0:
prev_day = 6
if (
self.check_alt_limits_day(current_day) \
and (not start_before_flag)
) or (
self.check_alt_limits_day(prev_day) \
and stop_before_flag
):
return True
else:
return False
def check_alt_limits_day(self, this_day):
"""Called by self.check_alt_limits().
Test the day(s) of the week on which alternative limits apply. The
specified day(s) are stored as a string in the form 'every_day',
'weekdays', 'weekends', or 'monday', 'tuesday' etc.
Returns:
True if the alternative limits apply today, False if not.
"""
# Test the day(s) of the week on which alterative limits apply. The
# specified day(s) are stored as a string in the form 'every_day',
# 'weekdays', 'weekends', or 'monday', 'tuesday' etc.
day_str = self.app_obj.alt_day_string
if day_str != 'every_day':
if (day_str == 'weekdays' and this_day > 4) \
or (day_str == 'weekends' and this_day < 5) \
or (day_str == 'monday' and this_day != 0) \
or (day_str == 'tuesday' and this_day != 1) \
or (day_str == 'wednesday' and this_day != 2) \
or (day_str == 'thursday' and this_day != 3) \
or (day_str == 'friday' and this_day != 4) \
or (day_str == 'saturday' and this_day != 5) \
or (day_str == 'sunday' and this_day != 6):
return False
return True
def change_worker_count(self, number):
"""Called by mainapp.TartubeApp.set_num_worker_default().
"""Called by mainapp.TartubeApp.set_num_worker_default(). Can also be
called by self.run() when the period of alternative performances limits
begins or ends.
When the number of simultaneous downloads allowed is changed during a
download operation, this function responds.
@ -495,8 +695,7 @@ class DownloadManager(threading.Thread):
Args:
number (int): The new value of
mainapp.TartubeApp.num_worker_default
number (int): The new number of simultaneous downloads allowed
"""
@ -1027,6 +1226,20 @@ class DownloadWorker(threading.Thread):
# Import the media data object (for convenience)
media_data_obj = self.download_item_obj.media_data_obj
# If the downloads.DownloadItem was created by a scheduled
# download (media.Scheduled), then change the number of
# workers, if necessary
if self.download_item_obj.scheduled_obj:
scheduled_obj = self.download_item_obj.scheduled_obj
if scheduled_obj.scheduled_num_worker_apply_flag \
and scheduled_obj.scheduled_num_worker \
!= len(self.download_manager_obj.worker_list):
self.download_manager_obj.change_worker_count(
scheduled_obj.scheduled_num_worker,
)
# When downloading a livestream that's broadcasting now, we
# can use Youtube Stream Capture. Otherwise, use youtube-dl
# for all downloading tasks
@ -1452,6 +1665,7 @@ class DownloadWorker(threading.Thread):
self.download_item_obj.media_data_obj,
self.options_manager_obj,
self.download_item_obj.operation_type,
self.download_item_obj.scheduled_obj,
)
self.available_flag = False
@ -1661,22 +1875,26 @@ class DownloadList(object):
if media_data_list and isinstance(media_data_list[0], media.Scheduled):
# media_data_list is a list of scheduled downloads
all_flag = False
all_obj = False
ignore_limits_flag = False
for scheduled_obj in media_data_list:
if scheduled_obj.all_flag:
all_flag = True
all_obj = scheduled_obj
if scheduled_obj.ignore_limits_flag:
ignore_limits_flag = True
if all_obj:
break
if all_flag:
if all_obj:
# Use all media data objects
for dbid in self.app_obj.media_top_level_list:
obj = self.app_obj.media_reg_dict[dbid]
self.create_item(
obj,
all_obj, # media.Scheduled object
None, # override_operation_type
False, # priority_flag
ignore_limits_flag,
@ -1703,6 +1921,7 @@ class DownloadList(object):
self.create_item(
obj,
scheduled_obj,
scheduled_obj.dl_mode,
priority_flag,
scheduled_obj.ignore_limits_flag,
@ -1722,6 +1941,7 @@ class DownloadList(object):
obj = self.app_obj.media_reg_dict[dbid]
self.create_item(
obj,
None, # media.Scheduled object
None, # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -1749,6 +1969,7 @@ class DownloadList(object):
# Use the specified media data object
self.create_item(
media_data_obj,
None, # media.Scheduled object
None, # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -1845,8 +2066,9 @@ class DownloadList(object):
self.download_item_dict[item_id].stage = new_stage
def create_item(self, media_data_obj, override_operation_type=None,
priority_flag=False, ignore_limits_flag=False, recursion_flag=False):
def create_item(self, media_data_obj, scheduled_obj=None,
override_operation_type=None, priority_flag=False,
ignore_limits_flag=False, recursion_flag=False):
"""Called initially by self.__init__() (or by many other functions,
for example in mainapp.TartubeApp).
@ -1884,6 +2106,10 @@ class DownloadList(object):
media_data_obj (media.Video, media.Channel, media.Playlist,
media.Folder): A media data object
scheduled_obj (media.Scheduled): The scheduled download object
which wants to download media_data_obj (None if no scheduled
download applies in this case)
override_operation_type (str): After the download operation has
started, any code can call this function to add new
downloads.DownloadItem objects to this downloads.DownloadList,
@ -2075,6 +2301,7 @@ class DownloadList(object):
download_item_obj = DownloadItem(
self.download_item_count,
media_data_obj,
scheduled_obj,
options_manager_obj,
operation_type,
ignore_limits_flag,
@ -2106,6 +2333,7 @@ class DownloadList(object):
for child_obj in media_data_obj.child_list:
self.create_item(
child_obj,
scheduled_obj,
operation_type,
priority_flag,
ignore_limits_flag,
@ -2150,6 +2378,7 @@ class DownloadList(object):
download_item_obj = DownloadItem(
media_data_obj.dbid,
media_data_obj,
None, # media.Scheduled object
options_manager_obj,
self.operation_type, # 'classic_real'. 'classic_sim' or
# 'classic_custom'
@ -2339,6 +2568,10 @@ class DownloadItem(object):
download operation was launched from the Classic Mode Tab, a dummy
media.Video object
scheduled_obj (media.Scheduled): The scheduled download object which
wants to download media_data_obj (None if no scheduled download
applies in this case)
options_manager_obj (options.OptionsManager): The object which
specifies download options for the media data object
@ -2367,8 +2600,8 @@ class DownloadItem(object):
# Standard class methods
def __init__(self, item_id, media_data_obj, options_manager_obj,
operation_type, ignore_limits_flag):
def __init__(self, item_id, media_data_obj, scheduled_obj,
options_manager_obj, operation_type, ignore_limits_flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('dld 2323 __init__')
@ -2378,6 +2611,9 @@ class DownloadItem(object):
# The media data object to be downloaded. When the download operation
# was launched from the Classic Mode Tab, a dummy media.Video object
self.media_data_obj = media_data_obj
# The scheduled download object which wants to download media_data_obj
# (None if no scheduled download applies in this case)
self.scheduled_obj = scheduled_obj
# The object which specifies download options for the media data object
self.options_manager_obj = options_manager_obj
@ -4089,6 +4325,7 @@ class VideoDownloader(object):
new_download_item_obj \
= self.download_manager_obj.download_list_obj.create_item(
new_container_obj,
self.download_item_obj.scheduled_obj,
self.download_item_obj.operation_type,
False, # priority_flag
self.download_item_obj.ignore_limits_flag,

View File

@ -86,6 +86,29 @@ while time_metric_setup_list:
TIME_METRIC_DICT[key] = value
TIME_METRIC_TRANS_DICT[key] = trans_key
specified_days_setup_list = [
'every_day', _('Every day'),
'weekdays', _('Weekdays'),
'weekends', _('Weekends'),
'monday', _('Monday'),
'tuesday', _('Tuesday'),
'wednesday', _('Wednesday'),
'thursday', _('Thursday'),
'friday', _('Friday'),
'saturday', _('Saturday'),
'sunday', _('Sunday'),
]
SPECIFIED_DAYS_LIST = []
SPECIFIED_DAYS_DICT = {}
while specified_days_setup_list:
key = specified_days_setup_list.pop(0)
value = specified_days_setup_list.pop(0)
SPECIFIED_DAYS_LIST.append(key)
SPECIFIED_DAYS_DICT[key] = value
KILO_SIZE = 1024.0
filesize_metric_setup_list = [
'B', 1,
@ -567,13 +590,15 @@ while language_setup_list:
if not xmas_flag:
DIALOGUE_ICON_DICT = {
'system_icon': 'system_icon_64.png',
'newbie_icon': 'newbie_icon_64.png',
'ready_icon': 'ready_icon_64.png',
'system_icon': 'system_icon_64.png',
}
else:
DIALOGUE_ICON_DICT = {
'system_icon': 'system_icon_xmas_64.png',
'newbie_icon': 'newbie_icon_64.png',
'ready_icon': 'ready_icon_64.png',
'system_icon': 'system_icon_xmas_64.png',
}
if not xmas_flag:
@ -672,6 +697,8 @@ LARGE_ICON_DICT = {
'copy_large': 'copy.png',
'hand_left_large': 'hand_left.png',
'hand_right_large': 'hand_right.png',
'limits_off_large': 'limits_off.png',
'limits_on_large': 'limits_on.png',
'question_large': 'question.png',
'warning_large': 'warning.png',
}

View File

@ -478,6 +478,9 @@ class TartubeApp(Gtk.Application):
# Flag set to True if operation warning messages should be shown in the
# Errors/Warnings tab
self.operation_warning_show_flag = True
# Flag set to True if the date (as well as the time) should be shown in
# the Errors/Warnings Tab
self.system_msg_show_date_flag = True
# Flag set to True if the total number of system error/warning messages
# shown in the tab label is not reset until the 'Clear the list'
# button is explicitly clicked (normally, the total numbers are
@ -1198,6 +1201,10 @@ class TartubeApp(Gtk.Application):
# self.operation_limit_flag is False
self.operation_download_limit = 3
# Flag set to True if the newbie dialogue should appear after a failed
# download operation, explaining what to do
self.show_newbie_dialogue_flag = True
# Media data classes are those specified in media.py. Those class
# objects are media.Video (for individual videos), media.Channel,
# media.Playlist and media.Folder (reprenting a sub-directory inside
@ -1754,6 +1761,26 @@ class TartubeApp(Gtk.Application):
# the comments in options.OptionsManager)
self.video_res_apply_flag = False
# Alternative performance limits (applied at certain times of the
# day/week)
# Note that these limits, if applied, apply to the whole video/
# channel/playlist, at the moment the video/channel/playlist download
# starts. Tartube cannot magically change youtube-dl's internal
# settings once the download has started
self.alt_num_worker = 4
self.alt_num_worker_apply_flag = False
self.alt_bandwidth = 1000
self.alt_bandwidth_apply_flag = False
# Two 24 hour clock times, marking the start and stop of the period
# during which alternative limits are applied. If the stop time is
# earlier than the start time, then it is applied the next day
self.alt_start_time = '21:00'
self.alt_stop_time = '07:00'
# A string describing the days on which the limit is applied:
# 'every_day', 'weekdays', 'weekends', or 'monday', 'tuesday' etc.
# The strings are translated by formats.SPECIFIED_DAYS_DICT
self.alt_day_string = 'every_day'
# The method of matching downloaded videos against existing
# media.Video objects:
# 'exact_match' - The video name must match exactly
@ -3469,7 +3496,9 @@ class TartubeApp(Gtk.Application):
= json_dict['operation_error_show_flag']
self.operation_warning_show_flag \
= json_dict['operation_warning_show_flag']
if version >= 2003116: # v2.3.116
self.system_msg_show_date_flag \
= json_dict['system_msg_show_date_flag']
if version >= 1000007: # v1.0.007
self.system_msg_keep_totals_flag \
= json_dict['system_msg_keep_totals_flag']
@ -3639,6 +3668,10 @@ class TartubeApp(Gtk.Application):
self.operation_download_limit \
= json_dict['operation_download_limit']
if version >= 2003114: # v2.3.114
self.show_newbie_dialogue_flag \
= json_dict['show_newbie_dialogue_flag']
if version >= 1003032: # v1.3.032
self.auto_clone_options_flag = json_dict['auto_clone_options_flag']
if version >= 2002116: # v2.2.116
@ -3849,6 +3882,17 @@ class TartubeApp(Gtk.Application):
self.video_res_default = json_dict['video_res_default']
self.video_res_apply_flag = json_dict['video_res_apply_flag']
if version >= 2003117: # v2.3.117
self.alt_num_worker = json_dict['alt_num_worker']
self.alt_num_worker_apply_flag \
= json_dict['alt_num_worker_apply_flag']
self.alt_bandwidth = json_dict['alt_bandwidth']
self.alt_bandwidth_apply_flag \
= json_dict['alt_bandwidth_apply_flag']
self.alt_start_time = json_dict['alt_start_time']
self.alt_stop_time = json_dict['alt_stop_time']
self.alt_day_string = json_dict['alt_day_string']
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']
@ -4322,6 +4366,7 @@ class TartubeApp(Gtk.Application):
'system_warning_show_flag': self.system_warning_show_flag,
'operation_error_show_flag': self.operation_error_show_flag,
'operation_warning_show_flag': self.operation_warning_show_flag,
'system_msg_show_date_flag': self.system_msg_show_date_flag,
'system_msg_keep_totals_flag': self.system_msg_keep_totals_flag,
'data_dir': self.data_dir,
@ -4418,6 +4463,8 @@ class TartubeApp(Gtk.Application):
'operation_check_limit': self.operation_check_limit,
'operation_download_limit': self.operation_download_limit,
'show_newbie_dialogue_flag': self.show_newbie_dialogue_flag,
'auto_clone_options_flag': self.auto_clone_options_flag,
'auto_delete_options_flag': self.auto_delete_options_flag,
'simple_options_flag': self.simple_options_flag,
@ -4503,6 +4550,14 @@ class TartubeApp(Gtk.Application):
'video_res_default': self.video_res_default,
'video_res_apply_flag': self.video_res_apply_flag,
'alt_num_worker': self.alt_num_worker,
'alt_num_worker_apply_flag': self.alt_num_worker_apply_flag,
'alt_bandwidth': self.alt_bandwidth,
'alt_bandwidth_apply_flag': self.alt_bandwidth_apply_flag,
'alt_start_time': self.alt_start_time,
'alt_stop_time': self.alt_stop_time,
'alt_day_string': self.alt_day_string,
'match_method': self.match_method,
'match_first_chars': self.match_first_chars,
'match_ignore_chars': self.match_ignore_chars,
@ -5835,6 +5890,16 @@ class TartubeApp(Gtk.Application):
]
self.ytdl_update_current = 'ytdl_update_disabled'
if version < 2003119: # v2.3.119
# This version adds IVs to media.Scheduled objects
for scheduled_obj in self.scheduled_list:
scheduled_obj.scheduled_num_worker = 2
scheduled_obj.scheduled_num_worker_apply_flag = False
scheduled_obj.scheduled_bandwidth = 500
scheduled_obj.scheduled_bandwidth_apply_flag = False
def save_db(self):
@ -6390,7 +6455,7 @@ class TartubeApp(Gtk.Application):
msg = 'Tartube database \'{0}\' can\'t be loaded' \
+ ' - another instance of Tartube may be using it.' \
+ ' If not, you can fix this problem by deleting' \
+ ' the lockfile \'{1}\'',
+ ' the lockfile \'{1}\''
self.system_warning(
103,
@ -8654,6 +8719,8 @@ class TartubeApp(Gtk.Application):
self.reset_backup_archive()
# If Tartube is due to shut down, then shut it down
show_newbie_dialogue_flag = False
if self.halt_after_operation_flag:
self.stop_continue()
@ -8661,30 +8728,41 @@ class TartubeApp(Gtk.Application):
elif not self.no_dialogue_this_time_flag \
and operation_type != 'classic_sim':
if not self.operation_halted_flag:
msg = _('Download operation complete')
# If videos were expected to be checked/downloaded, but nothing
# happened, show a newbie dialogue explaining what to do next
if self.show_newbie_dialogue_flag \
and dl_count == 0 \
and sim_count == 0:
show_newbie_dialogue_flag = True
else:
msg = _('Download operation halted')
if dl_count or sim_count:
if not self.operation_halted_flag:
msg = _('Download operation complete')
else:
msg = _('Download operation halted')
msg += '\n\n' + _('Videos downloaded:') + ' ' + str(dl_count) \
+ '\n' + _('Videos checked:') + ' ' + str(sim_count)
if dl_count or sim_count:
if time_num >= 10:
msg += '\n\n' + _('Time taken:') + ' ' \
+ utils.convert_seconds_to_string(time_num, True)
msg += '\n\n' + _('Videos downloaded:') + ' ' \
+ str(dl_count) + '\n' + _('Videos checked:') \
+ ' ' + str(sim_count)
if self.operation_dialogue_mode == 'dialogue':
if time_num >= 10:
msg += '\n\n' + _('Time taken:') + ' ' \
+ utils.convert_seconds_to_string(time_num, True)
self.dialogue_manager_obj.show_simple_msg_dialogue(
msg,
'info',
'ok',
)
if self.operation_dialogue_mode == 'dialogue':
elif self.operation_dialogue_mode == 'desktop':
self.main_win_obj.notify_desktop(None, msg)
self.dialogue_manager_obj.show_simple_msg_dialogue(
msg,
'info',
'ok',
)
elif self.operation_dialogue_mode == 'desktop':
self.main_win_obj.notify_desktop(None, msg)
# In any case, reset those IVs
self.halt_after_operation_flag = False
@ -8733,6 +8811,34 @@ class TartubeApp(Gtk.Application):
dummy_list,
)
# Show the newbie dialogue, if required
elif show_newbie_dialogue_flag:
dialogue_win = mainwin.NewbieDialogue(self.main_win_obj)
dialogue_win.run()
# Retrieve user choices from the dialogue window...
newbie_update_flag = dialogue_win.update_flag
newbie_config_flag = dialogue_win.config_flag
newbie_change_flag = dialogue_win.change_flag
newbie_website_flag = dialogue_win.website_flag
newbie_issues_flag = dialogue_win.issues_flag
self.show_newbie_dialogue_flag = dialogue_win.show_flag
dialogue_win.destroy()
if newbie_update_flag:
self.update_manager_start('ytdl')
elif newbie_config_flag:
config.SystemPrefWin(self.main_win_obj.app_obj, 'paths')
elif newbie_change_flag:
config.SystemPrefWin(self.main_win_obj.app_obj, 'forks')
elif newbie_website_flag:
utils.open_file(self, __main__.__website__)
elif newbie_issues_flag:
utils.open_file(self, __main__.__website_bugs__)
def update_manager_start(self, update_type):
@ -9986,6 +10092,7 @@ class TartubeApp(Gtk.Application):
download_item_obj \
= self.download_manager_obj.download_list_obj.create_item(
video_obj,
None, # media.Scheduled object
'real', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -15565,6 +15672,7 @@ class TartubeApp(Gtk.Application):
download_item_obj \
= self.download_manager_obj.download_list_obj.create_item(
video_obj,
None, # media.Scheduled object
'real', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -16666,7 +16774,7 @@ class TartubeApp(Gtk.Application):
# scheduled download that should start now
first_list = []
next_list = []
all_flag = False
all_obj = False
shutdown_flag = False
ignore_limits_flag = False
@ -16693,9 +16801,12 @@ class TartubeApp(Gtk.Application):
# Only perform this scheduled download
first_list = [scheduled_obj]
next_list = []
all_flag = scheduled_obj.all_flag
shutdown_flag = scheduled_obj.shutdown_flag
ignore_limits_flag = scheduled_obj.ignore_limits_flag
if scheduled_obj.all_flag:
all_obj = scheduled_obj
break
# 'start' should be done before 'scheduled'
@ -16704,13 +16815,15 @@ class TartubeApp(Gtk.Application):
else:
next_list.append(scheduled_obj)
if scheduled_obj.all_flag:
all_flag = True
if scheduled_obj.shutdown_flag:
shutdown_flag = True
if scheduled_obj.ignore_limits_flag:
ignore_limits_flag = True
if scheduled_obj.all_flag:
all_obj = scheduled_obj
break
start_list = first_list + next_list
# In case there are different values for media.Scheduled.dl_mode and
@ -16732,7 +16845,7 @@ class TartubeApp(Gtk.Application):
# If any scheduled downloads are due to start, and any of the
# media.Scheduled objects have their .all_flag IV set, then we simply
# download everything
if start_list and all_flag:
if start_list and all_obj:
if dl_mode is not None:
@ -16747,6 +16860,7 @@ class TartubeApp(Gtk.Application):
self.download_manager_start(
dl_mode,
True, # This function is the calling function
[all_obj], # Make sure performance limits respected
)
# Ignore operation limits, if required
@ -16776,6 +16890,7 @@ class TartubeApp(Gtk.Application):
self.script_slow_timer_insert_download(
media_data_obj,
all_obj,
dl_mode,
join_mode,
ignore_limits_flag,
@ -16815,6 +16930,7 @@ class TartubeApp(Gtk.Application):
media_data_obj = self.media_reg_dict[dbid]
self.script_slow_timer_insert_download(
media_data_obj,
scheduled_obj,
scheduled_obj.dl_mode,
scheduled_obj.join_mode,
scheduled_obj.ignore_limits_flag,
@ -16913,8 +17029,8 @@ class TartubeApp(Gtk.Application):
return 1
def script_slow_timer_insert_download(self, media_data_obj, dl_mode,
join_mode, ignore_limits_flag):
def script_slow_timer_insert_download(self, media_data_obj, scheduled_obj,
dl_mode, join_mode, ignore_limits_flag):
"""Called by self.script_slow_timer_callback(), when a download
operation is already in progress.
@ -16927,6 +17043,10 @@ class TartubeApp(Gtk.Application):
media data object to add. It, as well as all of its children,
are added to the download queue
scheduled_obj (media.Scheduled): The scheduled download object
which wants to download media_data_obj (None if no scheduled
download applies in this case)
dl_mode (str): 'sim', 'real' or 'multi', matching the value of
downloads.DownloadManager.operation_type
@ -16951,6 +17071,7 @@ class TartubeApp(Gtk.Application):
download_item_obj \
= self.download_manager_obj.download_list_obj.create_item(
media_data_obj,
scheduled_obj,
dl_mode,
priority_flag,
ignore_limits_flag,
@ -19819,6 +19940,68 @@ class TartubeApp(Gtk.Application):
self.allow_ytdl_archive_flag = True
def set_alt_bandwidth(self, value):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18916 set_alt_bandwidth')
self.alt_bandwidth = value
def set_alt_bandwidth_apply_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18917 set_alt_bandwidth_apply_flag')
if not flag:
self.alt_bandwidth_apply_flag = False
else:
self.alt_bandwidth_apply_flag = True
def set_alt_day_string(self, value):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18917 set_alt_day_string')
self.alt_day_string = value
def set_alt_num_worker(self, value):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18918 set_alt_num_worker')
self.alt_num_worker = value
def set_alt_num_worker_apply_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18919 set_alt_num_worker_apply_flag')
if not flag:
self.alt_num_worker_apply_flag = False
else:
self.alt_num_worker_apply_flag = True
def set_alt_start_time(self, value):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18920 set_alt_start_time')
self.alt_start_time = value
def set_alt_stop_time(self, value):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18921 set_alt_stop_time')
self.alt_stop_time = value
def set_apply_json_timeout_flag(self, flag):
if DEBUG_FUNC_FLAG:
@ -20957,7 +21140,9 @@ class TartubeApp(Gtk.Application):
old_value = self.num_worker_default
self.num_worker_default = value
if old_value != value and self.download_manager_obj:
if old_value != value \
and self.download_manager_obj \
and not self.download_manager_obj.alt_limits_flag:
self.download_manager_obj.change_worker_count(value)
if value > self.main_win_obj.output_page_count:
@ -21338,6 +21523,17 @@ class TartubeApp(Gtk.Application):
self.show_custom_icons_flag = True
def set_show_newbie_dialogue_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 20370 set_show_newbie_dialogue_flag')
if not flag:
self.show_newbie_dialogue_flag = False
else:
self.show_newbie_dialogue_flag = True
def set_show_pretty_dates_flag(self, flag):
"""Called by config.SystemPrefWin.on_pretty_date_button_toggled().
@ -21453,6 +21649,17 @@ class TartubeApp(Gtk.Application):
self.system_msg_keep_totals_flag = True
def set_system_msg_show_date_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 20474 set_system_msg_show_date_flag')
if not flag:
self.system_msg_show_date_flag = False
else:
self.system_msg_show_date_flag = True
def set_system_warning_show_flag(self, flag):
if DEBUG_FUNC_FLAG:

View File

@ -27,6 +27,7 @@ from gi.repository import Gtk, GObject, Gdk, GdkPixbuf
# Import other modules
import datetime
import functools
from gi.repository import Gio
import os
@ -230,6 +231,8 @@ class MainWin(Gtk.ApplicationWindow):
self.num_worker_spinbutton = None # Gtk.SpinButton
self.bandwidth_checkbutton = None # Gtk.CheckButton
self.bandwidth_spinbutton = None # Gtk.SpinButton
self.alt_limits_frame = None # Gtk.Frame
self.alt_limits_image = None # Gtk.Image
self.video_res_checkbutton = None # Gtk.CheckButton
self.video_res_combobox = None # Gtk.ComboBox
self.hide_finished_checkbutton = None # Gtk.CheckButton
@ -250,6 +253,8 @@ class MainWin(Gtk.ApplicationWindow):
# Gtk.CheckButton
self.show_operation_warning_checkbutton = None
# Gtk.CheckButton
self.show_system_dates_checkbutton = None
# Gtk.CheckButton
self.error_list_button = None # Gtk.Button
# (from self.setup_classic_mode_tab)
self.classic_paned = None # Gtk.VPaned
@ -2322,8 +2327,20 @@ class MainWin(Gtk.ApplicationWindow):
self.on_bandwidth_spinbutton_changed,
)
self.alt_limits_frame = Gtk.Frame()
grid.attach(self.alt_limits_frame, 4, 0, 1, 1)
self.alt_limits_frame.set_tooltip_text(
_('Alternative limits do not currently apply'),
)
self.alt_limits_image = Gtk.Image()
self.alt_limits_frame.add(self.alt_limits_image)
self.alt_limits_image.set_from_pixbuf(
self.pixbuf_dict['limits_off_large'],
)
self.video_res_checkbutton = Gtk.CheckButton()
grid.attach(self.video_res_checkbutton, 4, 0, 1, 1)
grid.attach(self.video_res_checkbutton, 5, 0, 1, 1)
self.video_res_checkbutton.set_label(_('Video resolution'))
self.video_res_checkbutton.set_active(
self.app_obj.video_res_apply_flag,
@ -2338,7 +2355,7 @@ class MainWin(Gtk.ApplicationWindow):
store.append( [string] )
self.video_res_combobox = Gtk.ComboBox.new_with_model(store)
grid.attach(self.video_res_combobox, 5, 0, 1, 1)
grid.attach(self.video_res_combobox, 6, 0, 1, 1)
renderer_text = Gtk.CellRendererText()
self.video_res_combobox.pack_start(renderer_text, True)
self.video_res_combobox.add_attribute(renderer_text, 'text', 0)
@ -3014,7 +3031,7 @@ class MainWin(Gtk.ApplicationWindow):
# visible)
for i, column_title in enumerate(
[
'hide', 'hide', 'hide', '', '', _('Time'), _('Type'),
'hide', 'hide', 'hide', '', '', _('Time'), _('Name'),
_('Message')
],
@ -3108,6 +3125,24 @@ class MainWin(Gtk.ApplicationWindow):
self.on_operation_warning_checkbutton_changed,
)
self.show_system_dates_checkbutton = Gtk.CheckButton()
hbox.pack_start(
self.show_system_dates_checkbutton,
False,
False,
0,
)
self.show_system_dates_checkbutton.set_label(
_('Show dates'),
)
self.show_system_dates_checkbutton.set_active(
self.app_obj.system_msg_show_date_flag,
)
self.show_system_dates_checkbutton.connect(
'toggled',
self.on_system_dates_checkbutton_changed,
)
self.error_list_button = Gtk.Button()
hbox.pack_end(self.error_list_button, False, False, 0)
self.error_list_button.set_label(_('Clear list'))
@ -4161,6 +4196,41 @@ class MainWin(Gtk.ApplicationWindow):
notify_obj.show()
def toggle_alt_limits_image(self, on_flag):
"""Can be called by anything.
Toggles the icon in the Progress Tab.
Args:
on_flag (bool): True for a normal image (signifying that
alternative performance limits currently apply), False for a
greyed-out image
"""
if on_flag:
self.alt_limits_image.set_from_pixbuf(
self.pixbuf_dict['limits_on_large'],
)
self.alt_limits_frame.set_tooltip_text(
_('Alternative limits currently apply'),
)
else:
self.alt_limits_image.set_from_pixbuf(
self.pixbuf_dict['limits_off_large'],
)
self.alt_limits_frame.set_tooltip_text(
_('Alternative limits do not currently apply'),
)
# (Auto-sort functions for main window widgets)
@ -11653,8 +11723,11 @@ class MainWin(Gtk.ApplicationWindow):
# Create a new row for every error and warning message
# Use the same time on each
local = utils.get_local_time()
time_string = str(local.strftime('%H:%M:%S'))
if self.app_obj.system_msg_show_date_flag:
time_string = datetime.datetime.today().strftime('%x %X')
else:
local = utils.get_local_time()
time_string = str(local.strftime('%H:%M:%S'))
if self.app_obj.operation_error_show_flag:
@ -11776,8 +11849,11 @@ class MainWin(Gtk.ApplicationWindow):
# Prepare the new row in the treeview
row_list = []
local = utils.get_local_time()
time_string = str(local.strftime('%H:%M:%S'))
if self.app_obj.system_msg_show_date_flag:
time_string = datetime.datetime.today().strftime('%x %X')
else:
local = utils.get_local_time()
time_string = str(local.strftime('%H:%M:%S'))
for i in range(3):
row_list.append('') # Hidden columns
@ -11832,8 +11908,11 @@ class MainWin(Gtk.ApplicationWindow):
# Prepare the new row in the treeview
row_list = []
local = utils.get_local_time()
time_string = str(local.strftime('%H:%M:%S'))
if self.app_obj.system_msg_show_date_flag:
time_string = datetime.datetime.today().strftime('%x %X')
else:
local = utils.get_local_time()
time_string = str(local.strftime('%H:%M:%S'))
for i in range(3):
row_list.append('') # Hidden columns
@ -13682,6 +13761,7 @@ class MainWin(Gtk.ApplicationWindow):
download_item_obj \
= download_manager_obj.download_list_obj.create_item(
media_data_obj,
None, # media.Scheduled object
'sim', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -13747,6 +13827,7 @@ class MainWin(Gtk.ApplicationWindow):
download_item_obj \
= download_manager_obj.download_list_obj.create_item(
media_data_obj,
None, # media.Scheduled object
'sim', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -13994,6 +14075,7 @@ class MainWin(Gtk.ApplicationWindow):
download_item_obj \
= download_manager_obj.download_list_obj.create_item(
media_data_obj,
None, # media.Scheduled object
'real', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -14063,6 +14145,7 @@ class MainWin(Gtk.ApplicationWindow):
download_item_obj \
= download_manager_obj.download_list_obj.create_item(
media_data_obj,
None, # media.Scheduled object
'real', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -16606,9 +16689,10 @@ class MainWin(Gtk.ApplicationWindow):
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 16345 on_bandwidth_spinbutton_changed')
self.app_obj.set_bandwidth_default(
int(self.bandwidth_spinbutton.get_value())
)
if self.bandwidth_checkbutton.get_active():
self.app_obj.set_bandwidth_default(
int(self.bandwidth_spinbutton.get_value())
)
def on_bandwidth_checkbutton_changed(self, checkbutton):
@ -16628,9 +16712,16 @@ class MainWin(Gtk.ApplicationWindow):
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 16367 on_bandwidth_checkbutton_changed')
self.app_obj.set_bandwidth_apply_flag(
self.bandwidth_checkbutton.get_active(),
)
if self.bandwidth_checkbutton.get_active():
self.app_obj.set_bandwidth_apply_flag(True)
self.app_obj.set_bandwidth_default(
int(self.bandwidth_spinbutton.get_value())
)
else:
self.app_obj.set_bandwidth_apply_flag(False)
def on_delete_event(self, widget, event):
@ -17070,6 +17161,26 @@ class MainWin(Gtk.ApplicationWindow):
self.app_obj.set_results_list_reverse_flag(checkbutton.get_active())
def on_system_dates_checkbutton_changed(self, checkbutton):
"""Called from callback in self.setup_errors_tab().
Toggles display of dates (as well as times) in the tab.
Args:
checkbutton (Gtk.CheckButton) - The clicked widget
"""
if DEBUG_FUNC_FLAG:
utils.debug_time(
'mwn 16806 on_system_dates_checkbutton_changed',
)
self.app_obj.set_system_msg_show_date_flag(checkbutton.get_active())
def on_system_error_checkbutton_changed(self, checkbutton):
"""Called from callback in self.setup_errors_tab().
@ -20665,6 +20776,7 @@ class ComplexCatalogueItem(object):
download_item_obj \
= app_obj.download_manager_obj.download_list_obj.create_item(
self.video_obj,
None, # media.Scheduled object
'real', # override_operation_type
False, # priority_flag
False, # ignore_limits_flag
@ -25426,6 +25538,7 @@ class MountDriveDialogue(Gtk.Dialog):
box.add(grid)
grid.set_border_width(main_win_obj.spacing_size)
grid.set_row_spacing(main_win_obj.spacing_size)
grid.set_column_spacing(main_win_obj.spacing_size)
# (Actually, the grid width of the area to the right of the Tartube
# logo)
grid_width = 2
@ -25668,6 +25781,264 @@ class MountDriveDialogue(Gtk.Dialog):
self.destroy()
class NewbieDialogue(Gtk.Dialog):
"""Called by mainapp.TartubeApp.download_manager_finished().
Python class handling a dialogue window that advises a newbie what to do if
the download operation failed to check/download any videos.
Args:
main_win_obj (mainwin.MainWin): The parent main window
"""
# Standard class methods
def __init__(self, main_win_obj):
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25247 __init__')
# IV list - class objects
# -----------------------
# Tartube's main window
self.main_win_obj = main_win_obj
# IV list - Gtk widgets
# ---------------------
# IV list - other
# ---------------
# Flag set to True when various widgets are selected
self.update_flag = False
self.config_flag = False
self.change_flag = False
self.website_flag = False
self.issues_flag = False
self.show_flag = main_win_obj.app_obj.show_newbie_dialogue_flag
# Code
# ----
Gtk.Dialog.__init__(
self,
_('Nothing happened?'),
main_win_obj,
Gtk.DialogFlags.DESTROY_WITH_PARENT,
(
Gtk.STOCK_OK, Gtk.ResponseType.OK,
)
)
self.set_modal(True)
# 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)
grid.set_column_spacing(main_win_obj.spacing_size)
# (Actually, the grid width of the area to the right of the Tartube
# logo)
grid_width = 2
image = Gtk.Image.new_from_pixbuf(
main_win_obj.pixbuf_dict['newbie_icon'],
)
grid.attach(image, 0, 0, 1, 3)
label = Gtk.Label(
_('Make sure the downloader is installed and\nupdated'),
)
grid.attach(label, 1, 0, grid_width, 1)
button = Gtk.Button.new_with_label(
_('Update') + ' ' + self.main_win_obj.app_obj.get_downloader(),
)
grid.attach(button, 1, 1, grid_width, 1)
button.connect('clicked', self.on_update_button_clicked)
# Separator
grid.attach(Gtk.HSeparator(), 1, 2, grid_width, 1)
label2 = Gtk.Label(
_('Tell Tartube where to find the downloader'),
)
grid.attach(label2, 1, 3, grid_width, 1)
button2 = Gtk.Button.new_with_label(
_('Set the downloader\'s file path'),
)
grid.attach(button2, 1, 4, grid_width, 1)
button2.connect('clicked', self.on_config_button_clicked)
button3 = Gtk.Button.new_with_label(
_('Try a different downloader'),
)
grid.attach(button3, 1, 5, grid_width, 1)
button3.connect('clicked', self.on_change_button_clicked)
# Separator
grid.attach(Gtk.HSeparator(), 1, 6, grid_width, 1)
label3 = Gtk.Label(
_('Find more help'),
)
grid.attach(label3, 1, 7, grid_width, 1)
button4 = Gtk.Button.new_with_label(
_('Read the FAQ'),
)
grid.attach(button4, 1, 8, 1, 1)
button4.connect('clicked', self.on_website_button_clicked)
button5 = Gtk.Button.new_with_label(
_('Ask for help'),
)
grid.attach(button5, 2, 8, 1, 1)
button5.connect('clicked', self.on_issues_button_clicked)
# Separator
grid.attach(Gtk.HSeparator(), 1, 9, grid_width, 1)
checkbutton = Gtk.CheckButton()
grid.attach(checkbutton, 1, 10, grid_width, 1)
checkbutton.set_label(_('Always show this window'))
if self.show_flag:
checkbutton.set_active(True)
checkbutton.connect('toggled', self.on_checkbutton_toggled)
# Display the dialogue window
self.show_all()
# Public class methods
def on_change_button_clicked(self, button):
"""Called from a callback in self.__init__().
When the button is clicked, open the preferences window.
Args:
button (Gtk.Button): The widget clicked
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25248 on_change_button_clicked')
self.change_flag = True
self.destroy()
def on_checkbutton_toggled(self, checkbutton):
"""Called from a callback in self.__init__().
Enables/disables showing this dialogue window.
Args:
checkbutton (Gtk.CheckButton): The clicked widget
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25249 on_checkbutton_toggled')
if checkbutton.get_active():
self.show_flag = True
else:
self.show_flag = False
def on_config_button_clicked(self, button):
"""Called from a callback in self.__init__().
When the button is clicked, open the preferences window.
Args:
button (Gtk.Button): The widget clicked
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25250 on_config_button_clicked')
self.config_flag = True
self.destroy()
def on_issues_button_clicked(self, button):
"""Called from a callback in self.__init__().
When the button is clicked, open the Tartube issues page.
Args:
button (Gtk.Button): The widget clicked
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25251 on_issues_button_clicked')
self.issues_flag = True
self.destroy()
def on_update_button_clicked(self, button):
"""Called from a callback in self.__init__().
When the button is clicked, perform an update operation.
Args:
button (Gtk.Button): The widget clicked
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25252 on_update_button_clicked')
self.update_flag = True
self.destroy()
def on_website_button_clicked(self, button):
"""Called from a callback in self.__init__().
When the button is clicked, open the Tartube website.
Args:
button (Gtk.Button): The widget clicked
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('mwn 25253 on_update_button_clicked')
self.website_flag = True
self.destroy()
class RemoveLockFileDialogue(Gtk.Dialog):
"""Called by mainapp.TartubeApp.load_db().

View File

@ -3350,6 +3350,16 @@ class Scheduled(object):
# mainapp.TartubeApp.operation_limit_flag, etc
self.ignore_limits_flag = False
# Maximum simultaneous downloads. If the flag is True, the specified
# value overrides the equivalent mainapp.TartubeApp IV
# Maximum download bandwidth. If the flag is True, the specified
# value overrides the equivalent mainapp.TartubeApp IV
self.scheduled_num_worker = 2
self.scheduled_num_worker_apply_flag = False
self.scheduled_bandwidth = 500
self.scheduled_bandwidth_apply_flag = False
# Flag set to True if the download operation should encompass all
# media data objects
self.all_flag = True

View File

@ -943,10 +943,10 @@ class OptionsParser(object):
def parse(self, media_data_obj, options_manager_obj,
operation_type='real'):
operation_type='real', scheduled_obj=None):
"""Called by downloads.DownloadWorker.prepare_download() and
mainwin.MainWin.update_textbuffer().
mainwin.MainWin.update_textbuffer() and several other functions.
Converts the download options stored in the specified
options.OptionsManager object into a list of youtube-dl command line
@ -964,6 +964,10 @@ class OptionsParser(object):
'classic_real', 'classic_custom' (matching possible values of
downloads.DownloadManager.operation_type)
scheduled_obj (media.Scheduled): If a scheduled download is
involved, the corresponding object (so bandwidth limits can be
extracted)
Returns:
List of strings with all the youtube-dl command line options
@ -982,7 +986,7 @@ class OptionsParser(object):
# Set the 'min_filesize' and 'max_filesize' options
self.build_file_sizes(copy_dict)
# Set the 'limit_rate' option
self.build_limit_rate(copy_dict)
self.build_limit_rate(copy_dict, scheduled_obj)
# Set the 'proxy' option
self.build_proxy(copy_dict)
@ -1117,7 +1121,7 @@ class OptionsParser(object):
copy_dict['max_filesize_unit']
def build_limit_rate(self, copy_dict):
def build_limit_rate(self, copy_dict, scheduled_obj):
"""Called by self.parse().
@ -1126,14 +1130,41 @@ class OptionsParser(object):
Args:
copy_dict (dict): Copy of the original options dictionary.
copy_dict (dict): Copy of the original options dictionary
scheduled_obj (media.Scheduled): If a scheduled download is
involved, the corresponding object (so bandwidth limits can be
extracted)
"""
# Set the bandwidth limit (e.g. '50K')
if self.app_obj.bandwidth_apply_flag:
# Set the bandwidth limit (e.g. '50K'). If alternative performance
# limits currently apply, use that limit instead
if self.app_obj.download_manager_obj \
and scheduled_obj \
and scheduled_obj.scheduled_bandwidth_apply_flag:
# The bandwidth limit is divided equally between the workers
limit = int(
scheduled_obj.scheduled_bandwidth
/ len(self.app_obj.download_manager_obj.worker_list)
)
copy_dict['limit_rate'] = str(limit) + 'K'
elif self.app_obj.download_manager_obj \
and self.app_obj.download_manager_obj.alt_limits_flag \
and self.app_obj.alt_bandwidth_apply_flag:
limit = int(
self.app_obj.alt_bandwidth
/ self.app_obj.alt_num_worker
)
copy_dict['limit_rate'] = str(limit) + 'K'
elif self.app_obj.bandwidth_apply_flag:
limit = int(
self.app_obj.bandwidth_default
/ self.app_obj.num_worker_default

File diff suppressed because it is too large Load Diff

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.110'
__date__ = '28 Feb 2021'
__version__ = '2.3.120'
__date__ = '7 May 2021'
__copyright__ = 'Copyright \xa9 2019-2021 A S Lewis'
__license__ = """
Copyright \xa9 2019-2021 A S Lewis.