Updates/fixes for #364 #334 #378 #102 #385 #369 #383 #361, other new features

master
A S Lewis 2022-03-08 17:41:55 +00:00
parent be34641edd
commit bf2df53846
69 changed files with 6391 additions and 4022 deletions

View File

@ -59,14 +59,14 @@ For a full list of new features and fixes, see `recent changes <CHANGES>`__.
3 Downloads
===========
Latest version: **v2.3.367 (12 Feb 2022)**
Latest version: **v2.3.332 (8 Aug 2021)**
Official packages (also available from the `Github release page <https://github.com/axcore/tartube/releases>`__):
- `MS Windows (64-bit) installer <https://sourceforge.net/projects/tartube/files/v2.3.367/install-tartube-2.3.367-64bit.exe/download>`__ and `portable edition <https://sourceforge.net/projects/tartube/files/v2.3.367/tartube-2.3.367-64bit-portable.zip/download>`__ from Sourceforge
- `MS Windows (32-bit) installer <https://sourceforge.net/projects/tartube/files/v2.3.367/install-tartube-2.3.367-32bit.exe/download>`__ and `portable edition <https://sourceforge.net/projects/tartube/files/v2.3.367/tartube-2.3.367-32bit-portable/download>`__ from Sourceforge (but see `7.23 Doesn't work on 32-bit Windows`_)
- `DEB package (for Debian-based distros, e.g. Ubuntu, Linux Mint) <https://sourceforge.net/projects/tartube/files/v2.3.367/python3-tartube_2.3.367.deb/download>`__ from Sourceforge
- `RPM package (for RHEL-based distros, e.g. Fedora) <https://sourceforge.net/projects/tartube/files/v2.3.367/tartube-2.3.367.rpm/download>`__ from Sourceforge
- `MS Windows (64-bit) installer <https://sourceforge.net/projects/tartube/files/v2.3.332/install-tartube-2.3.332-64bit.exe/download>`__ and `portable edition <https://sourceforge.net/projects/tartube/files/v2.3.332/tartube-2.3.332-64bit-portable.zip/download>`__ from Sourceforge
- `MS Windows (32-bit) installer <https://sourceforge.net/projects/tartube/files/v2.3.332/install-tartube-2.3.332-32bit.exe/download>`__ and `portable edition <https://sourceforge.net/projects/tartube/files/v2.3.332/tartube-2.3.332-32bit-portable/download>`__ from Sourceforge (but see `7.23 Doesn't work on 32-bit Windows`_)
- `DEB package (for Debian-based distros, e.g. Ubuntu, Linux Mint) <https://sourceforge.net/projects/tartube/files/v2.3.332/python3-tartube_2.3.332.deb/download>`__ from Sourceforge
- `RPM package (for RHEL-based distros, e.g. Fedora) <https://sourceforge.net/projects/tartube/files/v2.3.332/tartube-2.3.332.rpm/download>`__ from Sourceforge
There are also some DEB/RPM packages marked STRICT. In these packages, updates to **youtube-dl** from within **Tartube** have been disabled. If **Tartube** is uploaded to a repository with lots of rules, such as the official Debian repository, then you should probably use the STRICT packages.
@ -170,7 +170,7 @@ If you want to perform a manual installation, you can follow this procedure, whi
5.2 Installation - MacOS
------------------------
**Several users have reported problems installing Tartube on MacOS. The authors do not use MacOS, so we don't know how to fix these problems. Apologies in advance!**
**Several users have reported problems installing Tartube on MacOS. The authors do not use MacOS and don't know how to fix these problems. Apologies in advance.**
MacOS users should use the following procedure (with thanks to JeremyShih):
@ -377,7 +377,7 @@ Packages can be created in the standard way. For example, an RPM package would b
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The procedure used to create the MS Windows installers is described in full in the
`installer scripts themselves <nsis/tatrube_install_64bit.nsi>`__.
`installer scripts themselves <nsis/tartube_install_64bit.nsi>`__.
6 Using Tartube
===============
@ -416,10 +416,11 @@ The procedure used to create the MS Windows installers is described in full in t
* `6.16.2 Favourite channels, playlists and folders`_
* `6.17 Combining channels, playlists and folders`_
* `6.17.1 Combining one channel and many playlists`_
* `6.17.2 Combining channels from different websites`_
* `6.17.3 Download all videos to a single folder`_
* `6.17.4 Download all videos to an external folder`_
* `6.17.5 External folders and yt-dlp`_
* `6.17.2 Extracting playlists from a channel`_
* `6.17.3 Combining channels from different websites`_
* `6.17.4 Download all videos to a single folder`_
* `6.17.5 Download all videos to an external folder`_
* `6.17.6 External folders and yt-dlp`_
* `6.18 Archiving videos`_
* `6.19 Performance limits`_
* `6.20 Managing databases`_
@ -1021,7 +1022,35 @@ The solution is to tell **Tartube** to store all the videos from the channel and
- Now, right-click on each playlist in turn, and then select **Playlist actions > Set download destination...**
- In the dialogue window, click **Use a different location**, select the name of the channel, then click the **OK** button.
6.17.2 Combining channels from different websites
6.17.2 Extracting playlists from a channel
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As described above, a creator might have a single channel, and several playlists. If there are a *lot* of playlists, it might take a long time to add them all to Tartube's database. However, there is a shortcut for YouTube channels.
- On the channel's webpage, click the **Playlists** tab
- Add a new channel to Tartube's database, using this URL (which should end in **../playlists**)
YouTube does not always send us the list of playlists; that's why it's necessary to click the **Playlists** tab, rather than the **Videos** tab, as we normally would.
Now you have two choices. If you want to keep the original channel in your database, without downloading duplicate videos, do this:
- In Tartube's main window, right-click the channel, and select **Check channel**
- When the operation has finished, right-click the channel and select **Show > Channel properties... > Associated Playlists**
- Select the button **Set the channel as the download destination**
- Click the **Add all playlists button**
- Click **OK** to close the window
- On the channel's webpage, click the **Videos** tab
- In Tartube's main window, right-click the channel, and select **Channel actions > Set URL...**, and replace the URL with the one ending in **../videos**
Alternatively, if you don't want to keep the original channel, do this:
- In Tartube's main window, right-click the channel, and select **Check channel**
- When the operation has finished, right-click the channel and select **Show > Channel properties... > Associated Playlists**
- Click the **Add all playlists button**
- Click **OK** to close the window
- When you're ready, delete the channel
6.17.3 Combining channels from different websites
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A creator might release their videos on **YouTube**, but also on a site like **Odysee**. Sometimes they will only release a particular video on **Odysee**.
@ -1037,7 +1066,7 @@ The solution is to tell **Tartube** to store videos from both channels in a sing
It doesn't matter which of the two channels you use as the download destination. There is also no limit to the number of parallel channels, so if a creator uploads videos to a dozen different websites, you can add them all.
6.17.3 Download all videos to a single folder
6.17.4 Download all videos to a single folder
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you don't care about keeping videos in separate directories/folders on your filesystem, you can download *all* videos into the **Unsorted videos** folder. Regardless of whether you have added one channel or a thousand, all the videos will be stored in that one location.
@ -1048,7 +1077,7 @@ If you don't care about keeping videos in separate directories/folders on your f
Alternatively, you could select **Temporary Videos**. If you do, videos will be deleted when you restart **Tartube** (and will not be re-downloaded in the future).
6.17.4 Download all videos to an external folder
6.17.5 Download all videos to an external folder
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, all files are downloaded into Tartube's data folder. Users often request that **Tartube** should be able to download videos to other locations in the filesystem, *while retaining those videos in Tartube's database.*
@ -1070,7 +1099,7 @@ If one of these reasons applies, then you can do this:
- Click the **Set** button, and choose an external folder
- When you're ready, click the **OK** button to apply your changes
6.17.5 External folders and yt-dlp
6.17.6 External folders and yt-dlp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Users of `yt-dlp <https://github.com/yt-dlp/yt-dlp/>`__ should be aware of the download option **--paths**, which may be more convenient in some situations. See the **yt-dlp** documentation for more information about how it works. In Tartube, it can be configured like this:
@ -1755,14 +1784,10 @@ A: Tartube is known to fail on Windows 7 systems that have not been updated for
A: New installers for MS Windows are not released frequently, but small updates and fixes *are* uploaded frequently to `Github <https://github.com/axcore/tartube/>`__. You can update your Tartube installation quite easily.
- Download the latest source code from `Github <https://github.com/axcore/tartube/>`__
- On your MS Windows system, enable hidden folders. (Use a search engine if you're not sure how to do that)
- Open the folder where Tartube's source code was installed. By default, that is
**C:\\Users\\YOURNAME\\AppData\\Local\\Tartube\\msys64\\home\\user\\tartube\\**
- Remove the folder (the one containing a file called **setup.py**)
- Replace it with the folder you just downloaded
- Check that the replacement folder contains a file called **setup.py**
- In Tartube's main menu, select **System > Show Tartube script folder**
- A folder opens containing files such as **setup.py** and **README.rst**
- Copy the downloaded source code into this folder, replacing old files with new ones
- Restart Tartube
A: On Linux, if the DEB or RPM package doesn't work, try installing via PyPI.
@ -1776,8 +1801,10 @@ It may be helpful to turn on debug messages (which are visible in a terminal win
On MS Windows, this is how to run **Tartube** from inside a terminal window:
- First, enable hidden folders on your system
- Then, run the application: **C:\\Users\\YOURNAME\\AppData\\Local\\Tartube\\msys64\\mingw64.exe**
- In Tartube's main menu, select **System > Show Tartube install folder**
- After the folder window opens, shut down Tartube
- In the folder window, click the **msys64** or **msys32** folder to open it
- Open the terminal by double-clicking **mingw64.exe** or **mingw32.exe**
- In this window, type these commands to start **Tartube** (paying attention to the *forward* slashes):
**cd /home/user/tartube**
@ -2033,7 +2060,7 @@ This is a **youtube-dl** issue. A general solution is described in `this post <h
The solution describes how to create a cookies.txt file, which can be specified as a download option.
Having created the file, in the same edit window, click the **General** tab. In the box labelled **Extra command line options**, you can add:
Having created the file, in the same edit window, click the **General** tab. In the box labelled **Additional download options**, you can add:
**--cookies=YT-cookies.txt**
@ -2070,19 +2097,20 @@ Unfortunately, it is not possible to switch between proxies while downloading a
A: **Tartube** is a Linux application. The installer for MS Windows contains not just **Tartube** itself, but a copy of Python and a whole bunch of essential graphics libraries, all of them ported to MS Windows.
Installing `FFmpeg <https://ffmpeg.org/>`__ will dramatically increase the size of the installed folder.
If you're at all suspicious that such a small application uses such a large installer, you are invited to examine the installed files for yourself:
**C:\\Users\\YOURNAME\\AppData\\Local\\Tartube**
(You might need to enable hidden folders; this can be done from the Control Panel.)
- In Tartube's main menu, select **System > Show Tartube install folder**
Everything is copied into this single folder. The installer doesn't modify the Windows registry, nor does it install files anywhere else (other than to the desktop and the Start Menu).
The NSIS scripts used to create the installers can be found here:
**C:\\Users\\YOURNAME\\AppData\\Local\\Tartube\\msys64\\home\\user\\tartube\\nsis**
- In Tartube's main menu, select **System > Show Tartube script folder**
- Click the **nsis** folder to open it
The scripts contain full instructions, so you should be able to create your own installer, which can be compared with the official one.
The scripts contain full instructions, so you should be able to create your own installer and then compare it to the official one.
7.23 Doesn't work on 32-bit Windows
-----------------------------------
@ -2165,7 +2193,12 @@ A: Tartube shows download statistics in a number of places, for example **Edit >
The graphs are created by `matplotlib <https://matplotlib.org/>`__, but none of the Tartube installers use it. If you want graphs, you have to install matplotlib yourself.
On Linux/BSD, use your system's software manager. On MS Windows, run the application **C:\Users\YOURNAME\AppData\Local\Tartube\msys64\mingw64.exe**, and in the new window type **pacman -S mingw-w64-x86_64-python-matplotlib**.
On Linux/BSD, use your system's software manager.
On MS Windows, do this:
- In Tartube's main menu, select **System > Open MSYS2 terminal...**
- In the terminal window, type **pacman -S mingw-w64-x86_64-python-matplotlib**.
7.31 Tartube is not visible in the system tray
----------------------------------------------

View File

@ -1 +1 @@
2.3.367
2.3.393

View File

@ -7,7 +7,7 @@ You want to contribute a translation to this project? Well, that's just great!
The simple way
--------------
1. Get a copy of the file `../tartube/po/messages.pot <tartube/po/messages.pot>`__
1. Get a copy of the file `../tartube/po/messages.pot <tartube/po/messages.pot>`__
2. Open it in a text editor
3. Read the notes below
4. Translate everything
@ -33,7 +33,7 @@ Header
======
The lines at the top must be changed from this::
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
@ -41,7 +41,7 @@ The lines at the top must be changed from this::
...to this::
# Tartube
# Copyright (C) 2019-2022 A S Lewis
# Copyright (C) 2019-2021 A S Lewis
# This file is distributed under the same license as the Tartube package.
The Project-Id-Version must be changed from this::
@ -106,7 +106,7 @@ Some pieces of text are spread across several lines, like this::
msgid ""
"The video file is missing from Tartube's data folder (try downloading the "
"video again!)"
"video again!)"
msgstr ""
The two strings are added to each other, producing a single string. You can do the same, if you want. (It doesn't matter how many strings you use).
@ -120,7 +120,7 @@ Multiple strings are combined without extra space characters. You should add the
"¡No puedo usar "
"YouTube "
"porque no hablo inglés!"
Please preserve capitalisation and punctuation::
msgid "Help!"
@ -128,10 +128,10 @@ Please preserve capitalisation and punctuation::
msgid "HELP!"
msgstr "¡AYUDA!"
msgid "help!"
msgstr "¡ayuda!"
One exception to this rule is underline/underscore characters. These denote keyboard shortcuts. Don't add the underline/underscore character to your translation::
msgid "_Channel"
@ -164,7 +164,7 @@ Some strings contain {0}, {1}, {2} and so on. These are substituted for somethin
Your translation must include the literal {0}, {1}, {2} and so on.
msgstr "blah blah blah {0} blah blah {1} blah blah"
If your translation uses a different word order, then treat the substrings like a word.
msgstr "Give to the {1} the {0}, please"
@ -177,7 +177,7 @@ Earlier version of Tartube used *directory* on Linux systems, and *folder* on MS
Downloads
=========
You have probably noticed two buttons in Tartube's main window: **Check all** and **Download all**.
You have probably noticed two buttons in Tartube's main window: **Check all** and **Download all**.
The first one fetches a list of videos from websites, but doesn't download the videos. The second one fetches a list of videos from websites AND downloads the videos.
@ -186,7 +186,7 @@ Throughout **messages.pot**, the word *check* is used with this meaning. You can
Operations
==========
Throughout **messages.pot**, the word *operation* has a fixed meaning. When Tartube is busy doing something, many buttons don't work (are greyed out).
Throughout **messages.pot**, the word *operation* has a fixed meaning. When Tartube is busy doing something, many buttons don't work (are greyed out).
For example, click the **Download all** button, and it is greyed out until the downloads are finished.

View File

@ -4,18 +4,19 @@ All files in the ../dialogue sub-directory
All files in the ../status sub-directory
All files in the ../win sub-directory
Author: Vectorgraphit
Author: Vectorgraphit
Source: https://www.iconfinder.com/icons/199499/
Author: bekeen studio
Source: https://www.iconfinder.com/bekeenstudio
This work is licensed under the Creative Commons Attribution 3.0 Unported
License. To view a copy of this license, visit
http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
All files in the ../large sub-directory
All files in the ../msg sub-directory
All files in the ../overlays sub-directory
All files in the ../small sub-directory
All files in the ../stock sub-directory
All files in the ../toolbar sub-directory
@ -25,7 +26,7 @@ Source: https://www.fatcow.com/free-icons
This work is licensed under the Creative Commons Attribution 3.0 Generic
License. To view a copy of this license, visit
http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
All files in the ../locale directory

BIN
icons/large/attention.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
icons/large/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

BIN
icons/small/attention.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

BIN
icons/small/keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
# Tartube v2.3.367 installer script for MS Windows
# Tartube v2.3.393 installer script for MS Windows
#
# Copyright (C) 2019-2022 A S Lewis
#
@ -249,7 +249,7 @@
;Name and file
Name "Tartube"
OutFile "install-tartube-2.3.367-32bit.exe"
OutFile "install-tartube-2.3.393-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.367"
# "DisplayVersion" "2.3.393"
# Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"

View File

@ -1,4 +1,4 @@
# Tartube v2.3.367 installer script for MS Windows
# Tartube v2.3.393 installer script for MS Windows
#
# Copyright (C) 2019-2022 A S Lewis
#
@ -249,7 +249,7 @@
;Name and file
Name "Tartube"
OutFile "install-tartube-2.3.367-64bit.exe"
OutFile "install-tartube-2.3.393-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.367"
# "DisplayVersion" "2.3.393"
# Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.367'
__date__ = '12 Feb 2022'
__version__ = '2.3.393'
__date__ = '8 Mar 2022'
__copyright__ = 'Copyright \xa9 2019-2022 A S Lewis'
__license__ = """
Copyright \xa9 2019-2022 A S Lewis.

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.367'
__date__ = '12 Feb 2022'
__version__ = '2.3.393'
__date__ = '8 Mar 2022'
__copyright__ = 'Copyright \xa9 2019-2022 A S Lewis'
__license__ = """
Copyright \xa9 2019-2022 A S Lewis.

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.367'
__date__ = '12 Feb 2022'
__version__ = '2.3.393'
__date__ = '8 Mar 2022'
__copyright__ = 'Copyright \xa9 2019-2022 A S Lewis'
__license__ = """
Copyright \xa9 2019-2022 A S Lewis.

View File

@ -1,4 +1,4 @@
.TH man 1 "12 Feb 2022" "2.3.367" "tartube man page"
.TH man 1 "8 Mar 2022" "2.3.393" "tartube man page"
.SH NAME
tartube \- GUI front-end for youtube-dl
.SH SYNOPSIS

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -112,6 +112,10 @@ class DialogueManager(threading.Thread):
"""
if self.app_obj.dialogue_disable_msg_flag:
print(msg)
return
if parent_win_obj is None:
parent_win_obj = self.main_win_obj
@ -183,6 +187,10 @@ class DialogueManager(threading.Thread):
"""
if self.app_obj.dialogue_disable_msg_flag:
print(msg)
return
if parent_win_obj is None:
parent_win_obj = self.main_win_obj

View File

@ -4261,6 +4261,15 @@ class VideoDownloader(object):
else:
comment_list = []
if 'playlist_id' in json_dict:
playlist_id = json_dict['playlist_id']
if 'playlist_title' in json_dict:
playlist_title = json_dict['playlist_title']
else:
playlist_title = None
else:
playlist_id = None
# Does an existing media.Video object match this video?
media_data_obj = self.download_item_obj.media_data_obj
video_obj = None
@ -4365,6 +4374,14 @@ class VideoDownloader(object):
if comment_list and app_obj.comment_store_flag:
video_obj.set_comments(comment_list)
if app_obj.store_playlist_id_flag \
and playlist_id is not None \
and not isinstance(video_obj.parent_obj, media.Folder):
video_obj.parent_obj.set_playlist_id(
playlist_id,
playlist_title,
)
# If downloading from a channel/playlist, remember the video's
# index. (The server supplies an index even for a channel, and
# the user might want to convert a channel to a playlist)
@ -4471,6 +4488,14 @@ class VideoDownloader(object):
if not video_obj.comment_list and comment_list:
video_obj.set_comments(comment_list)
if app_obj.store_playlist_id_flag \
and playlist_id is not None \
and not isinstance(video_obj.parent_obj, media.Folder):
video_obj.parent_obj.set_playlist_id(
playlist_id,
playlist_title,
)
# If downloading from a channel/playlist, remember the video's
# index. (The server supplies an index even for a channel, and
# the user might want to convert a channel to a playlist)

View File

@ -281,7 +281,7 @@ video_option_setup_list = [
'400', 'mp4 [1440p] <400>', False,
'401', 'mp4 [2160p] <401>', False,
'402', 'mp4 [2880p] <402>', False,
'571', 'mp4 [8k] <571', False,
'571', 'mp4 [8k] <571>', False,
'43', 'webm [360p] <43>', False,
'44', 'webm [480p] <44>', False,
'45', 'webm [720p] <45>', False,
@ -703,55 +703,36 @@ TOOLBAR_ICON_DICT = {
}
LARGE_ICON_DICT = {
'video_both_large': 'video_both.png',
'video_left_large': 'video_left.png',
'video_none_large': 'video_none.png',
'video_right_large': 'video_right.png',
'channel_both_large': 'channel_both.png',
'channel_left_large': 'channel_left.png',
'channel_none_large': 'channel_none.png',
'channel_right_large': 'channel_right.png',
'playlist_both_large': 'playlist_both.png',
'playlist_left_large': 'playlist_left.png',
'playlist_none_large': 'playlist_none.png',
'playlist_right_large': 'playlist_right.png',
'folder_both_large': 'folder_yellow_both.png',
'folder_left_large': 'folder_yellow_left.png',
'folder_none_large': 'folder_yellow_none.png',
'folder_right_large': 'folder_yellow_right.png',
'folder_private_both_large': 'folder_red_both.png',
'folder_private_left_large': 'folder_red_left.png',
'folder_private_none_large': 'folder_red_none.png',
'folder_private_right_large': 'folder_red_right.png',
'folder_fixed_both_large': 'folder_green_both.png',
'folder_fixed_left_large': 'folder_green_left.png',
'folder_fixed_none_large': 'folder_green_none.png',
'folder_fixed_right_large': 'folder_green_right.png',
'folder_temp_both_large': 'folder_blue_both.png',
'folder_temp_left_large': 'folder_blue_left.png',
'folder_temp_none_large': 'folder_blue_none.png',
'folder_temp_right_large': 'folder_blue_right.png',
'folder_no_parent_both_large': 'folder_black_both.png',
'folder_no_parent_left_large': 'folder_black_left.png',
'folder_no_parent_none_large': 'folder_black_none.png',
'folder_no_parent_right_large': 'folder_black_right.png',
'attention_large': 'attention.png',
'channel_large': 'channel.png',
'copy_large': 'copy.png',
'error_large': 'error.png',
'folder_large': 'folder_yellow.png',
'folder_fixed_large': 'folder_green.png',
'folder_no_parent_large': 'folder_black.png',
'folder_private_large': 'folder_red.png',
'folder_temp_large': 'folder_blue.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',
'playlist_large': 'playlist.png',
'question_large': 'question.png',
'video_large': 'video.png',
'warning_large': 'warning.png',
}
LARGE_ICON_COMPOSITE_LIST = [
'channel_large',
'folder_large',
'folder_fixed_large',
'folder_no_parent_large',
'folder_private_large',
'folder_temp_large',
'playlist_large',
'video_large',
]
SMALL_ICON_DICT = {
'video_small': 'video.png',
'channel_small': 'channel.png',
@ -761,6 +742,7 @@ SMALL_ICON_DICT = {
'archived_small': 'archived.png',
'arrow_up_small': 'arrow_up.png',
'arrow_down_small': 'arrow_down.png',
'attention_small': 'attention.png',
'check_small': 'check.png',
'comment_small': 'comment.png',
'debut_now_small': 'debut_now.png',
@ -775,6 +757,7 @@ SMALL_ICON_DICT = {
'folder_blue_small': 'folder_blue.png',
'folder_green_small': 'folder_green.png',
'folder_red_small': 'folder_red.png',
'keyboard_small': 'keyboard.png',
'likes_small': 'likes.png',
'have_file_small': 'have_file.png',
'live_now_small': 'live_now.png',
@ -823,7 +806,7 @@ THUMB_ICON_DICT = {
}
EXTERNAL_ICON_DICT = {
'ytdl-gui': 'youtube-dl-gui.png',
'ytdl_gui': 'youtube-dl-gui.png',
}
# (Replaces system stock icons, if not available)

View File

@ -293,7 +293,7 @@ class TartubeApp(Gtk.Application):
# Default window sizes (in pixels)
self.main_win_width = 1000
self.main_win_height = 750
self.main_win_height = 700
self.config_win_width = 650
self.config_win_height = 450
# Default slider position. This value applies to the sliders in the
@ -776,7 +776,7 @@ class TartubeApp(Gtk.Application):
self.classic_format_selection = None
# Flag set to False, if videos should be downloaded in that format, or
# True if they should be converted to the format using FFmpeg/AVConv
self.classic_format_convert_flag = False
self.classic_format_convert_flag = True
# Flag set to True, if pending URLs (still visible in the top half of
# the Classic Mode tab, or not yet downloaded in the bottom half)
# should be saved when Tartube shuts down, and restored (to the top
@ -1247,6 +1247,10 @@ class TartubeApp(Gtk.Application):
# 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
# Flag set to True if a dialogue should appear when the user opens the
# MSYS2 terminal from the main menu (on MS Windows only; ignored on
# other systems)
self.show_msys2_dialogue_flag = True
# Media data classes are those specified in media.py. Those class
# objects are media.Video (for individual videos), media.Channel,
@ -1706,6 +1710,12 @@ class TartubeApp(Gtk.Application):
# doesn't end with .../videos, the user should be prompted to add it
self.dialogue_yt_remind_flag = True
# On Virtualbox MSWin installations, dialogue windows can freeze
# Tartube, forcing a restart. Flag set to True if message dialogue
# windows (only) should be disabled, their messages being displayed
# in the terminal instead
self.dialogue_disable_msg_flag = False
# Flag set to True if, when downloading videos, youtube-dl should be
# passed, --download-archive, creating the file ytdl-archive.txt
# If the file exists, youtube-dl won't re-download a video a user has
@ -1743,6 +1753,11 @@ class TartubeApp(Gtk.Application):
# are marked as missing. Ignored if self.track_missing_videos_flag or
# self.track_missing_time_flag is False
self.track_missing_time_days = 14
# Flag set to True if Tartube should retrieve the playlist ID from each
# checked/downloaded video, and store it in the parent channel/
# playlist. (The user can use the collected IDs to get a list of
# playlists associated with a channel)
self.store_playlist_id_flag = True
# Flag set to True if a list of timestamps should be extracted from a
# video's .info.json file, when it is received
@ -2254,6 +2269,27 @@ class TartubeApp(Gtk.Application):
gen_options_action.connect('activate', self.on_menu_general_options)
self.add_action(gen_options_action)
# 'System' column
if os.name == 'nt':
open_msys2_action = Gio.SimpleAction.new('open_msys2_menu', None)
open_msys2_action.connect('activate', self.on_menu_open_msys2)
self.add_action(open_msys2_action)
show_install_action = Gio.SimpleAction.new(
'show_install_menu',
None,
)
show_install_action.connect('activate', self.on_menu_show_install)
self.add_action(show_install_action)
show_script_action = Gio.SimpleAction.new(
'show_script_menu',
None,
)
show_script_action.connect('activate', self.on_menu_show_script)
self.add_action(show_script_action)
# 'Media' column
add_video_menu_action = Gio.SimpleAction.new('add_video_menu', None)
add_video_menu_action.connect('activate', self.on_menu_add_video)
@ -2751,16 +2787,6 @@ class TartubeApp(Gtk.Application):
)
self.add_action(classic_add_urls_button_action)
classic_remove_button_action = Gio.SimpleAction.new(
'classic_remove_button',
None,
)
classic_remove_button_action.connect(
'activate',
self.on_button_classic_remove,
)
self.add_action(classic_remove_button_action)
classic_play_button_action = Gio.SimpleAction.new(
'classic_play_button',
None,
@ -2771,25 +2797,15 @@ class TartubeApp(Gtk.Application):
)
self.add_action(classic_play_button_action)
classic_move_up_button_action = Gio.SimpleAction.new(
'classic_move_up_button',
classic_open_button_action = Gio.SimpleAction.new(
'classic_open_button',
None,
)
classic_move_up_button_action.connect(
classic_open_button_action.connect(
'activate',
self.on_button_classic_move_up,
self.on_button_classic_open,
)
self.add_action(classic_move_up_button_action)
classic_move_down_button_action = Gio.SimpleAction.new(
'classic_move_down_button',
None,
)
classic_move_down_button_action.connect(
'activate',
self.on_button_classic_move_down,
)
self.add_action(classic_move_down_button_action)
self.add_action(classic_open_button_action)
classic_redownload_button_action = Gio.SimpleAction.new(
'classic_redownload_button',
@ -2821,6 +2837,36 @@ class TartubeApp(Gtk.Application):
)
self.add_action(classic_ffmpeg_button_action)
classic_move_up_button_action = Gio.SimpleAction.new(
'classic_move_up_button',
None,
)
classic_move_up_button_action.connect(
'activate',
self.on_button_classic_move_up,
)
self.add_action(classic_move_up_button_action)
classic_move_down_button_action = Gio.SimpleAction.new(
'classic_move_down_button',
None,
)
classic_move_down_button_action.connect(
'activate',
self.on_button_classic_move_down,
)
self.add_action(classic_move_down_button_action)
classic_remove_button_action = Gio.SimpleAction.new(
'classic_remove_button',
None,
)
classic_remove_button_action.connect(
'activate',
self.on_button_classic_remove,
)
self.add_action(classic_remove_button_action)
classic_clear_dl_button_action = Gio.SimpleAction.new(
'classic_clear_dl_button',
None,
@ -3874,8 +3920,9 @@ class TartubeApp(Gtk.Application):
self.classic_dir_list = json_dict['classic_dir_list']
self.classic_dir_previous = json_dict['classic_dir_previous']
if version >= 2003190: # v2.3.190
# (Before v2.3.369, this values was stored with leading zeroes)
self.classic_format_selection \
= json_dict['classic_format_selection']
= utils.strip_whitespace(json_dict['classic_format_selection'])
self.classic_format_convert_flag \
= json_dict['classic_format_convert_flag']
if version >= 2002129: # v2.2.129
@ -4034,6 +4081,9 @@ class TartubeApp(Gtk.Application):
if version >= 2003114: # v2.3.114
self.show_newbie_dialogue_flag \
= json_dict['show_newbie_dialogue_flag']
if version >= 2003376: # v2.3.376
self.show_msys2_dialogue_flag \
= json_dict['show_msys2_dialogue_flag']
if version >= 1003032: # v1.3.032
self.auto_clone_options_flag = json_dict['auto_clone_options_flag']
@ -4178,6 +4228,10 @@ class TartubeApp(Gtk.Application):
if version >= 2003130: # v2.3.130
self.dialogue_yt_remind_flag = json_dict['dialogue_yt_remind_flag']
if version >= 2003371: # v2.3.371
self.dialogue_disable_msg_flag \
= json_dict['dialogue_disable_msg_flag']
if version >= 1003018: # v1.3.018
self.allow_ytdl_archive_flag \
= json_dict['allow_ytdl_archive_flag']
@ -4194,6 +4248,9 @@ class TartubeApp(Gtk.Application):
= json_dict['track_missing_time_flag']
self.track_missing_time_days \
= json_dict['track_missing_time_days']
if version >= 2003382: # v2.3.382
self.store_playlist_id_flag \
= json_dict['store_playlist_id_flag']
if version >= 2003181: # v2.3.181
self.video_timestamps_extract_json_flag \
@ -4566,8 +4623,8 @@ class TartubeApp(Gtk.Application):
elif os.name == 'nt' and not 'PROGRAMFILES(X86)' in os.environ:
self.ytdl_update_dict['ytdl_update_win_64_no_dependencies'] = [
'..\\..\\..\\mingw64\\bin\python3.exe',
'..\\..\\..\\mingw64\\bin\pip3-script.py',
'..\\..\\..\\mingw32\\bin\python3.exe',
'..\\..\\..\\mingw32\\bin\pip3-script.py',
'install',
'-upgrade',
'--no-dependencies',
@ -4958,6 +5015,7 @@ class TartubeApp(Gtk.Application):
'operation_download_limit': self.operation_download_limit,
'show_newbie_dialogue_flag': self.show_newbie_dialogue_flag,
'show_msys2_dialogue_flag': self.show_msys2_dialogue_flag,
'auto_clone_options_flag': self.auto_clone_options_flag,
'auto_delete_options_flag': self.auto_delete_options_flag,
@ -5008,6 +5066,8 @@ class TartubeApp(Gtk.Application):
'dialogue_keep_open_flag': self.dialogue_keep_open_flag,
'dialogue_yt_remind_flag': self.dialogue_yt_remind_flag,
'dialogue_disable_msg_flag': self.dialogue_disable_msg_flag,
'allow_ytdl_archive_flag': self.allow_ytdl_archive_flag,
'classic_ytdl_archive_flag': \
self.classic_ytdl_archive_flag,
@ -5015,6 +5075,7 @@ class TartubeApp(Gtk.Application):
'track_missing_videos_flag': self.track_missing_videos_flag,
'track_missing_time_flag': self.track_missing_time_flag,
'track_missing_time_days': self.track_missing_time_days,
'store_playlist_id_flag': self.store_playlist_id_flag,
'video_timestamps_extract_json_flag': \
self.video_timestamps_extract_json_flag,
@ -6625,7 +6686,7 @@ class TartubeApp(Gtk.Application):
options_obj.options_dict['no_allow_dynamic_mpd'] = False
options_obj.options_dict['hls_split_discontinuity'] = False
if version < 2003237: # v2.3.237
if version < 2003237: # v2.3.237
# This version adds new IVs to media.Video objects
for media_data_obj in self.media_reg_dict.values():
@ -6633,7 +6694,7 @@ class TartubeApp(Gtk.Application):
media_data_obj.vid = None
media_data_obj.slice_list = []
if version < 2003251: # v2.3.251
if version < 2003251: # v2.3.251
# This version adds new options to
# ffmpeg_tartube.FFmpegOptionsManager
@ -6641,7 +6702,7 @@ class TartubeApp(Gtk.Application):
options_obj.options_dict['slice_mode'] = 'video'
options_obj.options_dict['slice_list'] = []
if version < 2003291: # v2.3.291
if version < 2003291: # v2.3.291
# This version adds a new IV to downloads.CustomDLManager objects
for custom_dl_obj in self.custom_dl_reg_dict.values():
@ -6653,7 +6714,7 @@ class TartubeApp(Gtk.Application):
else:
custom_dl_obj.dl_precede_flag = False
if version < 2003295: # v2.3.295
if version < 2003295: # v2.3.295
# This version adds a new IV to media.Scheduled objects
for scheduled_obj in self.scheduled_list:
@ -6664,7 +6725,7 @@ class TartubeApp(Gtk.Application):
else:
scheduled_obj.custom_dl_uid = None
if version < 2003304: # v2.3.304
if version < 2003304: # v2.3.304
# This version fixes an incorrect value for an IV in media.Folder
# objects
@ -6673,20 +6734,40 @@ class TartubeApp(Gtk.Application):
and media_data_obj.restrict_mode == 'free':
media_data_obj.restrict_mode = 'open'
if version < 2003314: # v2.3.314
if version < 2003314: # v2.3.314
# This version adds a new IV to media.Video objects
for media_data_obj in self.media_reg_dict.values():
if isinstance(media_data_obj, media.Video):
media_data_obj.comment_list = []
if version < 2003316: # v2.3.316
if version < 2003316: # v2.3.316
# This version removes an option to options.OptionsManager
for options_obj in options_obj_list:
if 'write_comments' in options_obj.options_dict:
del options_obj.options_dict['write_comments']
if version < 2003375: # v2.3.375
# This version adds new options to options.OptionsManager
for options_obj in options_obj_list:
# (yt-dlp only)
options_obj.options_dict['no_cookies'] = False
options_obj.options_dict['cookies_from_browser'] = ''
options_obj.options_dict['no_cookies_from_browser'] = True
if version < 2003382: # v2.3.382
# This version adds a new IV to media.Channel and media.Playlist
# objects
for media_data_obj in self.media_reg_dict.values():
if isinstance(media_data_obj, media.Channel) \
or isinstance(media_data_obj, media.Playlist):
media_data_obj.playlist_id_dict = {}
# --- Do this last, or call to .check_integrity_db() fails -----------
# --------------------------------------------------------------------
@ -8276,20 +8357,20 @@ class TartubeApp(Gtk.Application):
self.ytdl_path_pypi,
)
if os.path.isfile(self.ytdl_path_default):
self.ytdl_path = self.ytdl_path_default
elif os.path.isfile(pypi_path) \
if os.path.isfile(pypi_path) \
or __main__.__pkg_install_flag__:
self.ytdl_path = self.ytdl_path_pypi
elif os.path.isfile(self.ytdl_path_default):
self.ytdl_path = self.ytdl_path_default
else:
self.ytdl_path = self.ytdl_bin
if not __main__.__pkg_strict_install_flag__:
if self.ytdl_path == self.ytdl_path_default:
self.ytdl_update_current = 'ytdl_update_default_path'
elif self.ytdl_path == self.ytdl_path_pypi:
if self.ytdl_path == self.ytdl_path_pypi:
self.ytdl_update_current = 'ytdl_update_pip3_recommend'
elif self.ytdl_path == self.ytdl_path_default:
self.ytdl_update_current = 'ytdl_update_default_path'
else:
self.ytdl_update_current = 'ytdl_update_local_path'
@ -10545,7 +10626,7 @@ class TartubeApp(Gtk.Application):
msg = _('Update operation halted')
else:
msg = _('Update operation complete') \
+ '\n\n' + self.get_downloader() + ' ' \
+ '\n\n' + self.get_downloader(wiz_win_obj) + ' ' \
+ _('version:') + ' '
if ytdl_version is not None:
msg += ytdl_version
@ -11021,6 +11102,7 @@ class TartubeApp(Gtk.Application):
utils.debug_time('app 8991 info_manager_finished')
# Import IVs from info.InfoManager, before it is destroyed
video_obj = self.info_manager_obj.video_obj
info_type = self.info_manager_obj.info_type
success_flag = self.info_manager_obj.success_flag
output_list = self.info_manager_obj.output_list.copy()
@ -11055,9 +11137,26 @@ class TartubeApp(Gtk.Application):
if url_string is not None and url_string != '':
utils.open_file(self, self.temp_test_dir)
# Then show a dialogue window/desktop notification, if allowed
if self.operation_dialogue_mode != 'default':
# Show a confirmation, if allowed
if self.operation_dialogue_mode == 'dialogue' \
and (info_type == 'formats' or info_type == 'subs') \
and success_flag:
# Show a custom dialogue window, that enables the user to modify or
# apply download options directly
dialogue_win = mainwin.FormatsSubsDialogue(
self.main_win_obj,
video_obj,
info_type,
)
response = dialogue_win.run()
dialogue_win.destroy()
elif self.operation_dialogue_mode != 'default':
# Then show a message dialogue window/desktop notification, if
# allowed
if info_type != 'version' or not success_flag:
if not success_flag:
@ -12438,6 +12537,21 @@ class TartubeApp(Gtk.Application):
json_dict['chapters'],
)
if self.store_playlist_id_flag \
and 'playlist_id' in json_dict \
and not isinstance(video_obj.parent_obj, media.Folder):
if 'playlist_title' in json_dict:
video_obj.parent_obj.set_playlist_id(
json_dict['playlist_id'],
json_dict['playlist_title'],
)
else:
video_obj.parent_obj.set_playlist_id(
json_dict['playlist_id'],
None,
)
def update_video_from_filesystem(self, video_obj, video_path,
override_flag=False):
@ -20970,6 +21084,58 @@ class TartubeApp(Gtk.Application):
self.main_win_obj.classic_mode_tab_move_row(False)
def on_button_classic_open(self, action, par):
"""Called from a callback in self.do_startup().
Opens the destination(s) of any videos downloaded from the selected
rows in the Classic Progress List.
Args:
action (Gio.SimpleAction): Object generated by Gio
par (None): Ignored
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('app 16911 on_button_classic_open')
selection = self.main_win_obj.classic_progress_treeview.get_selection()
(model, path_list) = selection.get_selected_rows()
if not path_list:
# Nothing selected
return
# Get the the dummy media.Video objects for each selected row, and
# produce a list of destinations, ignoring duplicates
dir_list = []
for path in path_list:
this_iter = model.get_iter(path)
dbid = model[this_iter][0]
dummy_obj = self.main_win_obj.classic_media_dict[dbid]
if dummy_obj.dummy_dir \
and not dummy_obj.dummy_dir in dir_list:
dir_list.append(dummy_obj.dummy_dir)
if not dir_list:
self.dialogue_manager_obj.show_msg_dialogue(
_('No destination(s) to show'),
'error',
'ok',
)
else:
for this_dir in dir_list:
utils.open_file(self, this_dir)
def on_button_classic_play(self, action, par):
"""Called from a callback in self.do_startup().
@ -22728,6 +22894,35 @@ class TartubeApp(Gtk.Application):
config.SystemPrefWin(self, 'live')
def on_menu_open_msys2(self, action, par):
"""Called from a callback in self.do_startup().
On MS Windows, opens the MINGW terminal for MSYS2.
Args:
action (Gio.SimpleAction): Object generated by Gio
par (None): Ignored
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18438 on_menu_open_msys2')
if 'PROGRAMFILES(X86)' in os.environ:
utils.open_file(self, '..\\..\\..\\mingw64.exe')
else:
utils.open_file(self, '..\\..\\..\\mingw32.exe')
if self.show_msys2_dialogue_flag:
dialogue_win = mainwin.MSYS2Dialogue(self.main_win_obj)
dialogue_win.run()
dialogue_win.destroy()
def on_menu_refresh_db(self, action, par):
"""Called from a callback in self.do_startup().
@ -22858,6 +23053,51 @@ class TartubeApp(Gtk.Application):
self.mark_folder_hidden(media_data_obj, False)
def on_menu_show_install(self, action, par):
"""Called from a callback in self.do_startup().
On MS Windows (only), opens Tartube's installation folder.
Args:
action (Gio.SimpleAction): Object generated by Gio
par (None): Ignored
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18561 on_menu_show_install')
# (This path assumes that the standard NSIS installation script was
# used to install Tartube)
utils.open_file(
self,
self.script_parent_dir + '\\..\\..\\..\\..\\..',
)
def on_menu_show_script(self, action, par):
"""Called from a callback in self.do_startup().
On MS Windows (only), opens Tartube's home folder.
Args:
action (Gio.SimpleAction): Object generated by Gio
par (None): Ignored
"""
if DEBUG_FUNC_FLAG:
utils.debug_time('app 18561 on_menu_show_install')
utils.open_file(self, self.script_parent_dir)
def on_menu_stop_soon(self, action, par):
"""Called from a callback in self.do_startup().
@ -23968,10 +24208,21 @@ class TartubeApp(Gtk.Application):
self.dialogue_copy_clipboard_flag = True
def set_dialogue_disable_msg_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 19519 set_dialogue_disable_msg_flag')
if not flag:
self.dialogue_disable_msg_flag = False
else:
self.dialogue_disable_msg_flag = True
def set_dialogue_keep_open_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 19518 set_dialogue_keep_open_flag')
utils.debug_time('app 19520 set_dialogue_keep_open_flag')
if not flag:
self.dialogue_keep_open_flag = False
@ -25017,10 +25268,21 @@ class TartubeApp(Gtk.Application):
self.show_custom_icons_flag = True
def set_show_msys2_dialogue_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 20370 set_show_msys2_dialogue_flag')
if not flag:
self.show_msys2_dialogue_flag = False
else:
self.show_msys2_dialogue_flag = True
def set_show_newbie_dialogue_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 20370 set_show_newbie_dialogue_flag')
utils.debug_time('app 20371 set_show_newbie_dialogue_flag')
if not flag:
self.show_newbie_dialogue_flag = False
@ -25214,10 +25476,21 @@ class TartubeApp(Gtk.Application):
self.split_video_subdir_flag = True
def set_store_playlist_id_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 20462 set_store_playlist_id_flag')
if not flag:
self.store_playlist_id_flag = False
else:
self.store_playlist_id_flag = True
def set_system_error_show_flag(self, flag):
if DEBUG_FUNC_FLAG:
utils.debug_time('app 20462 set_system_error_show_flag')
utils.debug_time('app 20463 set_system_error_show_flag')
if not flag:
self.system_error_show_flag = False

File diff suppressed because it is too large Load Diff

View File

@ -1630,6 +1630,58 @@ class GenericRemoteContainer(GenericContainer):
)
def set_playlist_id(self, playlist_id, playlist_title):
# (Don't overwrite an existing entry unless the existing name is blank)
if not playlist_id in self.playlist_id_dict \
or self.playlist_id_dict[playlist_id] is None:
self.playlist_id_dict[playlist_id] = playlist_title
def reset_playlist_id(self):
self.playlist_id_dict = {}
def extract_playlist_id(self, app_obj):
"""Can be called by anything, but mostly called by
config.ChannelPlaylistEditWin.on_reset_assoc_playlist_button_clicked().
Reads the .info.json file for every child media.Video object, extracts
the 'playlist_id' and 'playlist_title' fields, and uses them to update
self.playlist_id_dict.
Does not check mainapp.TartubeApp.store_playlist_id_flag; the calling
code should do that, if necessary.
Args:
app_obj (mainapp.TartubeApp): The main application
"""
self.playlist_id_dict = {}
for child_obj in self.child_list:
json_path = child_obj.check_actual_path_by_ext(
app_obj,
'.info.json',
)
if json_path is not None:
json_dict = app_obj.file_manager_obj.load_json(json_path)
if 'playlist_id' in json_dict:
if 'playlist_title' in json_dict:
self.playlist_id_dict[json_dict['playlist_id']] \
= json_dict['playlist_title']
else:
self.playlist_id_dict[json_dict['playlist_id']] = None
def set_source(self, source):
self.source = source
@ -3334,6 +3386,14 @@ class Channel(GenericRemoteContainer):
self.new_count = 0
self.waiting_count = 0
# Dictionary of playlist IDs extracted from every video in this channel
# (can be used to compile a list of playlists associated with a
# channel)
# Dictionary in the form
# playlist_id_dict[playlist_id] = playlist_title
# ...where 'playlist_title' is None if the title is not known
self.playlist_id_dict = {}
# List of error/warning messages generated the last time the channel
# was checked or downloaded. Both set to empty lists if the channel
# has never been checked or downloaded, or if there was no error/
@ -3553,6 +3613,16 @@ class Playlist(GenericRemoteContainer):
self.new_count = 0
self.waiting_count = 0
# Dictionary of playlist IDs extracted from every video in this
# playlist (will usually contain just one key-value pair; however, if
# the URL self.source is actually a channel, not a playlist, it may
# contain many key-value pairs; presumably the user will want to
# convert the media.Playlist to a media.Channel at some point)
# Dictionary in the form
# playlist_id_dict[playlist_id] = playlist_title
# ...where 'playlist_title' is None if the title is not known
self.playlist_id_dict = {}
# List of error/warning messages generated the last time the channel
# was checked or downloaded. Both set to empty lists if the channel
# has never been checked or downloaded, or if there was no error/

View File

@ -380,6 +380,16 @@ class OptionsManager(object):
no_clean_info_json (bool): If True, writes all fields to the infojson
(default is to remove some private fields)
no_cookies (bool): If True, does not read/dump cookies from/to file
(default)
cookies_from_browser (str): The name of the browser and (optionally)
the name/path of the profile to load cookies from; a string in the
from BROWSER[+KEYRING][:PROFILE]
no_cookies_from_browser (bool): If true, does not load cookies from the
browser (default)
[Internet Shortcut Options]
write_link (bool): If True, writes an internet shortcut file, depending
@ -804,6 +814,9 @@ class OptionsManager(object):
'force_overwrites': False,
'write_playlist_metafiles': False,
'no_clean_info_json': False,
'no_cookies': False,
'cookies_from_browser': '',
'no_cookies_from_browser': True,
# (Internet Shortcut Options)
'write_link': False,
'write_url_link': False,
@ -940,7 +953,7 @@ class OptionsParser(object):
# -i, --ignore-errors
OptionHolder('ignore_errors', '-i', False),
# --abort-on-error
OptionHolder('abort_on_error', '--abort-on-error ', False),
OptionHolder('abort_on_error', '--abort-on-error', False),
# NETWORK OPTIONS
# --proxy URL
OptionHolder('proxy', '--proxy', ''),
@ -1043,7 +1056,7 @@ class OptionsParser(object):
False,
),
# --prefer-insecure
OptionHolder('prefer_insecure', '--prefer-insecure ', False),
OptionHolder('prefer_insecure', '--prefer-insecure', False),
# --user-agent UA
OptionHolder('user_agent', '--user-agent', ''),
# --referer URL
@ -1172,6 +1185,16 @@ class OptionsParser(object):
),
# --no-clean-infojson
OptionHolder('no_clean_info_json', '--no-clean-infojson', False),
# --no-cookies
OptionHolder('no_cookies', '--no-cookies', False),
# --cookies-from-browser BROWSER[+KEYRING][:PROFILE]
OptionHolder('cookies_from_browser', '--cookies-from-browser', ''),
# --no-cookies-from-browser
OptionHolder(
'no_cookies_from_browser',
'--no-cookies-from-browser',
True,
),
# (Internet Shortcut Options)
# --write-link
OptionHolder('write_link', '--write-link', False),

File diff suppressed because it is too large Load Diff

View File

@ -42,8 +42,8 @@ import mainapp
# 'Global' variables
__packagename__ = 'tartube'
__version__ = '2.3.367'
__date__ = '12 Feb 2022'
__version__ = '2.3.393'
__date__ = '8 Mar 2022'
__copyright__ = 'Copyright \xa9 2019-2022 A S Lewis'
__license__ = """
Copyright \xa9 2019-2022 A S Lewis.

View File

@ -576,22 +576,18 @@ class UpdateManager(threading.Thread):
"""
substring = re.search(
regex_list = [
'Requirement already up\-to\-date.*\(([^\(\)]+)\)\s*$',
stdout,
)
if substring:
self.ytdl_version = substring.group(1)
else:
substring = re.search(
'Successfully installed ' + downloader + '\-([^\(\)]+)\s*$',
stdout,
)
'Requirement already satisfied.*\(([^\(\)]+)\)\s*$',
'yt-dlp is up to date \(([^\(\)]+)\)\s*$',
'Successfully installed ' + downloader + '\-([^\(\)]+)\s*$',
]
for regex in regex_list:
substring = re.search(regex, stdout)
if substring:
self.ytdl_version = substring.group(1)
return
def is_child_process_alive(self):

View File

@ -950,6 +950,25 @@ def convert_slices_to_clips(app_obj, custom_dl_obj, slice_list, temp_flag):
return clip_list
def convert_youtube_id_to_playlist(youtube_id):
"""Can be called by anything.
Converts an ID provided by YouTube into the full URL for a playlist.
Args:
youtube_id (str): The YouTube playlist ID
Returns:
The full URL for the playlist
"""
return 'https://www.youtube.com/playlist?list=' + youtube_id
def convert_youtube_id_to_rss(media_type, youtube_id):
"""Can be called by anything; usually called by
@ -974,6 +993,25 @@ def convert_youtube_id_to_rss(media_type, youtube_id):
+ '_id=' + youtube_id
def convert_youtube_id_to_video(youtube_id):
"""Can be called by anything.
Converts an ID provided by YouTube into the full URL for the video.
Args:
youtube_id (str): The YouTube video ID
Returns:
The full URL for the video
"""
return 'https://www.youtube.com/watch?v=' + youtube_id
def convert_youtube_to_hooktube(url):
"""Can be called by anything.
@ -1950,11 +1988,18 @@ custom_dl_obj=None, divert_mode=None):
os.path.abspath(os.path.join(dl_path, 'ytdl-archive.txt')),
)
# Fetch video comments, if required
if app_obj.comment_fetch_flag \
and app_obj.ytdl_fork is not None \
and app_obj.ytdl_fork == 'yt-dlp':
options_list.append('--write-comments')
# yt-dlp options
if app_obj.ytdl_fork is not None and app_obj.ytdl_fork == 'yt-dlp':
# Fetch video comments, if required
if app_obj.comment_fetch_flag:
options_list.append('--write-comments')
# On MS Windows, if --restrict-filenames is not specified, then insert
# --windows-filenames. (I can't be sure that yt-dlp knows it is
# running on MS Windows, when running inside MSYS2)
if os.name == 'nt' and not '--restrict-filenames' in options_list:
options_list.append('--windows-filenames')
# Show verbose output (youtube-dl debugging mode), if required
if app_obj.ytdl_write_verbose_flag:
@ -2160,6 +2205,15 @@ classic_flag):
mod_options_list.append('-f')
mod_options_list.append('(bestvideo+bestaudio/best)[protocol!*=dash]')
# On MS Windows and yt-dlp, if --restrict-filenames is not specified, then
# insert --windows-filenames. (I can't be sure that yt-dlp knows it is
# running on MS Windows, when running inside MSYS2)
if app_obj.ytdl_fork is not None \
and app_obj.ytdl_fork == 'yt-dlp' \
and os.name == 'nt' \
and not '--restrict-filenames' in mod_options_list:
mod_options_list.append('--windows-filenames')
# Supply youtube-dl with the path to the ffmpeg binary, if the user has
# provided one
if app_obj.ffmpeg_path is not None:
@ -2300,6 +2354,15 @@ clip_title, start_stamp, stop_stamp, custom_dl_obj, divert_mode, classic_flag):
mod_options_list.append('-f')
mod_options_list.append('(bestvideo+bestaudio/best)[protocol!*=dash]')
# On MS Windows and yt-dlp, if --restrict-filenames is not specified, then
# insert --windows-filenames. (I can't be sure that yt-dlp knows it is
# running on MS Windows, when running inside MSYS2)
if app_obj.ytdl_fork is not None \
and app_obj.ytdl_fork == 'yt-dlp' \
and os.name == 'nt' \
and not '--restrict-filenames' in mod_options_list:
mod_options_list.append('--windows-filenames')
# Supply youtube-dl with the path to the ffmpeg binary, if the user has
# provided one
if app_obj.ffmpeg_path is not None:

View File

@ -598,7 +598,7 @@ class SetupWizWin(GenericWizWin):
self.data_dir = None
# The name of the youtube-dl fork to use, by default ('None' when
# youtube-dl itself should be used)
self.ytdl_fork = None
self.ytdl_fork = 'yt-dlp'
# Flag set to True if yt-dlp (only), when installed via pip, should be
# installed without dependencies
if os.name == 'nt':