Git #271/281/288, add newbie dialogue
131
README.rst
@ -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.
|
||||
|
||||
|
BIN
icons/dialogue/newbie_icon_64.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
icons/large/limits_off.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
icons/large/limits_on.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
7192
locale/nl_NL/LC_MESSAGES/messages.pot
Normal 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"
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Name=Tartube
|
||||
Version=2.3.110
|
||||
Version=2.3.120
|
||||
Exec=tartube
|
||||
Icon=tartube
|
||||
Type=Application
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 50 KiB |
2
setup.py
@ -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',
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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().
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|