diff --git a/CHANGES b/CHANGES index 693a560..a5b4088 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,21 @@ +v1.2.008 (30 Sep 2019) +------------------------------------------------------------------------------- + +MINOR NEW FEATURES +- Tartube now ignores the YouTube 'WARNING: video doesn't have subtitles' + by default. You can change this setting, if you want to + +MAJOR FIXES +- When moving a channel/playlist/folder to a different place on your + filesystem, or when renaming a channel/playlist/folder, in certain rare + situations data in the Tartube database isn't updated correctly. This may + lead to a freeze or a crash. I'm not sure yet what the cause is, but I have + added temporary code to prevent the problem affecting any user +- Fixed error messages generated when checking/downloading individual + channels/playlists/folders +- Fixed faulty code for importing videos/channels/playlists/folders into the + database + v1.2.0 (31 Aug 2019) ------------------------------------------------------------------------------- @@ -34,7 +52,7 @@ MAJOR NEW FEATURES - Plain text exports of Tartube's database can now be re-imported. Some inconsistencies with the import process (from JSON and plain text files) have been fixed - + MINOR NEW FEATURES - Added tooltips in several places. If you don't want to see tooltips above videos/channels/playlists/folders, you can turn them off @@ -57,7 +75,7 @@ MINOR NEW FEATURES - The Tartube website can now be opened from the main window menu - XDG has been added as an optional dependency, for the benefit of Debian packagers - + MAJOR FIXES - The MS Windows installer should now work for everyone - Refresh operations are now stable (should not crash) on systems with Gtk 3.22 @@ -68,7 +86,7 @@ MAJOR FIXES contains - Marking/unmarking a video as favourite caused certain problems, which should now be fixed - + MINOR FIXES - Fixed some unicode errors in reading JSON and plain text files - Fixed the wrong page size displayed in the toolbar at the bototm of the diff --git a/README.rst b/README.rst index 429e6a5..bc0d35b 100644 --- a/README.rst +++ b/README.rst @@ -12,11 +12,11 @@ Problems can be reported at `our GitHub page `__ from Sourceforge -- `MS Windows (64-bit) installer `__ from Sourceforge -- `Source code `__ from Sourceforge +- `MS Windows (32-bit) installer `__ from Sourceforge +- `MS Windows (64-bit) installer `__ from Sourceforge +- `Source code `__ from Sourceforge - `Source code `__ and `support `__ from GitHub Why should I use Tartube? diff --git a/nsis/tartube_install_32bit.nsi b/nsis/tartube_install_32bit.nsi index 741af6b..caae07c 100644 --- a/nsis/tartube_install_32bit.nsi +++ b/nsis/tartube_install_32bit.nsi @@ -1,4 +1,4 @@ -# Tartube v1.2.0 installer script for MS Windows +# Tartube v1.2.008 installer script for MS Windows # # Copyright (C) 2019 A S Lewis # @@ -139,7 +139,7 @@ ;Name and file Name "Tartube" - OutFile "install-tartube-1.2.0-32bit.exe" + OutFile "install-tartube-1.2.008-32bit.exe" ;Default installation folder InstallDir "$LOCALAPPDATA\Tartube" @@ -244,7 +244,7 @@ Section "Tartube" SecClient "Publisher" "A S Lewis" WriteRegStr HKLM \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \ - "DisplayVersion" "1.2.0" + "DisplayVersion" "1.2.008" # Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" diff --git a/nsis/tartube_install_64bit.nsi b/nsis/tartube_install_64bit.nsi index 53035fa..4856a55 100644 --- a/nsis/tartube_install_64bit.nsi +++ b/nsis/tartube_install_64bit.nsi @@ -1,4 +1,4 @@ -# Tartube v1.2.0 installer script for MS Windows +# Tartube v1.2.008 installer script for MS Windows # # Copyright (C) 2019 A S Lewis # @@ -140,7 +140,7 @@ ;Name and file Name "Tartube" - OutFile "install-tartube-1.2.0-64bit.exe" + OutFile "install-tartube-1.2.008-64bit.exe" ;Default installation folder InstallDir "$LOCALAPPDATA\Tartube" @@ -245,7 +245,7 @@ Section "Tartube" SecClient "Publisher" "A S Lewis" WriteRegStr HKLM \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tartube" \ - "DisplayVersion" "1.2.0" + "DisplayVersion" "1.2.008" # Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" diff --git a/setup.py b/setup.py index 7068e2c..60cbbd7 100755 --- a/setup.py +++ b/setup.py @@ -62,7 +62,7 @@ if env_var_value is not None: # Setup setuptools.setup( name='tartube', - version='1.2.0', + version='1.2.008', description='GUI front-end for youtube-dl', # long_description=long_description, long_description="""Tartube is a GUI front-end for youtube-dl, partly based diff --git a/tartube/config.py b/tartube/config.py index bffdc1c..04830e2 100644 --- a/tartube/config.py +++ b/tartube/config.py @@ -4749,6 +4749,7 @@ class SystemPrefWin(GenericPrefWin): self.setup_scheduling_tab() self.setup_operations_tab() self.setup_ytdl_tab() + self.setup_ignore_tab() self.setup_debug_tab() @@ -5624,37 +5625,61 @@ class SystemPrefWin(GenericPrefWin): ) checkbutton.connect('toggled', self.on_json_button_toggled) - checkbutton2 = self.add_checkbutton(grid, + + def setup_ignore_tab(self): + + """Called by self.setup_tabs(). + + Sets up the 'Ignore' tab. + """ + + tab, grid = self.add_notebook_tab('_Ignore') + + # Ignore options + self.add_label(grid, + 'Ignore options', + 0, 0, 1, 1, + ) + + checkbutton = self.add_checkbutton(grid, 'Ignore \'Requested formats are incompatible for merge\' warnings', self.app_obj.ignore_merge_warning_flag, True, # Can be toggled by user - 0, 9, grid_width, 1, + 0, 1, 1, 1, ) - checkbutton2.connect('toggled', self.on_merge_button_toggled) + checkbutton.connect('toggled', self.on_merge_button_toggled) - checkbutton3 = self.add_checkbutton(grid, + checkbutton2 = self.add_checkbutton(grid, 'Ignore YouTube copyright errors', self.app_obj.ignore_yt_copyright_flag, True, # Can be toggled by user - 0, 10, grid_width, 1, + 0, 2, 1, 1, ) - checkbutton3.connect('toggled', self.on_copyright_button_toggled) + checkbutton2.connect('toggled', self.on_copyright_button_toggled) - checkbutton4 = self.add_checkbutton(grid, + checkbutton3 = self.add_checkbutton(grid, 'Ignore \'Child process exited with non-zero code\' errors', self.app_obj.ignore_child_process_exit_flag, True, # Can be toggled by user - 0, 11, grid_width, 1, + 0, 3, 1, 1, ) - checkbutton4.connect('toggled', self.on_child_process_button_toggled) + checkbutton3.connect('toggled', self.on_child_process_button_toggled) - checkbutton5 = self.add_checkbutton(grid, + checkbutton4 = self.add_checkbutton(grid, 'Ignore \'There are no annotations to write\' warnings', self.app_obj.ignore_no_annotations_flag, True, # Can be toggled by user - 0, 12, grid_width, 1, + 0, 4, 1, 1, ) - checkbutton5.connect('toggled', self.on_no_annotations_button_toggled) + checkbutton4.connect('toggled', self.on_no_annotations_button_toggled) + + checkbutton5 = self.add_checkbutton(grid, + 'Ignore \'Video doesn\'t have subtitles\' warnings', + self.app_obj.ignore_no_subtitles_flag, + True, # Can be toggled by user + 0, 5, 1, 1, + ) + checkbutton5.connect('toggled', self.on_no_subtitles_button_toggled) def setup_debug_tab(self): @@ -6415,7 +6440,7 @@ class SystemPrefWin(GenericPrefWin): def on_no_annotations_button_toggled(self, checkbutton): - """Called from callback in self.setup_ytdl_tab(). + """Called from callback in self.setup_ignore_tab(). Enables/disables ignoring of the 'no annotations' warning messages. @@ -6433,6 +6458,26 @@ class SystemPrefWin(GenericPrefWin): self.app_obj.set_ignore_no_annotations_flag(False) + def on_no_subtitles_button_toggled(self, checkbutton): + + """Called from callback in self.setup_ignore_tab(). + + Enables/disables ignoring of the 'no subtitles' warning messages. + + Args: + + checkbutton (Gtk.CheckButton): The widget clicked + + """ + + if checkbutton.get_active() \ + and not self.app_obj.ignore_no_subtitles_flag: + self.app_obj.set_ignore_no_subtitles_flag(True) + elif not checkbutton.get_active() \ + and self.app_obj.ignore_no_subtitles_flag: + self.app_obj.set_ignore_no_subtitles_flag(False) + + def on_reset_ffmpeg_button_clicked(self, button, entry): """Called from callback in self.setup_ytdl_tab(). diff --git a/tartube/downloads.py b/tartube/downloads.py index 5e2b15d..e31cfb9 100755 --- a/tartube/downloads.py +++ b/tartube/downloads.py @@ -373,7 +373,7 @@ class DownloadManager(threading.Thread): if DEBUG_FUNC_FLAG: print('dl 375 check_master_slave') - + for worker_obj in self.worker_list: if not worker_obj.available_flag \ @@ -1564,7 +1564,7 @@ class VideoDownloader(object): if DEBUG_FUNC_FLAG: print('dl 1566 check_dl_is_correct_type') - + if isinstance(self.download_item_obj.media_data_obj, media.Video): self.stop() @@ -2048,8 +2048,16 @@ class VideoDownloader(object): if not os.path.isfile(thumb_path): request_obj = requests.get(thumbnail) - with open(thumb_path, 'wb') as outfile: - outfile.write(request_obj.content) + +# with open(thumb_path, 'wb') as outfile: +# outfile.write(request_obj.content) + # v1.2.006 This crashes if the directory specified by + # thumb_path doesn't exist, so need to use 'try' + try: + with open(thumb_path, 'wb') as outfile: + outfile.write(request_obj.content) + except: + pass # If a new media.Video object was created, add a line to the Results # List, as well as updating the Video Catalogue @@ -2616,6 +2624,13 @@ class VideoDownloader(object): ): return True + elif app_obj.ignore_no_subtitles_flag \ + and re.search( + r'video doesn\'t have subtitles', + stderr, + ): + return True + else: # Not ignorable return False @@ -2727,7 +2742,7 @@ class VideoDownloader(object): if DEBUG_FUNC_FLAG: print('dl 2729 set_temp_destination') - + self.temp_path = path self.temp_filename = filename self.temp_extension = extension @@ -2739,7 +2754,7 @@ class VideoDownloader(object): if DEBUG_FUNC_FLAG: print('dl 2741 reset_temp_destination') - + self.temp_path = None self.temp_filename = None self.temp_extension = None diff --git a/tartube/mainapp.py b/tartube/mainapp.py index 6171a63..65c9e48 100755 --- a/tartube/mainapp.py +++ b/tartube/mainapp.py @@ -607,6 +607,9 @@ class TartubeApp(Gtk.Application): # Flag set to True if 'There are no annotations to write' messages # should be ignored (in the Errors/Warnings tab) self.ignore_no_annotations_flag = True + # Flag set to True if 'video doesn't have subtitles' errors should be + # ignored (in the Errors/Warnings tab) + self.ignore_no_subtitles_flag = True # During a download operation, the number of simultaneous downloads # allowed. (An instruction to youtube-dl to download video(s) from a @@ -1234,14 +1237,13 @@ class TartubeApp(Gtk.Application): # want new_mswin_flag = True custom_flag = True + name = utils.upper_case_first(__main__.__packagename__) dialogue_win = self.dialogue_manager_obj.show_msg_dialogue( - 'Click OK to create a folder in which\n' \ - + utils.upper_case_first(__main__.__packagename__) \ - + ' can store its videos\n\nIf you have used ' \ - + utils.upper_case_first(__main__.__packagename__) \ - + ' before,\nyou can select an existing folder\ninstead of' - + ' creating a new one', + 'Welcome to ' + name + '!\n\nClick OK to create a folder' \ + + ' in which\n' + name + ' can store its videos\n\n' \ + + 'If you have used ' + name + ' before,\nyou can select' \ + + ' an existing folder\ninstead of creating a new one', 'info', 'ok', self.main_win_obj, @@ -1353,9 +1355,9 @@ class TartubeApp(Gtk.Application): 'Gtk v' + str(self.gtk_version_major) + '.' \ + str(self.gtk_version_minor) + '.' \ + str(self.gtk_version_micro) \ - + ' is broken, which will cause problems when running ' \ + + ' is broken, which may cause problems when running ' \ + utils.upper_case_first(__main__.__packagename__) \ - + '. Please update it to at least Gtk v3.24', + + '. If possible, please update it to at least Gtk v3.24', ) # If file load/save has been disabled, we can now show a dialogue @@ -1748,6 +1750,9 @@ class TartubeApp(Gtk.Application): if version >= 1001077: # v1.1.077 self.ignore_no_annotations_flag \ = json_dict['ignore_no_annotations_flag'] + if version >= 1002004: # v1.2.004 + self.ignore_no_subtitles_flag \ + = json_dict['ignore_no_subtitles_flag'] # (Setting the value of the Gtk widgets automatically sets the IVs) self.main_win_obj.spinbutton.set_value(json_dict['num_worker_default']) @@ -1891,6 +1896,7 @@ class TartubeApp(Gtk.Application): 'ignore_child_process_exit_flag': \ self.ignore_child_process_exit_flag, 'ignore_no_annotations_flag': self.ignore_no_annotations_flag, + 'ignore_no_subtitles_flag': self.ignore_no_subtitles_flag, 'num_worker_default': self.num_worker_default, 'num_worker_apply_flag': self.num_worker_apply_flag, @@ -2355,6 +2361,17 @@ class TartubeApp(Gtk.Application): options_obj.options_dict.pop('to_audio') + if version < 1002005: # v1.2.005 + + # After moving videos from one filesystem location to another, + # some media.Video had the wrong value for their .file_dir IV + # To be safe, reset them all + for media_data_obj in self.media_reg_dict.values(): + if isinstance(media_data_obj, media.Video): + + media_data_obj.reset_file_dir(self) + + def save_db(self): """Called by self.start(), .stop(), .switch_db(), @@ -3651,7 +3668,7 @@ class TartubeApp(Gtk.Application): if DEBUG_FUNC_FLAG: print('ap 3653 announce_video_clone') - + video_path = os.path.abspath( os.path.join( video_obj.file_dir, @@ -4167,8 +4184,13 @@ class TartubeApp(Gtk.Application): # All videos which are descendents of media_data_obj must have their # .file_dir IV updated to the new location - for video_obj in media_data_obj.compile_all_videos( [] ): - video_obj.reset_file_dir(self) +# for video_obj in media_data_obj.compile_all_videos( [] ): +# video_obj.reset_file_dir(self) + # 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 # restarts it, then tries to perform a download operation, a load of @@ -4304,7 +4326,7 @@ class TartubeApp(Gtk.Application): if DEBUG_FUNC_FLAG: print('ap 4306 move_container_continue') - + source_obj = media_list[0] dest_obj = media_list[1] @@ -4324,8 +4346,13 @@ class TartubeApp(Gtk.Application): # All videos which are descendents of dest_obj must have their # .file_dir IV updated to the new location - for video_obj in source_obj.compile_all_videos( [] ): - video_obj.reset_file_dir(self) +# for video_obj in source_obj.compile_all_videos( [] ): +# video_obj.reset_file_dir(self) + # 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 # restarts it, then tries to perform a download operation, a load of @@ -5254,7 +5281,7 @@ class TartubeApp(Gtk.Application): if DEBUG_FUNC_FLAG: print('ap 5256 rename_container') - + # Do some basic checks if media_data_obj is None or isinstance(media_data_obj, media.Video) \ or self.current_manager_obj or self.main_win_obj.config_win_list \ @@ -6310,7 +6337,7 @@ class TartubeApp(Gtk.Application): if DEBUG_FUNC_FLAG: print('ap 6312 reset_options_manager') - + edit_win_obj = data_list.pop(0) # Replace the old object with a new one, which has the effect of @@ -7703,6 +7730,17 @@ class TartubeApp(Gtk.Application): self.ignore_no_annotations_flag = True + def set_ignore_no_subtitles_flag(self, flag): + + if DEBUG_FUNC_FLAG: + print('ap 7681 set_ignore_no_subtitles_flag') + + if not flag: + self.ignore_no_subtitles_flag = False + else: + self.ignore_no_subtitles_flag = True + + def set_ignore_yt_copyright_flag(self, flag): if DEBUG_FUNC_FLAG: diff --git a/tartube/mainwin.py b/tartube/mainwin.py index 463c40a..80df17b 100755 --- a/tartube/mainwin.py +++ b/tartube/mainwin.py @@ -1646,7 +1646,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 1648 redraw_main_toolbar') - + self.setup_main_toolbar() if self.app_obj.disable_dl_all_flag: @@ -1839,7 +1839,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 1841 enable_tooltips') - + self.video_index_treeview.set_tooltip_column(2) if update_catalogue_flag: self.video_catalogue_redraw_all( @@ -1864,7 +1864,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 1866 disable_tooltips') - + self.video_index_treeview.set_tooltip_column(-1) if update_catalogue_flag: self.video_catalogue_redraw_all( @@ -1882,7 +1882,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 1884 enable_dl_all_buttons') - + # This setting doesn't apply during a download/update/refresh # operation if not self.app_obj.current_manager_obj: @@ -1901,7 +1901,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 1903 move_container') - + # This setting doesn't apply during a download/update/refresh # operation if not self.app_obj.current_manager_obj: @@ -2777,7 +2777,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 2779 video_catalogue_multi_popup_menu') - + # Convert row_list, a list of mainwin.CatalogueRow objects, into a # list of media.Video objects video_list = [] @@ -3191,7 +3191,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 3193 video_index_setup_contents_submenu') - + mark_new_menu_item = Gtk.MenuItem.new_with_mnemonic('Mark as _new') mark_new_menu_item.connect( 'activate', @@ -3282,7 +3282,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 3284 add_watch_video_menu_items') - + # Watch video in player/download and watch if not video_obj.dl_flag: @@ -4618,7 +4618,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 4620 video_catalogue_toolbar_reset') - + self.catalogue_toolbar_current_page = 1 self.catalogue_toolbar_last_page = 1 @@ -4660,7 +4660,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 4662 video_catalogue_toolbar_update') - + self.catalogue_toolbar_current_page = page_num # If the page size is 0, then all videos are drawn on one page @@ -4787,7 +4787,7 @@ class MainWin(Gtk.ApplicationWindow): if DEBUG_FUNC_FLAG: print('mw 4789 progress_list_add_row') - + # Prepare the icon if isinstance(media_data_obj, media.Channel): pixbuf = self.pixbuf_dict['channel_small'] @@ -5663,7 +5663,7 @@ class MainWin(Gtk.ApplicationWindow): ) # Start a download operation - self.app_obj.download_manager_start(True, False, media_data_obj) + self.app_obj.download_manager_start(True, False, [media_data_obj] ) def on_video_index_delete_container(self, menu_item, media_data_obj): @@ -5712,7 +5712,7 @@ class MainWin(Gtk.ApplicationWindow): ) # Start a download operation - self.app_obj.download_manager_start(False, False, media_data_obj) + self.app_obj.download_manager_start(False, False, [media_data_obj] ) def on_video_index_drag_data_received(self, treeview, drag_context, x, y, \ @@ -6627,7 +6627,7 @@ class MainWin(Gtk.ApplicationWindow): ) # Start a download operation - self.app_obj.download_manager_start(True, False, media_data_obj) + self.app_obj.download_manager_start(True, False, [media_data_obj] ) def on_video_catalogue_check_multi(self, menu_item, media_data_list): @@ -6802,7 +6802,7 @@ class MainWin(Gtk.ApplicationWindow): ) # Start a download operation - self.app_obj.download_manager_start(False, False, media_data_obj) + self.app_obj.download_manager_start(False, False, [media_data_obj] ) def on_video_catalogue_download_multi(self, menu_item, media_data_list): @@ -6982,7 +6982,7 @@ class MainWin(Gtk.ApplicationWindow): self.app_obj.mark_video_downloaded(media_data_obj, False) # Now we're ready to start the download operation - self.app_obj.download_manager_start(False, False, media_data_obj) + self.app_obj.download_manager_start(False, False, [media_data_obj] ) def on_video_catalogue_remove_options(self, menu_item, media_data_obj): @@ -10287,7 +10287,7 @@ class ImportDialogue(Gtk.Dialog): # Update the data to be returned (eventually) to the calling # mainapp.TartubeApp.import_into_db() function - mini_dict = self.processed_dict[self.liststore[path][3]] + mini_dict = self.flat_db_dict[self.liststore[path][3]] mini_dict['import_flag'] = self.liststore[path][0] @@ -10309,7 +10309,7 @@ class ImportDialogue(Gtk.Dialog): for path in range(0, len(self.liststore)): self.liststore[path][0] = True - for mini_dict in self.processed_dict.values(): + for mini_dict in self.flat_db_dict.values(): mini_dict['import_flag'] = True @@ -10331,7 +10331,7 @@ class ImportDialogue(Gtk.Dialog): for path in range(0, len(self.liststore)): self.liststore[path][0] = False - for mini_dict in self.processed_dict.values(): + for mini_dict in self.flat_db_dict.values(): mini_dict['import_flag'] = False diff --git a/tartube/tartube b/tartube/tartube index 69c34e2..0f8e540 100755 --- a/tartube/tartube +++ b/tartube/tartube @@ -35,8 +35,8 @@ import mainapp # 'Global' variables __packagename__ = 'tartube' -__version__ = '1.2.0' -__date__ = '31 Aug 2019' +__version__ = '1.2.008' +__date__ = '30 Sep 2019' __copyright__ = 'Copyright \xa9 2019 A S Lewis' __license__ = """ Copyright \xc2\xa9 2019 A S Lewis. diff --git a/tartube/tartube_debian b/tartube/tartube_debian index ebb9516..fdef6c0 100755 --- a/tartube/tartube_debian +++ b/tartube/tartube_debian @@ -35,8 +35,8 @@ import mainapp # 'Global' variables __packagename__ = 'tartube' -__version__ = '1.2.0' -__date__ = '31 Aug 2019' +__version__ = '1.2.008' +__date__ = '30 Sep 2019' __copyright__ = 'Copyright \xa9 2019 A S Lewis' __license__ = """ Copyright \xc2\xa9 2019 A S Lewis.