Add files via upload
This commit is contained in:
parent
424ccecc6b
commit
8cca9e28d5
13
README.rst
13
README.rst
@ -10,6 +10,12 @@ Tartube is **alpha software**. It crashes a lot. If you find this
|
||||
frustrating, find a solution and then `send it to
|
||||
me <https://github.com/axcore/tartube/issues>`__.
|
||||
|
||||
Screenshots
|
||||
-----------
|
||||
|
||||
.. image:: screenshots/tartube.png
|
||||
:alt: Tartube screenshot
|
||||
|
||||
Why should I use Tartube?
|
||||
-------------------------
|
||||
|
||||
@ -20,8 +26,7 @@ Why should I use Tartube?
|
||||
- Tartube will organise your videos into convenient folders
|
||||
- Certain popular video websites manipulate search results, repeatedly
|
||||
unsubscribe people from their favourite channels and/or deliberately
|
||||
conceal videos which challenge the Californian political consensus.
|
||||
Tartube won't do any of those things
|
||||
conceal videos which challenge their preferred political views. Tartube won't do any of those things
|
||||
- Tartube can, in some circumstances, see videos that are
|
||||
region-blocked and age-restricted
|
||||
|
||||
@ -50,7 +55,7 @@ Install from source
|
||||
|
||||
1. Download & extract the source
|
||||
2. Change directory into the Tartube directory
|
||||
3. Run ``python setup.py install``
|
||||
3. Run ``python3 setup.py install``
|
||||
|
||||
Install using PyPI
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
@ -67,7 +72,7 @@ Run without installing
|
||||
|
||||
1. Download & extract the source
|
||||
2. Change directory into the Tartube directory
|
||||
3. Run 'python tartube.py'
|
||||
3. Run 'python3 tartube.py'
|
||||
|
||||
Frequently-Asked Questions
|
||||
--------------------------
|
||||
|
428
lib/downloads.py
428
lib/downloads.py
@ -98,6 +98,8 @@ class DownloadManager(threading.Thread):
|
||||
|
||||
def __init__(self, app_obj, force_sim_flag, download_list_obj):
|
||||
|
||||
print('dl 101 __init__')
|
||||
|
||||
super(DownloadManager, self).__init__()
|
||||
|
||||
# IV list - class objects
|
||||
@ -172,6 +174,8 @@ class DownloadManager(threading.Thread):
|
||||
download operation is complete.
|
||||
"""
|
||||
|
||||
print('dl 177 run')
|
||||
|
||||
# Perform the download operation until there is nothing left to
|
||||
# download, or until something has called
|
||||
# self.stop_download_operation()
|
||||
@ -267,6 +271,8 @@ class DownloadManager(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 274 change_worker_count')
|
||||
|
||||
# How many workers do we have already?
|
||||
current = len(self.worker_list)
|
||||
# If this object hasn't set up its worker pool yet, let the setup code
|
||||
@ -322,6 +328,8 @@ class DownloadManager(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 331 check_workers_all_finished')
|
||||
|
||||
for worker_obj in self.worker_list:
|
||||
if not worker_obj.available_flag:
|
||||
return False
|
||||
@ -342,6 +350,8 @@ class DownloadManager(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 353 get_available_worker')
|
||||
|
||||
for worker_obj in self.worker_list:
|
||||
if worker_obj.available_flag:
|
||||
return worker_obj
|
||||
@ -362,6 +372,8 @@ class DownloadManager(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 375 remove_worker')
|
||||
|
||||
new_list = []
|
||||
|
||||
for other_obj in self.worker_list:
|
||||
@ -382,6 +394,8 @@ class DownloadManager(threading.Thread):
|
||||
loop, the downloads.DownloadWorker objects are cleaned up.
|
||||
"""
|
||||
|
||||
print('dl 397 stop_download_operation')
|
||||
|
||||
self.running_flag = False
|
||||
|
||||
|
||||
@ -411,6 +425,8 @@ class DownloadWorker(threading.Thread):
|
||||
|
||||
def __init__(self, download_manager_obj):
|
||||
|
||||
print('dl 428 __init__')
|
||||
|
||||
super(DownloadWorker, self).__init__()
|
||||
|
||||
# IV list - class objects
|
||||
@ -465,6 +481,8 @@ class DownloadWorker(threading.Thread):
|
||||
create a new downloads.VideoDownloader object and wait for the result.
|
||||
"""
|
||||
|
||||
print('dl 484 run')
|
||||
|
||||
# Import the main application (for convenience)
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
|
||||
@ -534,6 +552,8 @@ class DownloadWorker(threading.Thread):
|
||||
Tidy up IVs and stop any child processes.
|
||||
"""
|
||||
|
||||
print('dl 555 close')
|
||||
|
||||
self.running_flag = False
|
||||
if self.video_downloader_obj:
|
||||
self.video_downloader_obj.stop()
|
||||
@ -555,6 +575,8 @@ class DownloadWorker(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 578 prepare_download')
|
||||
|
||||
self.download_item_obj = download_item_obj
|
||||
self.options_manager_obj = download_item_obj.options_manager_obj
|
||||
self.options_list = self.download_manager_obj.options_parser_obj.parse(
|
||||
@ -569,6 +591,8 @@ class DownloadWorker(threading.Thread):
|
||||
|
||||
"""Called by downloads.DownloadManager.change_worker_count()."""
|
||||
|
||||
print('dl 594 set_doomed_flag')
|
||||
|
||||
self.doomed_flag = flag
|
||||
|
||||
|
||||
@ -595,6 +619,8 @@ class DownloadWorker(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 622 data_callback')
|
||||
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
app_obj.main_win_obj.progress_list_receive_dl_stats(
|
||||
self.download_item_obj,
|
||||
@ -635,6 +661,8 @@ class DownloadList(object):
|
||||
|
||||
def __init__(self, app_obj, media_data_obj):
|
||||
|
||||
print('dl 664 __init__')
|
||||
|
||||
# IV list - class objects
|
||||
# -----------------------
|
||||
self.app_obj = app_obj
|
||||
@ -711,6 +739,8 @@ class DownloadList(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 742 change_item_stage')
|
||||
|
||||
self.download_item_dict[dbid].stage = new_stage
|
||||
|
||||
|
||||
@ -743,6 +773,8 @@ class DownloadList(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 776 create_item')
|
||||
|
||||
# Get the options.OptionsManager object that applies to this media
|
||||
# data object
|
||||
# (The manager might be specified by obj itself, or it might be
|
||||
@ -808,6 +840,8 @@ class DownloadList(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 843 fetch_next_item')
|
||||
|
||||
for dbid in self.download_item_list:
|
||||
this_item = self.download_item_dict[dbid]
|
||||
|
||||
@ -841,6 +875,8 @@ class DownloadList(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 878 get_options_manager')
|
||||
|
||||
if media_data_obj.options_obj:
|
||||
return media_data_obj.options_obj
|
||||
elif media_data_obj.parent_obj:
|
||||
@ -877,6 +913,8 @@ class DownloadItem(object):
|
||||
|
||||
def __init__(self, dbid, media_data_obj, options_manager_obj):
|
||||
|
||||
print('dl 916 __init__')
|
||||
|
||||
# IV list - class objects
|
||||
# -----------------------
|
||||
# The media data object to be downloaded
|
||||
@ -965,6 +1003,8 @@ class VideoDownloader(object):
|
||||
def __init__(self, download_manager_obj, download_worker_obj, \
|
||||
download_item_obj):
|
||||
|
||||
print('dl 1006 __init__')
|
||||
|
||||
# IV list - class objects
|
||||
# -----------------------
|
||||
# The downloads.DownloadManager object handling the entire download
|
||||
@ -1059,126 +1099,6 @@ class VideoDownloader(object):
|
||||
# Public class methods
|
||||
|
||||
|
||||
def OLDdo_download(self):
|
||||
|
||||
"""Called by downloads.DownloadWorker.run().
|
||||
|
||||
Based on YoutubeDLDownloader.download().
|
||||
|
||||
Downloads video(s) from a URL described by self.download_item_obj.
|
||||
|
||||
Returns:
|
||||
|
||||
The final return code, a value in the range 0-5 (as described
|
||||
above)
|
||||
|
||||
"""
|
||||
|
||||
# Import the main application (for convenience)
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
|
||||
# Set the default return code. Everything is OK unless we encounter
|
||||
# any problems
|
||||
self.return_code = self.OK
|
||||
|
||||
# Reset the errors/warnings stored in the media data object, the last
|
||||
# time it was checked/downloaded
|
||||
self.download_item_obj.media_data_obj.reset_error_warning()
|
||||
|
||||
# Prepare a system command...
|
||||
cmd_list = self.get_system_cmd()
|
||||
# ...and create a new child process using that command
|
||||
self.create_child_process(cmd_list)
|
||||
|
||||
# So that we can read from the child process STDOUT and STDERR, attach
|
||||
# a file descriptor to the PipeReader objects
|
||||
if self.child_process is not None:
|
||||
|
||||
self.stdout_reader.attach_file_descriptor(
|
||||
self.child_process.stdout,
|
||||
)
|
||||
|
||||
self.stderr_reader.attach_file_descriptor(
|
||||
self.child_process.stderr,
|
||||
)
|
||||
|
||||
# While downloading the video, update the callback function with
|
||||
# the status of the current job
|
||||
while self.is_child_process_alive():
|
||||
|
||||
# Read from the child process STDOUT, and convert into unicode for
|
||||
# Python's convenience
|
||||
while not self.stdout_queue.empty():
|
||||
|
||||
stdout = self.stdout_queue.get_nowait().rstrip()
|
||||
stdout = utils.convert_item(stdout, to_unicode=True)
|
||||
|
||||
if stdout:
|
||||
# Convert the statistics into a python dictionary in a
|
||||
# standard format, specified in the comments for
|
||||
# self.extract_stdout_data()
|
||||
dl_stat_dict = self.extract_stdout_data(stdout)
|
||||
# If the job's status is constants.COMPLETED_STAGE_ALREADY
|
||||
# or constants.ERROR_STAGE_ABORT, set our
|
||||
# self.return_code IV
|
||||
self.extract_stdout_status(dl_stat_dict)
|
||||
# Pass the dictionary on to self.download_worker_obj so the
|
||||
# main window can be updated
|
||||
self.download_worker_obj.data_callback(dl_stat_dict)
|
||||
|
||||
if (app_obj.ytdl_write_stdout_flag):
|
||||
print(stdout)
|
||||
|
||||
# The child process has finished
|
||||
while not self.stderr_queue.empty():
|
||||
|
||||
# Read from the child process STDERR queue (we don't need to read
|
||||
# it in real time), and convert into unicode for python's
|
||||
# convenience
|
||||
stderr = self.stderr_queue.get_nowait().rstrip()
|
||||
stderr = utils.convert_item(stderr, to_unicode=True)
|
||||
|
||||
if self.is_warning(stderr):
|
||||
self.set_return_code(self.WARNING)
|
||||
self.download_item_obj.media_data_obj.set_warning(stderr)
|
||||
|
||||
else:
|
||||
self.set_return_code(self.ERROR)
|
||||
self.download_item_obj.media_data_obj.set_error(stderr)
|
||||
|
||||
if (app_obj.ytdl_write_stderr_flag):
|
||||
print(stderr)
|
||||
|
||||
# We also set the return code to self.ERROR if the download didn't
|
||||
# start or if the child process return code is greater than 0
|
||||
# Original notes from youtube-dl-gui:
|
||||
# NOTE: In Linux if the called script is just empty Python exits
|
||||
# normally (ret=0), so we cant detect this or similar cases
|
||||
# using the code below
|
||||
# NOTE: In Unix a negative return code (-N) indicates that the child
|
||||
# was terminated by signal N (e.g. -9 = SIGKILL)
|
||||
if self.child_process is None:
|
||||
self.set_return_code(self.ERROR)
|
||||
self.download_item_obj.media_data_obj.set_error(
|
||||
'Download did not start',
|
||||
)
|
||||
|
||||
elif self.child_process.returncode > 0:
|
||||
self.set_return_code(self.ERROR)
|
||||
self.download_item_obj.media_data_obj.set_error(
|
||||
'Child process exited with non-zero code: {}'.format(
|
||||
self.child_process.returncode,
|
||||
)
|
||||
)
|
||||
|
||||
# Pass a dictionary of values to downloads.DownloadWorker, confirming
|
||||
# the result of the job. The values are passed on to the main
|
||||
# window
|
||||
self.last_data_callback()
|
||||
|
||||
# Pass the result back to the parent downloads.DownloadWorker object
|
||||
return self.return_code
|
||||
|
||||
def do_download(self):
|
||||
|
||||
"""Called by downloads.DownloadWorker.run().
|
||||
@ -1194,6 +1114,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1117 do_download')
|
||||
|
||||
# Import the main application (for convenience)
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
|
||||
@ -1311,6 +1233,8 @@ class VideoDownloader(object):
|
||||
Destructor function for this object.
|
||||
"""
|
||||
|
||||
print('dl 1236 close')
|
||||
|
||||
# Tell the PipeReader objects to shut down, thus joining their threads
|
||||
self.stdout_reader.join()
|
||||
self.stderr_reader.join()
|
||||
@ -1337,6 +1261,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1264 confirm_new_video')
|
||||
|
||||
if not self.video_num in self.video_check_dict:
|
||||
self.video_check_dict[self.video_num] = filename
|
||||
|
||||
@ -1386,6 +1312,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1315 confirm_old_video')
|
||||
|
||||
# Create shortcut variables (for convenience)
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
media_data_obj = self.download_item_obj.media_data_obj
|
||||
@ -1435,206 +1363,6 @@ class VideoDownloader(object):
|
||||
)
|
||||
|
||||
|
||||
def OLDconfirm_sim_video(self, json_dict):
|
||||
|
||||
"""Called by self.extract_stdout_data().
|
||||
|
||||
After a successful simulated download, youtube-dl presents us with JSON
|
||||
data for the video. Use that data to update everything.
|
||||
|
||||
Args:
|
||||
|
||||
json_dict (dict): JSON data from STDOUT, converted into a python
|
||||
dictionary
|
||||
|
||||
"""
|
||||
|
||||
# IMport the main application (for convenience)
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
|
||||
# From the JSON dictionary, extract the data we need
|
||||
if '_filename' in json_dict:
|
||||
full_path = json_dict['_filename']
|
||||
path, filename, extension = self.extract_filename(full_path)
|
||||
else:
|
||||
return app_obj.system_error(
|
||||
302,
|
||||
'Missing filename in JSON data',
|
||||
)
|
||||
|
||||
if 'upload_date' in json_dict:
|
||||
# date_string in form YYYYMMDD
|
||||
date_string = json_dict['upload_date']
|
||||
dt_obj = datetime.datetime.strptime(date_string, '%Y%m%d')
|
||||
upload_time = dt_obj.strftime('%s')
|
||||
else:
|
||||
upload_time = None
|
||||
|
||||
if 'duration' in json_dict:
|
||||
duration = json_dict['duration']
|
||||
else:
|
||||
duration = None
|
||||
|
||||
if 'title' in json_dict:
|
||||
name = json_dict['title']
|
||||
else:
|
||||
name = None
|
||||
|
||||
if 'description' in json_dict:
|
||||
descrip = json_dict['description']
|
||||
else:
|
||||
descrip = None
|
||||
|
||||
if 'thumbnail' in json_dict:
|
||||
thumbnail = json_dict['thumbnail']
|
||||
else:
|
||||
thumbnail = None
|
||||
|
||||
if 'webpage_url' in json_dict:
|
||||
source = json_dict['webpage_url']
|
||||
else:
|
||||
source = None
|
||||
|
||||
if 'playlist_index' in json_dict:
|
||||
playlist_index = json_dict['playlist_index']
|
||||
else:
|
||||
playlist_index = None
|
||||
|
||||
# Does an existing media.Video object match this video?
|
||||
media_data_obj = self.download_item_obj.media_data_obj
|
||||
video_obj = None
|
||||
if isinstance(media_data_obj, media.Video):
|
||||
video_obj = media_data_obj
|
||||
else:
|
||||
# media_data_obj is a media.Channel or media.Playlist object. Check
|
||||
# its child objects, looking for a matching video
|
||||
# (video_obj is set to None, if no match is found)
|
||||
video_obj = media_data_obj.find_matching_video(app_obj, filename)
|
||||
|
||||
if not video_obj:
|
||||
|
||||
# No matching media.Video object found, so create a new one
|
||||
video_obj = app_obj.create_video_from_download(
|
||||
self.download_item_obj,
|
||||
path,
|
||||
filename,
|
||||
extension,
|
||||
)
|
||||
|
||||
# Update its IVs with the JSON information we extracted
|
||||
if name is not None:
|
||||
video_obj.set_name(name)
|
||||
|
||||
if upload_time is not None:
|
||||
video_obj.set_upload_time(upload_time)
|
||||
|
||||
if duration is not None:
|
||||
video_obj.set_duration(duration)
|
||||
|
||||
if source is not None:
|
||||
video_obj.set_source(source)
|
||||
|
||||
if descrip is not None:
|
||||
video_obj.set_video_descrip(
|
||||
descrip,
|
||||
app_obj.main_win_obj.long_string_max_len,
|
||||
)
|
||||
|
||||
# Only save the playlist index when this video is actually stored
|
||||
# inside a media.Playlist object
|
||||
if isinstance(video_obj.parent_obj, media.Playlist) \
|
||||
and playlist_index is not None:
|
||||
video_obj.set_index(playlist_index)
|
||||
|
||||
else:
|
||||
|
||||
# If the 'Add videos' button was used, the path/filename/extension
|
||||
# won't be set yet
|
||||
if not video_obj.file_dir and full_path:
|
||||
video_obj.set_file(path, filename, extension)
|
||||
|
||||
# Update any video object IVs that are not set
|
||||
if video_obj.name == app_obj.default_video_name \
|
||||
and name is not None:
|
||||
video_obj.set_name(name)
|
||||
|
||||
if not video_obj.upload_time and upload_time is not None:
|
||||
video_obj.set_upload_time(upload_time)
|
||||
|
||||
if not video_obj.duration and duration is not None:
|
||||
video_obj.set_duration(duration)
|
||||
|
||||
if not video_obj.source and source is not None:
|
||||
video_obj.set_source(source)
|
||||
|
||||
if not video_obj.descrip and descrip is not None:
|
||||
video_obj.set_video_descrip(
|
||||
descrip,
|
||||
app_obj.main_win_obj.long_string_max_len,
|
||||
)
|
||||
|
||||
# Only save the playlist index when this video is actually stored
|
||||
# inside a media.Playlist object
|
||||
if not video_obj.index \
|
||||
and isinstance(video_obj.parent_obj, media.Playlist) \
|
||||
and playlist_index is not None:
|
||||
video_obj.set_index(playlist_index)
|
||||
|
||||
# Deal with the video description, JSON data and thumbnail, according
|
||||
# to the settings in options.OptionsManager
|
||||
options_dict =self.download_worker_obj.options_manager_obj.options_dict
|
||||
|
||||
if descrip and options_dict['write_description']:
|
||||
descrip_path = os.path.join(path, filename + '.description')
|
||||
if not options_dict['sim_keep_description']:
|
||||
descrip_path = utils.convert_path_to_temp(
|
||||
app_obj,
|
||||
descrip_path,
|
||||
)
|
||||
|
||||
# (Don't replace a file that already exists)
|
||||
if not os.path.isfile(descrip_path):
|
||||
fh = open(descrip_path, 'w')
|
||||
fh.write(descrip.encode('utf-8'))
|
||||
fh.close()
|
||||
|
||||
if options_dict['write_info']:
|
||||
json_path = os.path.join(path, filename + '.info.json')
|
||||
if not options_dict['sim_keep_info']:
|
||||
json_path = utils.convert_path_to_temp(app_obj, json_path)
|
||||
|
||||
if not os.path.isfile(json_path):
|
||||
with open(json_path, 'w') as outfile:
|
||||
json.dump(json_dict, outfile, indent=4)
|
||||
|
||||
if thumbnail and options_dict['write_thumbnail']:
|
||||
|
||||
# Download the thumbnail, if we don't already have it
|
||||
# The thumbnail's URL is something like
|
||||
# 'https://i.ytimg.com/vi/abcdefgh/maxresdefault.jpg'
|
||||
# When saved to disc by youtube-dl, the file is given the same name
|
||||
# as the video (but with a different extension)
|
||||
# Get the thumbnail's extension...
|
||||
remote_file, remote_ext = os.path.splitext(thumbnail)
|
||||
|
||||
# ...and thus get the filename used by youtube-dl when storing the
|
||||
# thumbnail locally
|
||||
thumb_path = os.path.join(
|
||||
video_obj.file_dir,
|
||||
video_obj.file_name + remote_ext,
|
||||
)
|
||||
|
||||
if not options_dict['sim_keep_thumbnail']:
|
||||
thumb_path = utils.convert_path_to_temp(app_obj, thumb_path)
|
||||
|
||||
if not os.path.isfile(thumb_path):
|
||||
request_obj = requests.get(thumbnail)
|
||||
with open(thumb_path, 'wb') as outfile:
|
||||
outfile.write(request_obj.content)
|
||||
|
||||
# Update the main window
|
||||
app_obj.announce_video_download(self.download_item_obj, video_obj)
|
||||
|
||||
def confirm_sim_video(self, json_dict):
|
||||
|
||||
"""Called by self.extract_stdout_data().
|
||||
@ -1649,7 +1377,9 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
# IMport the main application (for convenience)
|
||||
print('dl 1380 confirm_sim_video')
|
||||
|
||||
# Import the main application (for convenience)
|
||||
app_obj = self.download_manager_obj.app_obj
|
||||
|
||||
# From the JSON dictionary, extract the data we need
|
||||
@ -1859,6 +1589,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1592 create_child_process')
|
||||
|
||||
info = preexec = None
|
||||
if os.name == 'nt':
|
||||
# Hide the child process window that MS Windows helpfully creates
|
||||
@ -1911,6 +1643,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1646 extract_filename')
|
||||
|
||||
path, fullname = os.path.split(input_data.strip("\""))
|
||||
filename, extension = os.path.splitext(fullname)
|
||||
|
||||
@ -1956,6 +1690,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1693 extract_stdout_data')
|
||||
|
||||
# Initialise the dictionary with default key-value pairs for the main
|
||||
# window to display, to be overwritten (if possible) with new key-
|
||||
# value pairs as this function interprets the STDOUT message
|
||||
@ -2162,6 +1898,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1901 extract_stdout_status')
|
||||
|
||||
if 'status' in dl_stat_dict:
|
||||
if dl_stat_dict['status'] == constants.COMPLETED_STAGE_ALREADY:
|
||||
self.set_return_code(self.ALREADY)
|
||||
@ -2187,6 +1925,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1928 get_system_cmd')
|
||||
|
||||
options_list = self.download_worker_obj.options_list
|
||||
|
||||
# Simulate the download, rather than actually downloading videos, if
|
||||
@ -2220,6 +1960,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1963 is_child_process_alive')
|
||||
|
||||
if self.child_process is None:
|
||||
return False
|
||||
|
||||
@ -2245,6 +1987,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 1990 is_warning')
|
||||
|
||||
return stderr.split(':')[0] == 'WARNING'
|
||||
|
||||
|
||||
@ -2264,6 +2008,8 @@ class VideoDownloader(object):
|
||||
The new key-value pairs are used to update the main window.
|
||||
"""
|
||||
|
||||
print('dl 2011 last_data_callback')
|
||||
|
||||
dl_stat_dict = {}
|
||||
|
||||
if self.return_code == self.OK:
|
||||
@ -2313,6 +2059,8 @@ class VideoDownloader(object):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 2062 set_return_code')
|
||||
|
||||
if code >= self.return_code:
|
||||
self.return_code = code
|
||||
|
||||
@ -2325,6 +2073,8 @@ class VideoDownloader(object):
|
||||
self.STOPPED.
|
||||
"""
|
||||
|
||||
print('dl 2076 stop')
|
||||
|
||||
if self.is_child_process_alive():
|
||||
|
||||
if os.name == 'nt':
|
||||
@ -2371,6 +2121,8 @@ class PipeReader(threading.Thread):
|
||||
|
||||
def __init__(self, queue):
|
||||
|
||||
print('dl 2124 __init__')
|
||||
|
||||
super(PipeReader, self).__init__()
|
||||
|
||||
# IV list - other
|
||||
@ -2398,34 +2150,6 @@ class PipeReader(threading.Thread):
|
||||
# Public class methods
|
||||
|
||||
|
||||
def OLDOLDrun(self):
|
||||
|
||||
"""Called as a result of self.__init__().
|
||||
|
||||
Reads from STDOUT or STERR using the attached filed descriptor.
|
||||
"""
|
||||
|
||||
# Use this flag so that the loop can ignore FFmpeg error messsages
|
||||
# (because the parent VideoDownloader object shouldn't use that as a
|
||||
# serious error)
|
||||
ignore_line = False
|
||||
|
||||
while self.running_flag:
|
||||
|
||||
if self.file_descriptor is not None:
|
||||
for line in iter(self.file_descriptor.readline, str('')):
|
||||
|
||||
if str('ffmpeg version') in line:
|
||||
ignore_line = True
|
||||
|
||||
if not ignore_line:
|
||||
self.output_queue.put_nowait(line)
|
||||
|
||||
self.file_descriptor = None
|
||||
ignore_line = False
|
||||
|
||||
time.sleep(self.sleep_time)
|
||||
|
||||
def run(self):
|
||||
|
||||
"""Called as a result of self.__init__().
|
||||
@ -2433,6 +2157,8 @@ class PipeReader(threading.Thread):
|
||||
Reads from STDOUT or STERR using the attached filed descriptor.
|
||||
"""
|
||||
|
||||
print('dl 2160 run')
|
||||
|
||||
# Use this flag so that the loop can ignore FFmpeg error messsages
|
||||
# (because the parent VideoDownloader object shouldn't use that as a
|
||||
# serious error)
|
||||
@ -2475,6 +2201,8 @@ class PipeReader(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 2204 attach_file_descriptor')
|
||||
|
||||
self.file_descriptor = filedesc
|
||||
|
||||
|
||||
@ -2491,6 +2219,8 @@ class PipeReader(threading.Thread):
|
||||
|
||||
"""
|
||||
|
||||
print('dl 2222 join')
|
||||
|
||||
self.running_flag = False
|
||||
super(PipeReader, self).join(timeout)
|
||||
|
||||
|
203
lib/mainapp.py
203
lib/mainapp.py
@ -77,6 +77,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
print('ap 80 __init__')
|
||||
|
||||
super(TartubeApp, self).__init__(
|
||||
*args,
|
||||
application_id=__main__.__app_id__,
|
||||
@ -350,7 +352,8 @@ class TartubeApp(Gtk.Application):
|
||||
self.operation_save_flag = True
|
||||
# Flag set to True if a dialogue window should be shown at the end of
|
||||
# each download/update/refresh operation
|
||||
self.operation_dialogue_flag = True
|
||||
# self.operation_dialogue_flag = True
|
||||
self.operation_dialogue_flag = False
|
||||
# Flag set to True if self.update_video_from_filesystem() should get
|
||||
# the video duration, if not already known, using the moviepy.editor
|
||||
# module (which may be slow)
|
||||
@ -366,7 +369,8 @@ class TartubeApp(Gtk.Application):
|
||||
# and then waits for the results, an increase in this number is
|
||||
# applied to a download operation immediately, but a decrease is not
|
||||
# applied until one of the download jobs has finished
|
||||
self.num_worker_default = 2
|
||||
# self.num_worker_default = 2
|
||||
self.num_worker_default = 1
|
||||
# (Absoute minimum and maximum values)
|
||||
self.num_worker_max = 10
|
||||
self.num_worker_min = 1
|
||||
@ -415,40 +419,38 @@ class TartubeApp(Gtk.Application):
|
||||
# Debugging flags (can only be set by editing the source code)
|
||||
# Delete the config file and the contents of Tartube's data directory
|
||||
# on startup
|
||||
# self.debug_delete_data_flag = False
|
||||
self.debug_delete_data_flag = True
|
||||
self.debug_delete_data_flag = False
|
||||
# In the main window's menu, show a menu item for adding a set of
|
||||
# media data objects for testing
|
||||
# self.debug_test_media_menu_flag = False
|
||||
self.debug_test_media_menu_flag = True
|
||||
self.debug_test_media_menu_flag = False
|
||||
# In the main window's toolbar, show a toolbar item for adding a set of
|
||||
# media data objects for testing
|
||||
self.debug_test_media_toolbar_flag = False
|
||||
# Show an dialogue window with 'Tartube is already running!' if the
|
||||
# user tries to open a second instance of Tartube
|
||||
# self.debug_warn_multiple_flag = False
|
||||
self.debug_warn_multiple_flag = True
|
||||
self.debug_warn_multiple_flag = False
|
||||
# Open the main window in the top-left corner of the desktop
|
||||
# self.debug_open_top_left_flag = False
|
||||
self.debug_open_top_left_flag = True
|
||||
self.debug_open_top_left_flag = False
|
||||
# Automatically open the system preferences window on startup
|
||||
self.debug_open_pref_win_flag = False
|
||||
# For Tartube developers who don't want to manually change
|
||||
# self.ytdl_path and self.ytdl_update_current on every startup
|
||||
# (assuming that self.debug_delete_data_flag is True), modify those
|
||||
# IVs
|
||||
# self.debug_modify_ytdl_flag = False
|
||||
# self.debug_ytdl_path = None
|
||||
# self.debug_ytdl_update_current = None
|
||||
self.debug_modify_ytdl_flag = True
|
||||
self.debug_ytdl_path = 'youtube-dl'
|
||||
self.debug_ytdl_update_current = 'Update using pip'
|
||||
self.debug_modify_ytdl_flag = False
|
||||
self.debug_ytdl_path = None
|
||||
self.debug_ytdl_update_current = None
|
||||
# self.debug_modify_ytdl_flag = True
|
||||
# self.debug_ytdl_path = 'youtube-dl'
|
||||
# self.debug_ytdl_update_current = 'Update using pip'
|
||||
|
||||
|
||||
def do_startup(self):
|
||||
|
||||
"""Gio.Application standard function."""
|
||||
|
||||
print('ap 454 do_startup')
|
||||
|
||||
GObject.threads_init()
|
||||
Gtk.Application.do_startup(self)
|
||||
|
||||
@ -681,6 +683,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""Gio.Application standard function."""
|
||||
|
||||
print('ap 684 do_activate')
|
||||
|
||||
# Only allow a single main window (raise any existing main windows)
|
||||
if not self.main_win_obj:
|
||||
self.start()
|
||||
@ -712,6 +716,8 @@ class TartubeApp(Gtk.Application):
|
||||
handled by self.stop().
|
||||
"""
|
||||
|
||||
print('ap 721 do_shutdown')
|
||||
|
||||
# Don't prompt the user before halting a download/update/refresh
|
||||
# operation, as we would do in calls to self.stop()
|
||||
if self.download_manager_obj:
|
||||
@ -735,6 +741,8 @@ class TartubeApp(Gtk.Application):
|
||||
Performs general initialisation.
|
||||
"""
|
||||
|
||||
print('ap 746 start')
|
||||
|
||||
# Delete Tartube's config file and data directory, if the debugging
|
||||
# flag is set
|
||||
if self.debug_delete_data_flag:
|
||||
@ -859,6 +867,8 @@ class TartubeApp(Gtk.Application):
|
||||
self.do_shutdown().
|
||||
"""
|
||||
|
||||
print('ap 872 stop')
|
||||
|
||||
# If a download/update/refresh operation is in progress, get
|
||||
# confirmation before stopping
|
||||
if self.current_manager_obj:
|
||||
@ -932,6 +942,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 947 system_error')
|
||||
|
||||
if self.main_win_obj:
|
||||
self.main_win_obj.errors_list_add_system_error(error_code, msg)
|
||||
else:
|
||||
@ -950,6 +962,8 @@ class TartubeApp(Gtk.Application):
|
||||
loading/saving.
|
||||
"""
|
||||
|
||||
print('ap 967 load_config')
|
||||
|
||||
# Sanity check
|
||||
if self.current_manager_obj \
|
||||
or not os.path.isfile(self.config_file_name) \
|
||||
@ -1049,6 +1063,8 @@ class TartubeApp(Gtk.Application):
|
||||
loading/saving.
|
||||
"""
|
||||
|
||||
print('ap 1068 save_config')
|
||||
|
||||
# Sanity check
|
||||
if self.current_manager_obj or self.disable_load_save_flag:
|
||||
return
|
||||
@ -1118,6 +1134,8 @@ class TartubeApp(Gtk.Application):
|
||||
loading/saving.
|
||||
"""
|
||||
|
||||
print('ap 1139 load_db')
|
||||
|
||||
# Sanity check
|
||||
path = os.path.join(self.data_dir, self.db_file_name)
|
||||
if self.current_manager_obj \
|
||||
@ -1204,6 +1222,8 @@ class TartubeApp(Gtk.Application):
|
||||
loading/saving.
|
||||
"""
|
||||
|
||||
print('ap 1227 save_db')
|
||||
|
||||
# Sanity check
|
||||
if self.current_manager_obj or self.disable_load_save_flag:
|
||||
return
|
||||
@ -1285,6 +1305,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 1310 switch_db')
|
||||
|
||||
# Sanity check
|
||||
if self.current_manager_obj or self.disable_load_save_flag:
|
||||
return
|
||||
@ -1351,7 +1373,10 @@ class TartubeApp(Gtk.Application):
|
||||
"""Called by self.switch_db().
|
||||
|
||||
Resets media registry IVs, so that a new Tartube database file can be
|
||||
created."""
|
||||
created.
|
||||
"""
|
||||
|
||||
print('ap 1381 reset_db')
|
||||
|
||||
# Reset IVs to their default states
|
||||
self.general_options_obj = options.OptionsManager()
|
||||
@ -1378,6 +1403,8 @@ class TartubeApp(Gtk.Application):
|
||||
destroyed by the user.
|
||||
"""
|
||||
|
||||
print('ap 1408 create_system_folders')
|
||||
|
||||
self.fixed_all_folder = self.add_folder(
|
||||
'All Videos',
|
||||
None, # No parent folder
|
||||
@ -1434,6 +1461,8 @@ class TartubeApp(Gtk.Application):
|
||||
window's menu item.
|
||||
"""
|
||||
|
||||
print('ap 1466 disable_load_save')
|
||||
|
||||
self.disable_load_save_flag = True
|
||||
self.main_win_obj.save_db_menu_item.set_sensitive(False)
|
||||
|
||||
@ -1452,6 +1481,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 1486 file_error_dialogue')
|
||||
|
||||
if self.main_win_obj:
|
||||
self.show_msg_dialogue(
|
||||
msg,
|
||||
@ -1472,6 +1503,8 @@ class TartubeApp(Gtk.Application):
|
||||
'Temporary Videos' folder. (The folders themselves are not deleted).
|
||||
"""
|
||||
|
||||
print('ap 1508 delete_temp_folders')
|
||||
|
||||
for name in self.media_name_dict:
|
||||
|
||||
dbid = self.media_name_dict[name]
|
||||
@ -1514,6 +1547,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 1552 convert_version')
|
||||
|
||||
num_list = version.split('.')
|
||||
if len(num_list) != 3:
|
||||
return None
|
||||
@ -1556,6 +1591,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 1596 download_manager_start')
|
||||
|
||||
if self.current_manager_obj:
|
||||
# Download, update or refresh operation already in progress
|
||||
return self.system_error(
|
||||
@ -1656,6 +1693,8 @@ class TartubeApp(Gtk.Application):
|
||||
continue running for a few seconds more.
|
||||
"""
|
||||
|
||||
print('ap 1698 download_manager_halt_timer')
|
||||
|
||||
if self.timer_id:
|
||||
self.timer_check_time = time.time() + self.timer_final_time
|
||||
|
||||
@ -1668,6 +1707,8 @@ class TartubeApp(Gtk.Application):
|
||||
widgets.
|
||||
"""
|
||||
|
||||
print('ap 1712 download_manager_finished')
|
||||
|
||||
# Get the time taken by the download operation, so we can convert it
|
||||
# into a nice string below (e.g. '05:15')
|
||||
time_num = int(
|
||||
@ -1727,6 +1768,8 @@ class TartubeApp(Gtk.Application):
|
||||
self.update_manager_finished() is called.
|
||||
"""
|
||||
|
||||
print('ap 1773 update_manager_start')
|
||||
|
||||
if self.current_manager_obj:
|
||||
# Download, update or refresh operation already in progress
|
||||
return self.system_error(
|
||||
@ -1770,6 +1813,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 1818 update_manager_finished')
|
||||
|
||||
# Any code can check whether a download/update/refresh operation is in
|
||||
# progress, or not, by checking this IV
|
||||
self.current_manager_obj = None
|
||||
@ -1838,6 +1883,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 1888 refresh_manager_start')
|
||||
|
||||
if self.current_manager_obj:
|
||||
# Download, update or refresh operation already in progress
|
||||
return self.system_error(
|
||||
@ -1884,6 +1931,8 @@ class TartubeApp(Gtk.Application):
|
||||
widgets.
|
||||
"""
|
||||
|
||||
print('ap 1936 refresh_manager_finished')
|
||||
|
||||
# Any code can check whether a download/update/refresh operation is in
|
||||
# progress, or not, by checking this IV
|
||||
self.current_manager_obj = None
|
||||
@ -1948,6 +1997,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2002 create_video_from_download')
|
||||
|
||||
# The downloads.DownloadItem handles a download for a video, a channel
|
||||
# or a playlist
|
||||
media_data_obj = download_item_obj.media_data_obj
|
||||
@ -2019,6 +2070,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2075 announce_video_download')
|
||||
|
||||
# If the video's parent media data object (a channel, playlist or
|
||||
# folder) is selected in the Video Index, update the Video Catalogue
|
||||
# for the downloaded video
|
||||
@ -2070,6 +2123,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2128 update_video_when_file_found')
|
||||
|
||||
# Only set the .name IV if the video is currently unnamed
|
||||
if video_obj.name == self.default_video_name:
|
||||
video_obj.set_name(video_obj.file_name)
|
||||
@ -2150,6 +2205,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2210 update_video_from_json')
|
||||
|
||||
json_path = os.path.join(
|
||||
video_obj.file_dir,
|
||||
video_obj.file_name + '.info.json',
|
||||
@ -2195,6 +2252,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2257 update_video_from_filesystem')
|
||||
|
||||
if video_obj.upload_time is None:
|
||||
video_obj.set_upload_time(os.path.getmtime(video_path))
|
||||
|
||||
@ -2238,6 +2297,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2302 add_video')
|
||||
|
||||
# Videos can't be placed inside other videos
|
||||
if parent_obj and isinstance(parent_obj, media.Video):
|
||||
return self.system_error(
|
||||
@ -2306,6 +2367,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2372 add_channel')
|
||||
|
||||
# Channels can only be placed inside an unrestricted media.Folder
|
||||
# object (if they have a parent at all)
|
||||
if parent_obj \
|
||||
@ -2384,6 +2447,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2452 add_playlist')
|
||||
|
||||
# Playlists can only be place inside an unrestricted media.Folder
|
||||
# object (if they have a parent at all)
|
||||
if parent_obj \
|
||||
@ -2460,6 +2525,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2530 add_folder')
|
||||
|
||||
# Folders can only be placed inside an unrestricted media.Folder object
|
||||
# (if they have a parent at all)
|
||||
if parent_obj \
|
||||
@ -2527,6 +2594,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2599 move_container_to_top')
|
||||
|
||||
# Do some basic checks
|
||||
if media_data_obj is None or isinstance(media_data_obj, media.Video) \
|
||||
or self.current_manager_obj or not media_data_obj.parent_obj:
|
||||
@ -2591,6 +2660,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2665 move_container')
|
||||
|
||||
# Do some basic checks
|
||||
if source_obj is None or isinstance(source_obj, media.Video) \
|
||||
or dest_obj is None or isinstance(dest_obj, media.Video) \
|
||||
@ -2711,6 +2782,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2787 delete_video')
|
||||
|
||||
if not isinstance(video_obj, media.Video):
|
||||
return self.system_error(
|
||||
115,
|
||||
@ -2769,6 +2842,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2847 delete_container')
|
||||
|
||||
# Check this isn't a video or a fixed folder (which cannot be removed)
|
||||
if isinstance(media_data_obj, media.Video) \
|
||||
or (
|
||||
@ -2881,6 +2956,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 2961 mark_video_new')
|
||||
|
||||
# (List of Video Index rows to update, at the end of this function)
|
||||
update_list = [self.fixed_new_folder]
|
||||
if not no_update_index_flag:
|
||||
@ -2978,6 +3055,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3060 mark_video_downloaded')
|
||||
|
||||
# (List of Video Index rows to update, at the end of this function)
|
||||
update_list = [video_obj.parent_obj, self.fixed_all_folder]
|
||||
|
||||
@ -3068,6 +3147,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3152 mark_video_favourite')
|
||||
|
||||
# (List of Video Index rows to update, at the end of this function)
|
||||
update_list = [self.fixed_fav_folder]
|
||||
if not no_update_index_flag:
|
||||
@ -3170,6 +3251,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3256 mark_folder_hidden')
|
||||
|
||||
if not isinstance(folder_obj, media.Folder):
|
||||
return self.system_error(
|
||||
120,
|
||||
@ -3226,6 +3309,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3314 mark_container_favourite')
|
||||
|
||||
if isinstance(media_data_obj, media.Video):
|
||||
return self.system_error(
|
||||
121,
|
||||
@ -3306,6 +3391,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3396 apply_download_options')
|
||||
|
||||
if self.current_manager_obj \
|
||||
or media_data_obj.options_obj\
|
||||
or (
|
||||
@ -3341,6 +3428,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3433 remove_download_options')
|
||||
|
||||
if self.current_manager_obj or not media_data_obj.options_obj:
|
||||
return self.system_error(
|
||||
123,
|
||||
@ -3371,6 +3460,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3465 watch_video_in_player')
|
||||
|
||||
path = os.path.join(
|
||||
video_obj.file_dir,
|
||||
video_obj.file_name + video_obj.file_ext,
|
||||
@ -3421,6 +3512,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3517 show_msg_dialogue')
|
||||
|
||||
# Prepare arguments
|
||||
main_win_obj = self.main_win_obj
|
||||
if not main_win_obj:
|
||||
@ -3490,6 +3583,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3588 timer_callback')
|
||||
|
||||
if self.timer_check_time is None:
|
||||
self.main_win_obj.progress_list_display_dl_stats()
|
||||
self.main_win_obj.results_list_update_row()
|
||||
@ -3529,6 +3624,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3629 on_button_stop_operation')
|
||||
|
||||
self.operation_halted_flag = True
|
||||
|
||||
if self.download_manager_obj:
|
||||
@ -3553,6 +3650,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3655 on_button_switch_view')
|
||||
|
||||
if not self.complex_catalogue_flag:
|
||||
self.complex_catalogue_flag = True
|
||||
else:
|
||||
@ -3580,6 +3679,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3684 on_menu_about')
|
||||
|
||||
dialogue_win = Gtk.AboutDialog()
|
||||
dialogue_win.set_transient_for(self.main_win_obj)
|
||||
dialogue_win.set_destroy_with_parent(True)
|
||||
@ -3617,6 +3718,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3723 on_menu_about_close')
|
||||
|
||||
action.destroy()
|
||||
|
||||
|
||||
@ -3635,6 +3738,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3743 on_menu_add_channel')
|
||||
|
||||
dialogue_win = mainwin.AddChannelDialogue(self.main_win_obj)
|
||||
response = dialogue_win.run()
|
||||
|
||||
@ -3719,6 +3824,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3829 on_menu_add_folder')
|
||||
|
||||
dialogue_win = mainwin.AddFolderDialogue(self.main_win_obj)
|
||||
response = dialogue_win.run()
|
||||
|
||||
@ -3783,6 +3890,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3895 on_menu_add_playlist')
|
||||
|
||||
dialogue_win = mainwin.AddPlaylistDialogue(self.main_win_obj)
|
||||
response = dialogue_win.run()
|
||||
|
||||
@ -3867,6 +3976,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 3981 on_menu_add_video')
|
||||
|
||||
dialogue_win = mainwin.AddVideoDialogue(self.main_win_obj)
|
||||
response = dialogue_win.run()
|
||||
|
||||
@ -3925,6 +4036,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4041 on_menu_check_all')
|
||||
|
||||
self.download_manager_start(True)
|
||||
|
||||
|
||||
@ -3942,6 +4055,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4060 on_menu_download_all')
|
||||
|
||||
self.download_manager_start(False)
|
||||
|
||||
|
||||
@ -3959,6 +4074,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4079 on_menu_general_options')
|
||||
|
||||
config.OptionsEditWin(self, self.general_options_obj, None)
|
||||
|
||||
|
||||
@ -3976,6 +4093,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4098 on_menu_refresh_db')
|
||||
|
||||
self.refresh_manager_start()
|
||||
|
||||
|
||||
@ -3993,6 +4112,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4117 on_menu_save_db')
|
||||
|
||||
self.save_db()
|
||||
|
||||
# Show a dialogue window for confirmation (unless file load/save has
|
||||
@ -4021,6 +4142,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4147 on_menu_show_hidden')
|
||||
|
||||
for name in self.media_name_dict:
|
||||
|
||||
dbid = self.media_name_dict[name]
|
||||
@ -4045,6 +4168,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4173 on_menu_system_preferences')
|
||||
|
||||
config.SystemPrefWin(self)
|
||||
|
||||
|
||||
@ -4063,6 +4188,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4193 on_menu_test')
|
||||
|
||||
# Add media data objects for testing: videos, channels, playlists and/
|
||||
# or folders
|
||||
testing.add_test_media(self)
|
||||
@ -4097,6 +4224,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4229 on_menu_update_ytdl')
|
||||
|
||||
self.update_manager_start()
|
||||
|
||||
|
||||
@ -4114,6 +4243,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4248 on_menu_quit')
|
||||
|
||||
self.stop()
|
||||
|
||||
|
||||
@ -4135,6 +4266,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4271 reject_media_name')
|
||||
|
||||
# Get the existing media data object with this name
|
||||
dbid = self.media_name_dict[name]
|
||||
media_data_obj = self.media_reg_dict[dbid]
|
||||
@ -4171,6 +4304,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4309 set_bandwidth_default')
|
||||
|
||||
if value < self.bandwidth_min or value > self.bandwidth_max:
|
||||
return self.system_error(
|
||||
124,
|
||||
@ -4188,6 +4323,8 @@ class TartubeApp(Gtk.Application):
|
||||
progress, the new setting is applied to the next download job.
|
||||
"""
|
||||
|
||||
print('ap 4328 set_bandwidth_apply_flag')
|
||||
|
||||
if not flag:
|
||||
self.bandwidth_apply_flag = False
|
||||
else:
|
||||
@ -4196,6 +4333,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_complex_index_flag(self, flag):
|
||||
|
||||
print('ap 4338 set_complex_index_flag')
|
||||
|
||||
if not flag:
|
||||
self.complex_index = False
|
||||
else:
|
||||
@ -4204,16 +4343,22 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_match_first_chars(self, num_chars):
|
||||
|
||||
print('ap 4348 set_match_first_chars')
|
||||
|
||||
self.match_first_chars = num_chars
|
||||
|
||||
|
||||
def set_match_ignore_chars(self, num_chars):
|
||||
|
||||
print('ap 4355 set_match_ignore_chars')
|
||||
|
||||
self.match_ignore_chars = num_chars
|
||||
|
||||
|
||||
def set_match_method(self, method):
|
||||
|
||||
print('ap 4362 set_match_method')
|
||||
|
||||
self.match_method = method
|
||||
|
||||
|
||||
@ -4226,6 +4371,8 @@ class TartubeApp(Gtk.Application):
|
||||
download job.
|
||||
"""
|
||||
|
||||
print('ap 4376 set_num_worker_apply_flag')
|
||||
|
||||
if not flag:
|
||||
self.bandwidth_apply_flag = False
|
||||
else:
|
||||
@ -4247,6 +4394,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
"""
|
||||
|
||||
print('ap 4399 set_num_worker_default')
|
||||
|
||||
if value < self.num_worker_min or value > self.num_worker_max:
|
||||
return self.system_error(
|
||||
125,
|
||||
@ -4262,6 +4411,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_operation_auto_update_flag(self, flag):
|
||||
|
||||
print('ap 4416 set_operation_auto_update_flag')
|
||||
|
||||
if not flag:
|
||||
self.operation_auto_update_flag = False
|
||||
else:
|
||||
@ -4270,6 +4421,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_operation_dialogue_flag(self, flag):
|
||||
|
||||
print('ap 4426 set_operation_dialogue_flag')
|
||||
|
||||
if not flag:
|
||||
self.operation_dialogue_flag = False
|
||||
else:
|
||||
@ -4278,6 +4431,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_operation_save_flag(self, flag):
|
||||
|
||||
print('ap 4436 set_operation_save_flag')
|
||||
|
||||
if not flag:
|
||||
self.operation_save_flag = False
|
||||
else:
|
||||
@ -4286,6 +4441,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_use_module_moviepy_flag(self, flag):
|
||||
|
||||
print('ap 4446 set_use_module_moviepy_flag')
|
||||
|
||||
if not flag:
|
||||
self.use_module_moviepy_flag = False
|
||||
else:
|
||||
@ -4294,6 +4451,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_use_module_moviepy_flag(self, flag):
|
||||
|
||||
print('ap 4456 set_use_module_moviepy_flag')
|
||||
|
||||
if not flag:
|
||||
self.use_module_validators_flag = False
|
||||
else:
|
||||
@ -4302,16 +4461,22 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_ytdl_path(self, path):
|
||||
|
||||
print('ap 4466 set_ytdl_path')
|
||||
|
||||
self.ytdl_path = path
|
||||
|
||||
|
||||
def set_ytdl_update_current(self, string):
|
||||
|
||||
print('ap 4473 set_ytdl_update_current')
|
||||
|
||||
self.ytdl_update_current = string
|
||||
|
||||
|
||||
def set_ytdl_write_stderr_flag(self, flag):
|
||||
|
||||
print('ap 4480 set_ytdl_write_stderr_flag')
|
||||
|
||||
if not flag:
|
||||
self.ytdl_write_stderr_flag = False
|
||||
else:
|
||||
@ -4320,6 +4485,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_ytdl_write_stdout_flag(self, flag):
|
||||
|
||||
print('ap 4490 set_ytdl_write_stdout_flag')
|
||||
|
||||
if not flag:
|
||||
self.ytdl_write_stdout_flag = False
|
||||
else:
|
||||
@ -4328,6 +4495,8 @@ class TartubeApp(Gtk.Application):
|
||||
|
||||
def set_ytdl_write_verbose_flag(self, flag):
|
||||
|
||||
print('ap 4500 set_ytdl_write_verbose_flag')
|
||||
|
||||
if not flag:
|
||||
self.ytdl_write_verbose_flag = False
|
||||
else:
|
||||
|
510
lib/mainwin.py
510
lib/mainwin.py
File diff suppressed because it is too large
Load Diff
98
lib/media.py
98
lib/media.py
@ -401,40 +401,6 @@ class GenericRemoteContainer(GenericContainer):
|
||||
self.vid_count += 1
|
||||
|
||||
|
||||
def OLDdo_sort(self, obj1, obj2):
|
||||
|
||||
"""Sorting function used by functools.cmp_to_key(), and called by
|
||||
self.sort_children().
|
||||
|
||||
Sort videos by upload time, with the most recent video first.
|
||||
|
||||
When downloading a channel or playlist, we assume that YouTube (etc)
|
||||
supplies us with the most recent upload first. Therefore, when the
|
||||
upload time is the same, sort by the order in youtube-dl fetches the
|
||||
videos.
|
||||
|
||||
Args:
|
||||
|
||||
obj1, obj2 (media.Video) - Video objects being sorted
|
||||
|
||||
Returns:
|
||||
|
||||
-1 if obj1 comes first, 1 if obj2 comes first, 0 if they are equal
|
||||
|
||||
"""
|
||||
|
||||
if obj1.upload_time < obj2.upload_time:
|
||||
return 1
|
||||
elif obj1.upload_time == obj2.upload_time:
|
||||
if obj1.receive_time < obj2.receive_time:
|
||||
return -1
|
||||
elif obj1.receive_time == obj2.receive_time:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
def do_sort(self, obj1, obj2):
|
||||
|
||||
"""Sorting function used by functools.cmp_to_key(), and called by
|
||||
@ -1326,70 +1292,6 @@ class Folder(GenericContainer):
|
||||
# def del_child(): # Inherited from GenericContainer
|
||||
|
||||
|
||||
def OLDdo_sort(self, obj1, obj2):
|
||||
|
||||
"""Sorting function used by functools.cmp_to_key(), and called by
|
||||
self.sort_children().
|
||||
|
||||
Sorts the child media.Video, media.Channel, media.Playlist and
|
||||
media.Folder objects.
|
||||
|
||||
Firstly, sort by class - folders, channels/playlists, then videos.
|
||||
|
||||
Within folders, channels and playlists, sort alphabetically. Within
|
||||
videos, sort by upload time.
|
||||
|
||||
Args:
|
||||
|
||||
obj1, obj2 (media.Video, media.Channel, media.Playlist or
|
||||
media.Folder) - Media data objects being sorted
|
||||
|
||||
Returns:
|
||||
|
||||
-1 if obj1 comes first, 1 if obj2 comes first, 0 if they are equal
|
||||
|
||||
"""
|
||||
|
||||
if str(obj1.__class__) == str(obj2.__class__) \
|
||||
or (
|
||||
isinstance(obj1, GenericRemoteContainer) \
|
||||
and isinstance(obj2, GenericRemoteContainer)
|
||||
):
|
||||
if isinstance(obj1, Video):
|
||||
|
||||
if obj1.upload_time < obj2.upload_time:
|
||||
return 1
|
||||
elif obj1.upload_time == obj2.upload_time:
|
||||
if obj1.receive_time < obj2.receive_time:
|
||||
return -1
|
||||
elif obj1.receive_time == obj2.receive_time:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
else:
|
||||
if obj1.name.lower() < obj2.name.lower:
|
||||
return -1
|
||||
elif obj1.name.lower() == obj2.name.lower:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
else:
|
||||
|
||||
if isinstance(obj1, Folder):
|
||||
return -1
|
||||
elif isinstance(obj2, Folder):
|
||||
return 1
|
||||
elif isinstance(obj1, Channel) or isinstance(obj1, Playlist):
|
||||
return -1
|
||||
elif isinstance(obj2, Channel) or isinstance(obj2, Playlist):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def do_sort(self, obj1, obj2):
|
||||
|
||||
"""Sorting function used by functools.cmp_to_key(), and called by
|
||||
|
@ -105,90 +105,6 @@ class UpdateManager(threading.Thread):
|
||||
# Public class methods
|
||||
|
||||
|
||||
def OLDrun(self):
|
||||
|
||||
"""Called as a result of self.__init__().
|
||||
|
||||
Based on code from downloads.VideoDownloader.do_download().
|
||||
|
||||
Creates a child process to run the youtube-dl update.
|
||||
|
||||
Reads from the child process STDOUT and STDERR, and calls the main
|
||||
application with the result of the update (success or failure).
|
||||
"""
|
||||
|
||||
# Prepare the system command
|
||||
|
||||
# The user can change the system command for updating youtube-dl,
|
||||
# depending on how it was installed
|
||||
# (For example, if youtube-dl was installed via pip, then it must be
|
||||
# updated via pip)
|
||||
cmd_list \
|
||||
= self.app_obj.ytdl_update_dict[self.app_obj.ytdl_update_current]
|
||||
|
||||
# Create a new child process using that command
|
||||
self.create_child_process(cmd_list)
|
||||
|
||||
# So that we can read from the child process STDOUT and STDERR, attach
|
||||
# a file descriptor to the PipeReader objects
|
||||
if self.child_process is not None:
|
||||
|
||||
self.stdout_reader.attach_file_descriptor(
|
||||
self.child_process.stdout,
|
||||
)
|
||||
|
||||
self.stderr_reader.attach_file_descriptor(
|
||||
self.child_process.stderr,
|
||||
)
|
||||
|
||||
while self.is_child_process_alive():
|
||||
|
||||
# Read from the child process STDOUT, and convert into unicode for
|
||||
# Python's convenience
|
||||
while not self.stdout_queue.empty():
|
||||
|
||||
stdout = self.stdout_queue.get_nowait().rstrip()
|
||||
stdout = utils.convert_item(stdout, to_unicode=True)
|
||||
|
||||
if stdout:
|
||||
# "It looks like you installed youtube-dl with a package
|
||||
# manager, pip, setup.py or a tarball. Please use that to
|
||||
# update."
|
||||
if re.search('It looks like you installed', stdout):
|
||||
self.stderr_list.append(stdout)
|
||||
else:
|
||||
self.stdout_list.append(stdout)
|
||||
|
||||
# The child process has finished
|
||||
while not self.stderr_queue.empty():
|
||||
|
||||
# Read from the child process STDERR queue (we don't need to read
|
||||
# it in real time), and convert into unicode for python's
|
||||
# convenience
|
||||
stderr = self.stderr_queue.get_nowait().rstrip()
|
||||
stderr = utils.convert_item(stderr, to_unicode=True)
|
||||
|
||||
if stderr:
|
||||
self.stderr_list.append(stderr)
|
||||
|
||||
# (Generate our own error messages for debugging purposes, in certain
|
||||
# situations)
|
||||
if self.child_process is None:
|
||||
self.stderr_list.append('Download did not start')
|
||||
|
||||
elif self.child_process.returncode > 0:
|
||||
self.stderr_list.append(
|
||||
'Child process exited with non-zero code: {}'.format(
|
||||
self.child_process.returncode,
|
||||
)
|
||||
)
|
||||
|
||||
# Operation complete; inform the main application of success or failure
|
||||
if self.stderr_list:
|
||||
self.app_obj.update_manager_finished(False)
|
||||
else:
|
||||
self.app_obj.update_manager_finished(True)
|
||||
|
||||
def run(self):
|
||||
|
||||
"""Called as a result of self.__init__().
|
||||
|
BIN
screenshots/tartube.png
Normal file
BIN
screenshots/tartube.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 KiB |
2
setup.py
2
setup.py
@ -35,7 +35,7 @@ import setuptools
|
||||
# Setup
|
||||
setuptools.setup(
|
||||
name='tartube',
|
||||
version='0.1.007',
|
||||
version='0.1.015',
|
||||
description='GUI front-end for youtube-dl',
|
||||
# long_description=long_description,
|
||||
long_description="""Tartube is a GUI front-end for youtube-dl, partly based on youtube-dl-gui and written in Python 3 / Gtk 3""",
|
||||
|
@ -34,8 +34,8 @@ from lib import mainapp
|
||||
|
||||
# 'Global' variables
|
||||
__packagename__ = 'tartube'
|
||||
__version__ = '0.1.007'
|
||||
__date__ = '28 May 2019'
|
||||
__version__ = '0.1.015'
|
||||
__date__ = '3 Jun 2019'
|
||||
__copyright__ = 'Copyright \xc2\xa9 2019 A S Lewis'
|
||||
__license__ = """
|
||||
Copyright \xc2\xa9 2019 A S Lewis.
|
||||
|
Loading…
x
Reference in New Issue
Block a user