Update to v1.3.007
16
CHANGES
@ -1,4 +1,18 @@
|
|||||||
v1.3.0 (20 Sep 2019)
|
v1.3.007 (20 Dec 2019)
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MAJOR FIXES
|
||||||
|
- v1.3.007 was completely broken when replacing an earlier installation. Fixed
|
||||||
|
- When Tartube's data directory was copied from one place to another (for
|
||||||
|
example, from one external drive to another), Tartube did not adapt to the
|
||||||
|
change very well. The way file paths are stored in Tartube's database has
|
||||||
|
been changed to eliminate this problem
|
||||||
|
|
||||||
|
MINOR FIXES
|
||||||
|
- Fixed an invalid time value which (sometimes) prevented a refresh operation
|
||||||
|
from completing correctly
|
||||||
|
|
||||||
|
v1.3.0 (20 Dec 2019)
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
MAJOR NEW FEATURES
|
MAJOR NEW FEATURES
|
||||||
- Tartube on MS Windows did not recognise FFmpeg or AVConv. You can now tell
|
- Tartube on MS Windows did not recognise FFmpeg or AVConv. You can now tell
|
||||||
|
39
README.rst
@ -11,40 +11,9 @@ Works with YouTube, BitChute, and hundreds of other websites
|
|||||||
* `1 Introduction`_
|
* `1 Introduction`_
|
||||||
* `2 Why should I use Tartube?`_
|
* `2 Why should I use Tartube?`_
|
||||||
* `3 Quick start guide`_
|
* `3 Quick start guide`_
|
||||||
* `3.1 MS Windows`_
|
|
||||||
* `3.2 Linux/BSD`_
|
|
||||||
* `4 Downloads`_
|
* `4 Downloads`_
|
||||||
* `5 Installation`_
|
* `5 Installation`_
|
||||||
* `5.1 Installation - MS Windows`_
|
|
||||||
* `5.2 Installation - MacOS`_
|
|
||||||
* `5.3 Installation - Linux/BSD`_
|
|
||||||
* `5.3.1 Linux/BSD Installation requirements`_
|
|
||||||
* `5.3.2 Optional dependencies`_
|
|
||||||
* `5.3.3 Install using PyPI`_
|
|
||||||
* `5.3.4 Install from source`_
|
|
||||||
* `5.3.5 Run without installing`_
|
|
||||||
* `6 Getting started`_
|
* `6 Getting started`_
|
||||||
* `6.1 Choose where to save videos`_
|
|
||||||
* `6.2 Check youtube-dl is updated`_
|
|
||||||
* `6.3 Setting youtube-dl's location`_
|
|
||||||
* `6.4 Setting the location of FFmpeg / AVConv`_
|
|
||||||
* `6.4.1 On MS Windows`_
|
|
||||||
* `6.4.2 On Linux/BSD`_
|
|
||||||
* `6.5 Introducing system folders`_
|
|
||||||
* `6.6 Adding videos`_
|
|
||||||
* `6.7 Adding channels and playlists`_
|
|
||||||
* `6.8 Adding folders`_
|
|
||||||
* `6.9 Things you can do`_
|
|
||||||
* `6.10 General download options`_
|
|
||||||
* `6.11 Other download options`_
|
|
||||||
* `6.12 Favourite videos`_
|
|
||||||
* `6.13 Watching videos`_
|
|
||||||
* `6.14 Combining channels, playlists and folders`_
|
|
||||||
* `6.14.1 Combining one channel and many playlists`_
|
|
||||||
* `6.14.2 Combining channels from different websites`_
|
|
||||||
* `6.14.3 Download all videos to a single folder`_
|
|
||||||
* `6.15 Archiving videos`_
|
|
||||||
* `6.16 Exporting/importing the Tartube database`_
|
|
||||||
* `7. Frequently-Asked Questions`_
|
* `7. Frequently-Asked Questions`_
|
||||||
* `8. Future plans`_
|
* `8. Future plans`_
|
||||||
* `9. Known issues`_
|
* `9. Known issues`_
|
||||||
@ -110,11 +79,11 @@ Problems can be reported at `our GitHub page <https://github.com/axcore/tartube/
|
|||||||
4 Downloads
|
4 Downloads
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Latest version: **v1.3.0 (20 Dec 2019)**
|
Latest version: **v1.3.007 (20 Dec 2019)**
|
||||||
|
|
||||||
- `MS Windows (32-bit) installer <https://sourceforge.net/projects/tartube/files/v1.3.0/install-tartube-1.3.0-32bit.exe/download>`__ from Sourceforge
|
- `MS Windows (32-bit) installer <https://sourceforge.net/projects/tartube/files/v1.3.007/install-tartube-1.3.007-32bit.exe/download>`__ from Sourceforge
|
||||||
- `MS Windows (64-bit) installer <https://sourceforge.net/projects/tartube/files/v1.3.0/install-tartube-1.3.0-64bit.exe/download>`__ from Sourceforge
|
- `MS Windows (64-bit) installer <https://sourceforge.net/projects/tartube/files/v1.3.007/install-tartube-1.3.007-64bit.exe/download>`__ from Sourceforge
|
||||||
- `Source code <https://sourceforge.net/projects/tartube/files/v1.3.0/tartube_v1.3.0.tar.gz/download>`__ from Sourceforge
|
- `Source code <https://sourceforge.net/projects/tartube/files/v1.3.007/tartube_v1.3.007.tar.gz/download>`__ from Sourceforge
|
||||||
- `Source code <https://github.com/axcore/tartube>`__ and `support <https://github.com/axcore/tartube/issues>`__ from GitHub
|
- `Source code <https://github.com/axcore/tartube>`__ and `support <https://github.com/axcore/tartube/issues>`__ from GitHub
|
||||||
|
|
||||||
5 Installation
|
5 Installation
|
||||||
|
@ -5,13 +5,14 @@ All files in the ../win sub-directory
|
|||||||
|
|
||||||
Author: Vectorgraphit
|
Author: Vectorgraphit
|
||||||
Source: https://www.iconfinder.com/icons/199499/
|
Source: https://www.iconfinder.com/icons/199499/
|
||||||
|
Author: bekeen studio
|
||||||
|
https://www.iconfinder.com/bekeenstudio
|
||||||
|
|
||||||
This work is licensed under the Creative Commons Attribution 3.0 Unported
|
This work is licensed under the Creative Commons Attribution 3.0 Unported
|
||||||
License. To view a copy of this license, visit
|
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.
|
Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
|
||||||
|
|
||||||
|
|
||||||
All files in the ../large sub-directory
|
All files in the ../large sub-directory
|
||||||
All files in the ../small sub-directory
|
All files in the ../small sub-directory
|
||||||
All files in the ../template sub-directory
|
All files in the ../template sub-directory
|
||||||
|
BIN
icons/dialogue/system_icon_xmas_64.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
icons/win/system_icon_xmas_128.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
icons/win/system_icon_xmas_16.png
Normal file
After Width: | Height: | Size: 895 B |
BIN
icons/win/system_icon_xmas_24.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
icons/win/system_icon_xmas_256.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
icons/win/system_icon_xmas_32.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
icons/win/system_icon_xmas_48.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
icons/win/system_icon_xmas_512.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
icons/win/system_icon_xmas_64.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
@ -1,4 +1,4 @@
|
|||||||
# Tartube v1.3.0 installer script for MS Windows
|
# Tartube v1.3.007 installer script for MS Windows
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019 A S Lewis
|
# Copyright (C) 2019 A S Lewis
|
||||||
#
|
#
|
||||||
@ -139,7 +139,7 @@
|
|||||||
|
|
||||||
;Name and file
|
;Name and file
|
||||||
Name "Tartube"
|
Name "Tartube"
|
||||||
OutFile "install-tartube-1.3.0-32bit.exe"
|
OutFile "install-tartube-1.3.007-32bit.exe"
|
||||||
|
|
||||||
;Default installation folder
|
;Default installation folder
|
||||||
InstallDir "$LOCALAPPDATA\Tartube"
|
InstallDir "$LOCALAPPDATA\Tartube"
|
||||||
@ -244,7 +244,7 @@ Section "Tartube" SecClient
|
|||||||
"Publisher" "A S Lewis"
|
"Publisher" "A S Lewis"
|
||||||
WriteRegStr HKLM \
|
WriteRegStr HKLM \
|
||||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \
|
"Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \
|
||||||
"DisplayVersion" "1.3.0"
|
"DisplayVersion" "1.3.007"
|
||||||
|
|
||||||
# Create uninstaller
|
# Create uninstaller
|
||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Tartube v1.3.0 installer script for MS Windows
|
# Tartube v1.3.007 installer script for MS Windows
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019 A S Lewis
|
# Copyright (C) 2019 A S Lewis
|
||||||
#
|
#
|
||||||
@ -140,7 +140,7 @@
|
|||||||
|
|
||||||
;Name and file
|
;Name and file
|
||||||
Name "Tartube"
|
Name "Tartube"
|
||||||
OutFile "install-tartube-1.3.0-64bit.exe"
|
OutFile "install-tartube-1.3.007-64bit.exe"
|
||||||
|
|
||||||
;Default installation folder
|
;Default installation folder
|
||||||
InstallDir "$LOCALAPPDATA\Tartube"
|
InstallDir "$LOCALAPPDATA\Tartube"
|
||||||
@ -245,7 +245,7 @@ Section "Tartube" SecClient
|
|||||||
"Publisher" "A S Lewis"
|
"Publisher" "A S Lewis"
|
||||||
WriteRegStr HKLM \
|
WriteRegStr HKLM \
|
||||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \
|
"Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \
|
||||||
"DisplayVersion" "1.3.0"
|
"DisplayVersion" "1.3.007"
|
||||||
|
|
||||||
# Create uninstaller
|
# Create uninstaller
|
||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
|
2
setup.py
@ -62,7 +62,7 @@ if env_var_value is not None:
|
|||||||
# Setup
|
# Setup
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name='tartube',
|
name='tartube',
|
||||||
version='1.3.0',
|
version='1.3.007',
|
||||||
description='GUI front-end for youtube-dl',
|
description='GUI front-end for youtube-dl',
|
||||||
# long_description=long_description,
|
# long_description=long_description,
|
||||||
long_description="""Tartube is a GUI front-end for youtube-dl, partly based
|
long_description="""Tartube is a GUI front-end for youtube-dl, partly based
|
||||||
|
@ -4096,6 +4096,7 @@ class VideoEditWin(GenericEditWin):
|
|||||||
entry6.set_text(
|
entry6.set_text(
|
||||||
os.path.abspath(
|
os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.app_obj.downloads_dir,
|
||||||
self.edit_obj.file_dir,
|
self.edit_obj.file_dir,
|
||||||
self.edit_obj.file_name + self.edit_obj.file_ext,
|
self.edit_obj.file_name + self.edit_obj.file_ext,
|
||||||
),
|
),
|
||||||
|
@ -1982,7 +1982,7 @@ class VideoDownloader(object):
|
|||||||
# If the 'Add videos' button was used, the path/filename/extension
|
# If the 'Add videos' button was used, the path/filename/extension
|
||||||
# won't be set yet
|
# won't be set yet
|
||||||
if not video_obj.file_dir and full_path:
|
if not video_obj.file_dir and full_path:
|
||||||
video_obj.set_file(path, filename, extension)
|
video_obj.set_file(filename, extension)
|
||||||
|
|
||||||
# Update any video object IVs that are not set
|
# Update any video object IVs that are not set
|
||||||
if video_obj.name == app_obj.default_video_name \
|
if video_obj.name == app_obj.default_video_name \
|
||||||
@ -2084,6 +2084,7 @@ class VideoDownloader(object):
|
|||||||
# thumbnail locally
|
# thumbnail locally
|
||||||
thumb_path = os.path.abspath(
|
thumb_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
app_obj.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + remote_ext,
|
video_obj.file_name + remote_ext,
|
||||||
),
|
),
|
||||||
|
@ -25,13 +25,22 @@
|
|||||||
|
|
||||||
|
|
||||||
# Import other modules
|
# Import other modules
|
||||||
# ...
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
# Import our modules
|
# Import our modules
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
|
|
||||||
|
# Some icons are different at Christmas
|
||||||
|
today = datetime.date.today()
|
||||||
|
day = today.strftime("%d")
|
||||||
|
month = today.strftime("%m")
|
||||||
|
if (int(month) == 12 and int(day) >= 24) or (int(month) == 1 and int(day) <= 5):
|
||||||
|
xmas_flag = True
|
||||||
|
else:
|
||||||
|
xmas_flag = False
|
||||||
|
|
||||||
# Used by utils.format_bytes()
|
# Used by utils.format_bytes()
|
||||||
KILO_SIZE = 1024.0
|
KILO_SIZE = 1024.0
|
||||||
FILESIZE_METRIC_LIST = [
|
FILESIZE_METRIC_LIST = [
|
||||||
@ -417,9 +426,14 @@ LANGUAGE_CODE_LIST = [
|
|||||||
['Zulu', 'zu'],
|
['Zulu', 'zu'],
|
||||||
]
|
]
|
||||||
|
|
||||||
DIALOGUE_ICON_DICT = {
|
if not xmas_flag:
|
||||||
'system_icon': 'system_icon_64.png',
|
DIALOGUE_ICON_DICT = {
|
||||||
}
|
'system_icon': 'system_icon_64.png',
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
DIALOGUE_ICON_DICT = {
|
||||||
|
'system_icon': 'system_icon_xmas_64.png',
|
||||||
|
}
|
||||||
|
|
||||||
TOOLBAR_ICON_DICT = {
|
TOOLBAR_ICON_DICT = {
|
||||||
'tool_channel_large': 'channel_large.png',
|
'tool_channel_large': 'channel_large.png',
|
||||||
@ -514,13 +528,25 @@ SMALL_ICON_DICT = {
|
|||||||
'warning_small': 'warning.png',
|
'warning_small': 'warning.png',
|
||||||
}
|
}
|
||||||
|
|
||||||
WIN_ICON_LIST = [
|
if not xmas_flag:
|
||||||
'system_icon_16.png',
|
WIN_ICON_LIST = [
|
||||||
'system_icon_24.png',
|
'system_icon_16.png',
|
||||||
'system_icon_32.png',
|
'system_icon_24.png',
|
||||||
'system_icon_48.png',
|
'system_icon_32.png',
|
||||||
'system_icon_64.png',
|
'system_icon_48.png',
|
||||||
'system_icon_128.png',
|
'system_icon_64.png',
|
||||||
'system_icon_256.png',
|
'system_icon_128.png',
|
||||||
'system_icon_512.png',
|
'system_icon_256.png',
|
||||||
]
|
'system_icon_512.png',
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
WIN_ICON_LIST = [
|
||||||
|
'system_icon_xmas_16.png',
|
||||||
|
'system_icon_xmas_24.png',
|
||||||
|
'system_icon_xmas_32.png',
|
||||||
|
'system_icon_xmas_48.png',
|
||||||
|
'system_icon_xmas_64.png',
|
||||||
|
'system_icon_xmas_128.png',
|
||||||
|
'system_icon_xmas_256.png',
|
||||||
|
'system_icon_xmas_512.png',
|
||||||
|
]
|
||||||
|
@ -1936,7 +1936,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
= json_dict['auto_delete_watched_flag']
|
= json_dict['auto_delete_watched_flag']
|
||||||
self.auto_delete_days = json_dict['auto_delete_days']
|
self.auto_delete_days = json_dict['auto_delete_days']
|
||||||
|
|
||||||
if version >= 1001041: # v1.1.041
|
if version >= 1002041: # v1.2.041
|
||||||
self.delete_on_shutdown_flag = json_dict['delete_on_shutdown_flag']
|
self.delete_on_shutdown_flag = json_dict['delete_on_shutdown_flag']
|
||||||
|
|
||||||
self.complex_index_flag = json_dict['complex_index_flag']
|
self.complex_index_flag = json_dict['complex_index_flag']
|
||||||
@ -2339,7 +2339,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
# To be safe, update every video in the registry
|
# To be safe, update every video in the registry
|
||||||
for media_data_obj in self.media_reg_dict.values():
|
for media_data_obj in self.media_reg_dict.values():
|
||||||
if isinstance(media_data_obj, media.Video):
|
if isinstance(media_data_obj, media.Video):
|
||||||
media_data_obj.reset_file_dir(self)
|
media_data_obj.reset_file_dir()
|
||||||
|
|
||||||
if version < 4015: # v0.4.015
|
if version < 4015: # v0.4.015
|
||||||
|
|
||||||
@ -2434,6 +2434,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
# to set the nickname
|
# to set the nickname
|
||||||
json_path = os.path.abspath(
|
json_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
media_data_obj.file_dir,
|
media_data_obj.file_dir,
|
||||||
media_data_obj.file_name + '.info.json',
|
media_data_obj.file_name + '.info.json',
|
||||||
),
|
),
|
||||||
@ -2550,15 +2551,14 @@ class TartubeApp(Gtk.Application):
|
|||||||
= options_obj.options_dict['to_audio']
|
= options_obj.options_dict['to_audio']
|
||||||
options_obj.options_dict.pop('to_audio')
|
options_obj.options_dict.pop('to_audio')
|
||||||
|
|
||||||
if version < 1002005: # v1.2.005
|
if version < 1003004: # v1.3.004
|
||||||
|
|
||||||
# After moving videos from one filesystem location to another,
|
# The way that directories are stored in media.VideoObj.file_dir
|
||||||
# some media.Video had the wrong value for their .file_dir IV
|
# has changed. Reset those values for all video objects
|
||||||
# To be safe, reset them all
|
|
||||||
for media_data_obj in self.media_reg_dict.values():
|
for media_data_obj in self.media_reg_dict.values():
|
||||||
if isinstance(media_data_obj, media.Video):
|
if isinstance(media_data_obj, media.Video):
|
||||||
|
|
||||||
media_data_obj.reset_file_dir(self)
|
media_data_obj.reset_file_dir()
|
||||||
|
|
||||||
|
|
||||||
def save_db(self):
|
def save_db(self):
|
||||||
@ -3466,10 +3466,16 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
# Get the time taken by the download operation, so we can convert it
|
# Get the time taken by the download operation, so we can convert it
|
||||||
# into a nice string below (e.g. '05:15')
|
# into a nice string below (e.g. '05:15')
|
||||||
time_num = int(
|
# For refresh operations, RefreshManager.stop_time() might not have
|
||||||
self.download_manager_obj.stop_time \
|
# been set at this point (for some reason), so we need to check for
|
||||||
- self.download_manager_obj.start_time
|
# the equivalent problem
|
||||||
)
|
if self.download_manager_obj.stop_time is not None:
|
||||||
|
time_num = int(
|
||||||
|
self.download_manager_obj.stop_time \
|
||||||
|
- self.download_manager_obj.start_time
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
time_num = int(time.time() - self.download_manager_obj.start_time)
|
||||||
|
|
||||||
# Any code can check whether a download/update/refresh operation is in
|
# Any code can check whether a download/update/refresh operation is in
|
||||||
# progress, or not, by checking this IV
|
# progress, or not, by checking this IV
|
||||||
@ -3895,10 +3901,15 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
# Get the time taken by the download operation, so we can convert it
|
# Get the time taken by the download operation, so we can convert it
|
||||||
# into a nice string below (e.g. '05:15')
|
# into a nice string below (e.g. '05:15')
|
||||||
time_num = int(
|
# For some reason, RefreshManager.stop_time() might not be set, so we
|
||||||
self.refresh_manager_obj.stop_time \
|
# need to check for that
|
||||||
- self.refresh_manager_obj.start_time
|
if self.refresh_manager_obj.stop_time is not None:
|
||||||
)
|
time_num = int(
|
||||||
|
self.refresh_manager_obj.stop_time \
|
||||||
|
- self.refresh_manager_obj.start_time
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
time_num = int(time.time() - self.refresh_manager_obj.start_time)
|
||||||
|
|
||||||
# Any code can check whether a download/update/refresh operation is in
|
# Any code can check whether a download/update/refresh operation is in
|
||||||
# progress, or not, by checking this IV
|
# progress, or not, by checking this IV
|
||||||
@ -3998,7 +4009,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
# If the video was added manually (for example, using the 'Add
|
# If the video was added manually (for example, using the 'Add
|
||||||
# videos' button), then its filepath won't be set yet
|
# videos' button), then its filepath won't be set yet
|
||||||
if not video_obj.file_dir:
|
if not video_obj.file_dir:
|
||||||
video_obj.set_file(dir_path, filename, extension)
|
video_obj.set_file(filename, extension)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
@ -4008,9 +4019,20 @@ class TartubeApp(Gtk.Application):
|
|||||||
video_obj = None
|
video_obj = None
|
||||||
for child_obj in media_data_obj.child_list:
|
for child_obj in media_data_obj.child_list:
|
||||||
|
|
||||||
|
child_file_dir = None
|
||||||
|
if child_obj.file_dir is not None:
|
||||||
|
child_file_dir = os.path.abspath(
|
||||||
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
|
child_obj.file_dir,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(child_obj, media.Video) \
|
if isinstance(child_obj, media.Video) \
|
||||||
and child_obj.file_dir and child_obj.file_dir == dir_path \
|
and child_file_dir \
|
||||||
and child_obj.file_name and child_obj.file_name == filename:
|
and child_file_dir == dir_path \
|
||||||
|
and child_obj.file_name \
|
||||||
|
and child_obj.file_name == filename:
|
||||||
video_obj = child_obj
|
video_obj = child_obj
|
||||||
|
|
||||||
if video_obj is None:
|
if video_obj is None:
|
||||||
@ -4040,7 +4062,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
# Since we have them to hand, set the video's file path IVs
|
# Since we have them to hand, set the video's file path IVs
|
||||||
# immediately
|
# immediately
|
||||||
video_obj.set_file(dir_path, filename, extension)
|
video_obj.set_file(filename, extension)
|
||||||
|
|
||||||
# If the video is in a channel or a playlist, assume that youtube-dl is
|
# If the video is in a channel or a playlist, assume that youtube-dl is
|
||||||
# supplying a list of videos in the order of upload, newest first -
|
# supplying a list of videos in the order of upload, newest first -
|
||||||
@ -4169,6 +4191,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
old_path = os.path.abspath(
|
old_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.description',
|
video_obj.file_name + '.description',
|
||||||
),
|
),
|
||||||
@ -4185,6 +4208,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
old_path = os.path.abspath(
|
old_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.info.json',
|
video_obj.file_name + '.info.json',
|
||||||
),
|
),
|
||||||
@ -4202,6 +4226,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
old_path = os.path.abspath(
|
old_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.annotations.xml',
|
video_obj.file_name + '.annotations.xml',
|
||||||
),
|
),
|
||||||
@ -4256,6 +4281,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
video_path = os.path.abspath(
|
video_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + video_obj.file_ext,
|
video_obj.file_name + video_obj.file_ext,
|
||||||
)
|
)
|
||||||
@ -4303,6 +4329,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
json_path = os.path.abspath(
|
json_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.info.json',
|
video_obj.file_name + '.info.json',
|
||||||
),
|
),
|
||||||
@ -4382,13 +4409,13 @@ class TartubeApp(Gtk.Application):
|
|||||||
this_thread.daemon = True
|
this_thread.daemon = True
|
||||||
this_thread.start()
|
this_thread.start()
|
||||||
this_thread.join(10) # Timeout after 10 seconds
|
this_thread.join(10) # Timeout after 10 seconds
|
||||||
if this_thread.is_alive():
|
# if this_thread.is_alive():
|
||||||
self.system_error(
|
# self.system_error(
|
||||||
132,
|
# 132,
|
||||||
'\'' + video_obj.parent_obj.name + '\': moviepy module' \
|
# '\'' + video_obj.parent_obj.name + '\': moviepy module' \
|
||||||
+ 'failed to fetch duration of video \'' \
|
# + 'failed to fetch duration of video \'' \
|
||||||
+ video_obj.name + '\'',
|
# + video_obj.name + '\'',
|
||||||
)
|
# )
|
||||||
|
|
||||||
# (Can't set the video source directly)
|
# (Can't set the video source directly)
|
||||||
|
|
||||||
@ -4420,8 +4447,16 @@ class TartubeApp(Gtk.Application):
|
|||||||
if DEBUG_FUNC_FLAG:
|
if DEBUG_FUNC_FLAG:
|
||||||
print('ap 3751 set_duration_from_moviepy')
|
print('ap 3751 set_duration_from_moviepy')
|
||||||
|
|
||||||
clip = moviepy.editor.VideoFileClip(video_path)
|
try:
|
||||||
video_obj.set_duration(clip.duration)
|
clip = moviepy.editor.VideoFileClip(video_path)
|
||||||
|
video_obj.set_duration(clip.duration)
|
||||||
|
except:
|
||||||
|
self.system_error(
|
||||||
|
132,
|
||||||
|
'\'' + video_obj.parent_obj.name + '\': moviepy module' \
|
||||||
|
+ 'failed to fetch duration of video \'' \
|
||||||
|
+ video_obj.name + '\'',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# (Add media data objects)
|
# (Add media data objects)
|
||||||
@ -4853,13 +4888,8 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
# All videos which are descendents of media_data_obj must have their
|
# All videos which are descendents of media_data_obj must have their
|
||||||
# .file_dir IV updated to the new location
|
# .file_dir IV updated to the new location
|
||||||
# for video_obj in media_data_obj.compile_all_videos( [] ):
|
for video_obj in media_data_obj.compile_all_videos( [] ):
|
||||||
# video_obj.reset_file_dir(self)
|
video_obj.reset_file_dir()
|
||||||
# v1.2.006 This has been observed to fail. Don't know why, yet, so
|
|
||||||
# reset the .file_dir IV for all videos, just to be safe
|
|
||||||
for other_obj in self.media_reg_dict.values():
|
|
||||||
if isinstance(other_obj, media.Video):
|
|
||||||
other_obj.reset_file_dir(self)
|
|
||||||
|
|
||||||
# Save the database (because, if the user terminates Tartube and then
|
# Save the database (because, if the user terminates Tartube and then
|
||||||
# restarts it, then tries to perform a download operation, a load of
|
# restarts it, then tries to perform a download operation, a load of
|
||||||
@ -5046,13 +5076,8 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
# All videos which are descendents of dest_obj must have their
|
# All videos which are descendents of dest_obj must have their
|
||||||
# .file_dir IV updated to the new location
|
# .file_dir IV updated to the new location
|
||||||
# for video_obj in source_obj.compile_all_videos( [] ):
|
for video_obj in source_obj.compile_all_videos( [] ):
|
||||||
# video_obj.reset_file_dir(self)
|
video_obj.reset_file_dir()
|
||||||
# v1.2.006 This has been observed to fail. Don't know why, yet, so
|
|
||||||
# reset the .file_dir IV for all videos, just to be safe
|
|
||||||
for other_obj in self.media_reg_dict.values():
|
|
||||||
if isinstance(other_obj, media.Video):
|
|
||||||
other_obj.reset_file_dir(self)
|
|
||||||
|
|
||||||
# Save the database (because, if the user terminates Tartube and then
|
# Save the database (because, if the user terminates Tartube and then
|
||||||
# restarts it, then tries to perform a download operation, a load of
|
# restarts it, then tries to perform a download operation, a load of
|
||||||
@ -5135,6 +5160,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
video_path = os.path.abspath(
|
video_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + video_obj.file_ext,
|
video_obj.file_name + video_obj.file_ext,
|
||||||
),
|
),
|
||||||
@ -5151,6 +5177,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
descrip_path = os.path.abspath(
|
descrip_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.description',
|
video_obj.file_name + '.description',
|
||||||
),
|
),
|
||||||
@ -5161,6 +5188,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
json_path = os.path.abspath(
|
json_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.info.json',
|
video_obj.file_name + '.info.json',
|
||||||
),
|
),
|
||||||
@ -6047,7 +6075,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
# All videos which are descendents of media_data_obj must have
|
# All videos which are descendents of media_data_obj must have
|
||||||
# their .file_dir IV updated to the new location
|
# their .file_dir IV updated to the new location
|
||||||
for video_obj in media_data_obj.compile_all_videos( [] ):
|
for video_obj in media_data_obj.compile_all_videos( [] ):
|
||||||
video_obj.reset_file_dir(self)
|
video_obj.reset_file_dir()
|
||||||
|
|
||||||
# Reset the Video Index and the Video Catalogue (this prevents a
|
# Reset the Video Index and the Video Catalogue (this prevents a
|
||||||
# lot of problems)
|
# lot of problems)
|
||||||
@ -6943,6 +6971,7 @@ class TartubeApp(Gtk.Application):
|
|||||||
|
|
||||||
path = os.path.abspath(
|
path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + video_obj.file_ext,
|
video_obj.file_name + video_obj.file_ext,
|
||||||
),
|
),
|
||||||
|
@ -5357,6 +5357,7 @@ class MainWin(Gtk.ApplicationWindow):
|
|||||||
# Get the video's full file path now, as we use it several times
|
# Get the video's full file path now, as we use it several times
|
||||||
video_path = os.path.abspath(
|
video_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.app_obj.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + video_obj.file_ext,
|
video_obj.file_name + video_obj.file_ext,
|
||||||
),
|
),
|
||||||
@ -5371,6 +5372,7 @@ class MainWin(Gtk.ApplicationWindow):
|
|||||||
mkv_flag = True
|
mkv_flag = True
|
||||||
video_path = os.path.abspath(
|
video_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.app_obj.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + '.mkv',
|
video_obj.file_name + '.mkv',
|
||||||
),
|
),
|
||||||
@ -7527,6 +7529,7 @@ class MainWin(Gtk.ApplicationWindow):
|
|||||||
|
|
||||||
path = os.path.abspath(
|
path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
self.app_obj.downloads_dir,
|
||||||
media_data_obj.file_dir,
|
media_data_obj.file_dir,
|
||||||
media_data_obj.file_name + media_data_obj.file_ext,
|
media_data_obj.file_name + media_data_obj.file_ext,
|
||||||
),
|
),
|
||||||
@ -8610,6 +8613,7 @@ class SimpleCatalogueItem(object):
|
|||||||
if self.main_win_obj.app_obj.show_tooltips_flag:
|
if self.main_win_obj.app_obj.show_tooltips_flag:
|
||||||
self.hbox.set_tooltip_text(
|
self.hbox.set_tooltip_text(
|
||||||
self.video_obj.fetch_tooltip_text(
|
self.video_obj.fetch_tooltip_text(
|
||||||
|
self.main_win_obj.app_obj,
|
||||||
self.main_win_obj.tooltip_max_len,
|
self.main_win_obj.tooltip_max_len,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -9017,6 +9021,7 @@ class ComplexCatalogueItem(object):
|
|||||||
if self.main_win_obj.app_obj.show_tooltips_flag:
|
if self.main_win_obj.app_obj.show_tooltips_flag:
|
||||||
self.frame.set_tooltip_text(
|
self.frame.set_tooltip_text(
|
||||||
self.video_obj.fetch_tooltip_text(
|
self.video_obj.fetch_tooltip_text(
|
||||||
|
self.main_win_obj.app_obj,
|
||||||
self.main_win_obj.tooltip_max_len,
|
self.main_win_obj.tooltip_max_len,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -716,10 +716,10 @@ class GenericContainer(GenericMedia):
|
|||||||
|
|
||||||
def get_dir(self, app_obj, new_name=None):
|
def get_dir(self, app_obj, new_name=None):
|
||||||
|
|
||||||
"""Called by self.__init___(), or any other code.
|
"""Can be called by anything.
|
||||||
|
|
||||||
Fetches the full path to the sub-directory used by this channel,
|
Fetches the full path to the sub-directory currently used by this
|
||||||
playlist or folder.
|
channel, playlist or folder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
||||||
@ -752,6 +752,41 @@ class GenericContainer(GenericMedia):
|
|||||||
return os.path.abspath(os.path.join(app_obj.downloads_dir, *dir_list))
|
return os.path.abspath(os.path.join(app_obj.downloads_dir, *dir_list))
|
||||||
|
|
||||||
|
|
||||||
|
def get_relative_dir(self, new_name=None):
|
||||||
|
|
||||||
|
"""Can be called by anything, for example by media.VideoObj.set_file().
|
||||||
|
|
||||||
|
Fetches the path to the sub-directory used by this channel, playlist or
|
||||||
|
folder, relative to mainapp.TartubeApp.downloads_dir.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
new_name (str): If specified, fetches the relative path to the
|
||||||
|
sub-directory that would be used by this channel, playlist or
|
||||||
|
folder, if it were renamed to 'new_name'. If not specified, the
|
||||||
|
channel/playlist/folder's actual name is used
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
The path to the directory relative to
|
||||||
|
mainapp.TartubeApp.downloads_dir
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if new_name is not None:
|
||||||
|
dir_list = [new_name]
|
||||||
|
else:
|
||||||
|
dir_list = [self.name]
|
||||||
|
|
||||||
|
obj = self
|
||||||
|
while obj.parent_obj:
|
||||||
|
|
||||||
|
obj = obj.parent_obj
|
||||||
|
dir_list.insert(0, obj.name)
|
||||||
|
|
||||||
|
return os.path.join(*dir_list)
|
||||||
|
|
||||||
|
|
||||||
class GenericRemoteContainer(GenericContainer):
|
class GenericRemoteContainer(GenericContainer):
|
||||||
|
|
||||||
"""Base python class inherited by media.Channel and media.Playlist."""
|
"""Base python class inherited by media.Channel and media.Playlist."""
|
||||||
@ -1015,7 +1050,8 @@ class Video(GenericMedia):
|
|||||||
# auto-deleted (but it can still be deleted manually by the user)
|
# auto-deleted (but it can still be deleted manually by the user)
|
||||||
self.archive_flag = False
|
self.archive_flag = False
|
||||||
|
|
||||||
# The file's directory, name and extension
|
# The file's directory (relative to mainapp.TartubeApp.downloads_dir),
|
||||||
|
# name and extension
|
||||||
self.file_dir = None
|
self.file_dir = None
|
||||||
self.file_name = None
|
self.file_name = None
|
||||||
self.file_ext = None
|
self.file_ext = None
|
||||||
@ -1099,13 +1135,15 @@ class Video(GenericMedia):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def fetch_tooltip_text(self, max_length=None):
|
def fetch_tooltip_text(self, app_obj, max_length=None):
|
||||||
|
|
||||||
"""Called by mainwin.SimpleCatalogueItem.update_tooltips() and
|
"""Called by mainwin.SimpleCatalogueItem.update_tooltips() and
|
||||||
mainwin.ComplexCatalogueItem.update_tooltips().
|
mainwin.ComplexCatalogueItem.update_tooltips().
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
||||||
|
app_obj (mainapp.TartubeApp): The main application
|
||||||
|
|
||||||
max_length (int or None): If specified, the maximum line length, in
|
max_length (int or None): If specified, the maximum line length, in
|
||||||
characters
|
characters
|
||||||
|
|
||||||
@ -1130,6 +1168,7 @@ class Video(GenericMedia):
|
|||||||
else:
|
else:
|
||||||
text += os.path.abspath(
|
text += os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
app_obj.downloads_dir,
|
||||||
self.file_dir,
|
self.file_dir,
|
||||||
self.file_name + self.file_ext,
|
self.file_name + self.file_ext,
|
||||||
),
|
),
|
||||||
@ -1140,7 +1179,6 @@ class Video(GenericMedia):
|
|||||||
|
|
||||||
# Apply a maximum line length, if required
|
# Apply a maximum line length, if required
|
||||||
if max_length is not None:
|
if max_length is not None:
|
||||||
# text = utils.tidy_up_long_string(text, max_length, False)
|
|
||||||
text = utils.tidy_up_long_descrip(text, max_length)
|
text = utils.tidy_up_long_descrip(text, max_length)
|
||||||
|
|
||||||
return text
|
return text
|
||||||
@ -1163,6 +1201,7 @@ class Video(GenericMedia):
|
|||||||
|
|
||||||
descrip_path = os.path.abspath(
|
descrip_path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
app_obj.downloads_dir,
|
||||||
self.file_dir,
|
self.file_dir,
|
||||||
self.file_name + '.description',
|
self.file_name + '.description',
|
||||||
),
|
),
|
||||||
@ -1206,14 +1245,14 @@ class Video(GenericMedia):
|
|||||||
self.duration = None
|
self.duration = None
|
||||||
|
|
||||||
|
|
||||||
def set_file(self, path, filename, extension):
|
def set_file(self, filename, extension):
|
||||||
|
|
||||||
self.file_dir = path
|
self.file_dir = self.parent_obj.get_relative_dir()
|
||||||
self.file_name = filename
|
self.file_name = filename
|
||||||
self.file_ext = extension
|
self.file_ext = extension
|
||||||
|
|
||||||
|
|
||||||
def reset_file_dir(self, app_obj):
|
def reset_file_dir(self):
|
||||||
|
|
||||||
"""Called by mainapp.TartubeApp.move_container_to_top_continue()
|
"""Called by mainapp.TartubeApp.move_container_to_top_continue()
|
||||||
and .move_container_continue().
|
and .move_container_continue().
|
||||||
@ -1223,7 +1262,7 @@ class Video(GenericMedia):
|
|||||||
moved along with it must have its .file_dir IV updated.
|
moved along with it must have its .file_dir IV updated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.file_dir = self.parent_obj.get_dir(app_obj)
|
self.file_dir = self.parent_obj.get_relative_dir()
|
||||||
|
|
||||||
|
|
||||||
def set_file_size(self, size=None):
|
def set_file_size(self, size=None):
|
||||||
|
@ -335,7 +335,7 @@ class RefreshManager(threading.Thread):
|
|||||||
= child_obj.file_name + child_obj.file_ext
|
= child_obj.file_name + child_obj.file_ext
|
||||||
|
|
||||||
if not child_relative_path in alt_list:
|
if not child_relative_path in alt_list:
|
||||||
child_obj.set_file(dir_path, filename, ext)
|
child_obj.set_file(filename, ext)
|
||||||
|
|
||||||
# Eliminate this media.Video object; no other video file should
|
# Eliminate this media.Video object; no other video file should
|
||||||
# match it
|
# match it
|
||||||
@ -378,7 +378,8 @@ class RefreshManager(threading.Thread):
|
|||||||
# Set the new video object's IVs
|
# Set the new video object's IVs
|
||||||
filename, ext = os.path.splitext(filter_dict[filename])
|
filename, ext = os.path.splitext(filter_dict[filename])
|
||||||
video_obj.set_name(filename)
|
video_obj.set_name(filename)
|
||||||
video_obj.set_file(dir_path, filename, ext)
|
video_obj.set_nickname(filename)
|
||||||
|
video_obj.set_file(filename, ext)
|
||||||
|
|
||||||
if ext == '.mkv':
|
if ext == '.mkv':
|
||||||
video_obj.set_mkv()
|
video_obj.set_mkv()
|
||||||
|
@ -35,7 +35,7 @@ import mainapp
|
|||||||
|
|
||||||
# 'Global' variables
|
# 'Global' variables
|
||||||
__packagename__ = 'tartube'
|
__packagename__ = 'tartube'
|
||||||
__version__ = '1.3.0'
|
__version__ = '1.3.007'
|
||||||
__date__ = '20 Dec 2019'
|
__date__ = '20 Dec 2019'
|
||||||
__copyright__ = 'Copyright \xa9 2019 A S Lewis'
|
__copyright__ = 'Copyright \xa9 2019 A S Lewis'
|
||||||
__license__ = """
|
__license__ = """
|
||||||
|
@ -35,7 +35,7 @@ import mainapp
|
|||||||
|
|
||||||
# 'Global' variables
|
# 'Global' variables
|
||||||
__packagename__ = 'tartube'
|
__packagename__ = 'tartube'
|
||||||
__version__ = '1.3.0'
|
__version__ = '1.3.007'
|
||||||
__date__ = '20 Dec 2019'
|
__date__ = '20 Dec 2019'
|
||||||
__copyright__ = 'Copyright \xa9 2019 A S Lewis'
|
__copyright__ = 'Copyright \xa9 2019 A S Lewis'
|
||||||
__license__ = """
|
__license__ = """
|
||||||
|
@ -401,6 +401,7 @@ def find_thumbnail(app_obj, video_obj, temp_dir_flag=False):
|
|||||||
# Look in main data directory
|
# Look in main data directory
|
||||||
path = os.path.abspath(
|
path = os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
app_obj.downloads_dir,
|
||||||
video_obj.file_dir,
|
video_obj.file_dir,
|
||||||
video_obj.file_name + ext,
|
video_obj.file_name + ext,
|
||||||
),
|
),
|
||||||
|