2018-11-14 23:10:55 +00:00
import os
import time
import traceback
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
2020-04-22 21:00:35 +00:00
from hydrus . core import HydrusConstants as HC
from hydrus . core import HydrusData
from hydrus . core import HydrusExceptions
from hydrus . core import HydrusGlobals as HG
from hydrus . core import HydrusPaths
from hydrus . client import ClientConstants as CC
from hydrus . client import ClientExporting
from hydrus . client import ClientSearch
from hydrus . client import ClientTags
from hydrus . client . gui import ClientGUIACDropdown
from hydrus . client . gui import ClientGUICommon
from hydrus . client . gui import ClientGUIDialogsQuick
from hydrus . client . gui import ClientGUIListBoxes
from hydrus . client . gui import ClientGUIListCtrl
from hydrus . client . gui import ClientGUIScrolledPanels
from hydrus . client . gui import ClientGUIScrolledPanelsEdit
from hydrus . client . gui import ClientGUITime
from hydrus . client . gui import ClientGUITopLevelWindows
from hydrus . client . gui import QtPorting as QP
2018-11-14 23:10:55 +00:00
class EditExportFoldersPanel ( ClientGUIScrolledPanels . EditPanel ) :
def __init__ ( self , parent , export_folders ) :
ClientGUIScrolledPanels . EditPanel . __init__ ( self , parent )
2018-11-28 22:31:04 +00:00
self . _export_folders_panel = ClientGUIListCtrl . BetterListCtrlPanel ( self )
2018-11-14 23:10:55 +00:00
2019-05-01 21:24:42 +00:00
columns = [ ( ' name ' , 20 ) , ( ' path ' , - 1 ) , ( ' type ' , 12 ) , ( ' query ' , 16 ) , ( ' paused ' , 8 ) , ( ' period ' , 16 ) , ( ' phrase ' , 20 ) ]
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
self . _export_folders = ClientGUIListCtrl . BetterListCtrl ( self . _export_folders_panel , ' export_folders ' , 6 , 40 , columns , self . _ConvertExportFolderToListCtrlTuples , use_simple_delete = True , activation_callback = self . _Edit )
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
self . _export_folders_panel . SetListCtrl ( self . _export_folders )
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
self . _export_folders_panel . AddButton ( ' add ' , self . _AddFolder )
self . _export_folders_panel . AddButton ( ' edit ' , self . _Edit , enabled_only_on_selection = True )
self . _export_folders_panel . AddDeleteButton ( )
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
#
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
self . _export_folders . AddDatas ( export_folders )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP . VBoxLayout ( )
2018-11-14 23:10:55 +00:00
intro = ' Here you can set the client to regularly export a certain query to a particular location. '
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( vbox , ClientGUICommon . BetterStaticText ( self , intro ) , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( vbox , self . _export_folders_panel , CC . FLAGS_EXPAND_BOTH_WAYS )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . widget ( ) . setLayout ( vbox )
2018-11-14 23:10:55 +00:00
def _AddFolder ( self ) :
new_options = HG . client_controller . new_options
phrase = new_options . GetString ( ' export_phrase ' )
name = ' export folder '
path = ' '
export_type = HC . EXPORT_FOLDER_TYPE_REGULAR
2018-11-28 22:31:04 +00:00
delete_from_client_after_export = False
2018-11-14 23:10:55 +00:00
file_search_context = ClientSearch . FileSearchContext ( file_service_key = CC . LOCAL_FILE_SERVICE_KEY )
period = 15 * 60
2018-11-28 22:31:04 +00:00
export_folder = ClientExporting . ExportFolder ( name , path , export_type = export_type , delete_from_client_after_export = delete_from_client_after_export , file_search_context = file_search_context , period = period , phrase = phrase )
2018-11-14 23:10:55 +00:00
with ClientGUITopLevelWindows . DialogEdit ( self , ' edit export folder ' ) as dlg :
panel = EditExportFolderPanel ( dlg , export_folder )
dlg . SetPanel ( panel )
2019-11-14 03:56:30 +00:00
if dlg . exec ( ) == QW . QDialog . Accepted :
2018-11-14 23:10:55 +00:00
export_folder = panel . GetValue ( )
export_folder . SetNonDupeName ( self . _GetExistingNames ( ) )
self . _export_folders . AddDatas ( ( export_folder , ) )
def _ConvertExportFolderToListCtrlTuples ( self , export_folder ) :
2019-05-01 21:24:42 +00:00
( name , path , export_type , delete_from_client_after_export , file_search_context , run_regularly , period , phrase , last_checked , paused , run_now ) = export_folder . ToTuple ( )
2018-11-14 23:10:55 +00:00
if export_type == HC . EXPORT_FOLDER_TYPE_REGULAR :
pretty_export_type = ' regular '
elif export_type == HC . EXPORT_FOLDER_TYPE_SYNCHRONISE :
pretty_export_type = ' synchronise '
2018-11-28 22:31:04 +00:00
if delete_from_client_after_export :
pretty_export_type + = ' and deleting from the client! '
2019-01-09 22:59:03 +00:00
pretty_file_search_context = ' , ' . join ( predicate . ToString ( with_count = False ) for predicate in file_search_context . GetPredicates ( ) )
2018-11-14 23:10:55 +00:00
2019-05-01 21:24:42 +00:00
if run_regularly :
pretty_period = HydrusData . TimeDeltaToPrettyTimeDelta ( period )
else :
pretty_period = ' not running regularly '
if run_now :
pretty_period + = ' (running after dialog ok) '
if paused :
pretty_paused = ' yes '
else :
pretty_paused = ' '
2018-11-14 23:10:55 +00:00
pretty_phrase = phrase
2019-05-01 21:24:42 +00:00
display_tuple = ( name , path , pretty_export_type , pretty_file_search_context , pretty_paused , pretty_period , pretty_phrase )
2018-11-14 23:10:55 +00:00
2019-05-01 21:24:42 +00:00
sort_tuple = ( name , path , pretty_export_type , pretty_file_search_context , paused , period , phrase )
2018-11-14 23:10:55 +00:00
return ( display_tuple , sort_tuple )
2018-11-28 22:31:04 +00:00
def _Edit ( self ) :
2018-11-14 23:10:55 +00:00
export_folders = self . _export_folders . GetData ( only_selected = True )
for export_folder in export_folders :
with ClientGUITopLevelWindows . DialogEdit ( self , ' edit export folder ' ) as dlg :
panel = EditExportFolderPanel ( dlg , export_folder )
dlg . SetPanel ( panel )
2019-11-14 03:56:30 +00:00
if dlg . exec ( ) == QW . QDialog . Accepted :
2018-11-14 23:10:55 +00:00
edited_export_folder = panel . GetValue ( )
self . _export_folders . DeleteDatas ( ( export_folder , ) )
edited_export_folder . SetNonDupeName ( self . _GetExistingNames ( ) )
self . _export_folders . AddDatas ( ( edited_export_folder , ) )
else :
return
2018-11-28 22:31:04 +00:00
def _GetExistingNames ( self ) :
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
existing_names = { export_folder . GetName ( ) for export_folder in self . _export_folders . GetData ( ) }
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
return existing_names
2018-11-14 23:10:55 +00:00
def GetValue ( self ) :
export_folders = self . _export_folders . GetData ( )
return export_folders
class EditExportFolderPanel ( ClientGUIScrolledPanels . EditPanel ) :
def __init__ ( self , parent , export_folder ) :
ClientGUIScrolledPanels . EditPanel . __init__ ( self , parent )
self . _export_folder = export_folder
2019-05-01 21:24:42 +00:00
( name , path , export_type , delete_from_client_after_export , file_search_context , run_regularly , period , phrase , self . _last_checked , paused , run_now ) = self . _export_folder . ToTuple ( )
2018-11-14 23:10:55 +00:00
self . _path_box = ClientGUICommon . StaticBox ( self , ' name and location ' )
2019-11-14 03:56:30 +00:00
self . _name = QW . QLineEdit ( self . _path_box )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _path = QP . DirPickerCtrl ( self . _path_box )
2018-11-14 23:10:55 +00:00
#
self . _type_box = ClientGUICommon . StaticBox ( self , ' type of export ' )
self . _type = ClientGUICommon . BetterChoice ( self . _type_box )
2019-11-14 03:56:30 +00:00
self . _type . addItem ( ' regular ' , HC . EXPORT_FOLDER_TYPE_REGULAR )
self . _type . addItem ( ' synchronise ' , HC . EXPORT_FOLDER_TYPE_SYNCHRONISE )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _delete_from_client_after_export = QW . QCheckBox ( self . _type_box )
2018-11-28 22:31:04 +00:00
2018-11-14 23:10:55 +00:00
#
self . _query_box = ClientGUICommon . StaticBox ( self , ' query to export ' )
self . _page_key = ' export folders placeholder '
2020-03-18 21:35:57 +00:00
self . _tag_autocomplete = ClientGUIACDropdown . AutoCompleteDropdownTagsRead ( self . _query_box , self . _page_key , file_search_context , allow_all_known_files = False , force_system_everything = True )
2018-11-14 23:10:55 +00:00
#
self . _period_box = ClientGUICommon . StaticBox ( self , ' export period ' )
self . _period = ClientGUITime . TimeDeltaButton ( self . _period_box , min = 3 * 60 , days = True , hours = True , minutes = True )
2019-11-14 03:56:30 +00:00
self . _run_regularly = QW . QCheckBox ( self . _period_box )
2019-05-01 21:24:42 +00:00
2019-11-14 03:56:30 +00:00
self . _paused = QW . QCheckBox ( self . _period_box )
2019-05-01 21:24:42 +00:00
2019-11-14 03:56:30 +00:00
self . _run_now = QW . QCheckBox ( self . _period_box )
2019-05-01 21:24:42 +00:00
2018-11-14 23:10:55 +00:00
#
self . _phrase_box = ClientGUICommon . StaticBox ( self , ' filenames ' )
2019-11-14 03:56:30 +00:00
self . _pattern = QW . QLineEdit ( self . _phrase_box )
2018-11-14 23:10:55 +00:00
self . _examples = ClientGUICommon . ExportPatternButton ( self . _phrase_box )
#
2019-11-14 03:56:30 +00:00
self . _name . setText ( name )
2018-11-14 23:10:55 +00:00
self . _path . SetPath ( path )
2019-07-24 21:39:02 +00:00
self . _type . SetValue ( export_type )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _delete_from_client_after_export . setChecked ( delete_from_client_after_export )
2018-11-28 22:31:04 +00:00
2018-11-14 23:10:55 +00:00
self . _period . SetValue ( period )
2019-11-14 03:56:30 +00:00
self . _run_regularly . setChecked ( run_regularly )
2019-05-01 21:24:42 +00:00
2019-11-14 03:56:30 +00:00
self . _paused . setChecked ( paused )
2019-05-01 21:24:42 +00:00
2019-11-14 03:56:30 +00:00
self . _run_now . setChecked ( run_now )
2019-05-01 21:24:42 +00:00
2019-11-14 03:56:30 +00:00
self . _pattern . setText ( phrase )
2018-11-14 23:10:55 +00:00
#
rows = [ ]
rows . append ( ( ' name: ' , self . _name ) )
rows . append ( ( ' folder path: ' , self . _path ) )
gridbox = ClientGUICommon . WrapInGrid ( self . _path_box , rows )
self . _path_box . Add ( gridbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
#
text = ''' regular - try to export the files to the directory, overwriting if the filesize if different
synchronise - try to export the files to the directory , overwriting if the filesize if different , and delete anything else in the directory
If you select synchronise , be careful ! '''
st = ClientGUICommon . BetterStaticText ( self . _type_box , label = text )
2019-11-20 23:10:46 +00:00
st . setWordWrap ( True )
2018-11-14 23:10:55 +00:00
self . _type_box . Add ( st , CC . FLAGS_EXPAND_PERPENDICULAR )
self . _type_box . Add ( self . _type , CC . FLAGS_EXPAND_PERPENDICULAR )
2018-11-28 22:31:04 +00:00
rows = [ ]
rows . append ( ( ' delete files from client after export: ' , self . _delete_from_client_after_export ) )
gridbox = ClientGUICommon . WrapInGrid ( self . _type_box , rows )
self . _type_box . Add ( gridbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
2020-03-11 21:52:11 +00:00
self . _query_box . Add ( self . _tag_autocomplete )
2018-11-14 23:10:55 +00:00
self . _period_box . Add ( self . _period , CC . FLAGS_EXPAND_PERPENDICULAR )
2019-05-01 21:24:42 +00:00
rows = [ ]
rows . append ( ( ' run regularly?: ' , self . _run_regularly ) )
rows . append ( ( ' paused: ' , self . _paused ) )
rows . append ( ( ' run on dialog ok: ' , self . _run_now ) )
gridbox = ClientGUICommon . WrapInGrid ( self . _period_box , rows )
self . _period_box . Add ( gridbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
2019-11-14 03:56:30 +00:00
phrase_hbox = QP . HBoxLayout ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( phrase_hbox , self . _pattern , CC . FLAGS_EXPAND_BOTH_WAYS )
QP . AddToLayout ( phrase_hbox , self . _examples , CC . FLAGS_VCENTER )
2018-11-14 23:10:55 +00:00
self . _phrase_box . Add ( phrase_hbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
2019-11-14 03:56:30 +00:00
vbox = QP . VBoxLayout ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( vbox , self . _path_box , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( vbox , self . _type_box , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( vbox , self . _query_box , CC . FLAGS_EXPAND_BOTH_WAYS )
QP . AddToLayout ( vbox , self . _period_box , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( vbox , self . _phrase_box , CC . FLAGS_EXPAND_PERPENDICULAR )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . widget ( ) . setLayout ( vbox )
2018-11-14 23:10:55 +00:00
2018-11-28 22:31:04 +00:00
self . _UpdateTypeDeleteUI ( )
2019-11-14 03:56:30 +00:00
self . _type . currentIndexChanged . connect ( self . _UpdateTypeDeleteUI )
self . _delete_from_client_after_export . clicked . connect ( self . EventDeleteFilesAfterExport )
2018-11-28 22:31:04 +00:00
def _UpdateTypeDeleteUI ( self ) :
2019-07-24 21:39:02 +00:00
if self . _type . GetValue ( ) == HC . EXPORT_FOLDER_TYPE_SYNCHRONISE :
2018-11-28 22:31:04 +00:00
2019-11-14 03:56:30 +00:00
self . _delete_from_client_after_export . setEnabled ( False )
2018-11-28 22:31:04 +00:00
2019-11-14 03:56:30 +00:00
if self . _delete_from_client_after_export . isChecked ( ) :
2018-11-28 22:31:04 +00:00
2019-11-14 03:56:30 +00:00
self . _delete_from_client_after_export . setChecked ( False )
2018-11-28 22:31:04 +00:00
else :
2019-11-14 03:56:30 +00:00
self . _delete_from_client_after_export . setEnabled ( True )
2018-11-28 22:31:04 +00:00
def CanOK ( self ) :
2019-11-14 03:56:30 +00:00
if self . _delete_from_client_after_export . isChecked ( ) :
2018-11-28 22:31:04 +00:00
message = ' You have set this export folder to delete the files from the client after export! Are you absolutely sure this is what you want? '
2019-09-05 00:05:32 +00:00
result = ClientGUIDialogsQuick . GetYesNo ( self , message )
2019-11-14 03:56:30 +00:00
if result != QW . QDialog . Accepted :
2018-11-28 22:31:04 +00:00
2019-09-05 00:05:32 +00:00
return False
2018-11-28 22:31:04 +00:00
return True
2019-11-14 03:56:30 +00:00
def EventDeleteFilesAfterExport ( self ) :
2018-11-28 22:31:04 +00:00
2019-11-14 03:56:30 +00:00
if self . _delete_from_client_after_export . isChecked ( ) :
2018-11-28 22:31:04 +00:00
2019-11-14 03:56:30 +00:00
QW . QMessageBox . warning ( self , ' Warning ' , ' This will delete the exported files from your client after the export! If you do not know what this means, uncheck it! ' )
2018-11-28 22:31:04 +00:00
2018-11-14 23:10:55 +00:00
def GetValue ( self ) :
2019-11-14 03:56:30 +00:00
name = self . _name . text ( )
2018-11-14 23:10:55 +00:00
2019-01-09 22:59:03 +00:00
path = self . _path . GetPath ( )
2018-11-14 23:10:55 +00:00
2019-07-24 21:39:02 +00:00
export_type = self . _type . GetValue ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
delete_from_client_after_export = self . _delete_from_client_after_export . isChecked ( )
2018-11-28 22:31:04 +00:00
2020-03-11 21:52:11 +00:00
file_search_context = self . _tag_autocomplete . GetFileSearchContext ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
run_regularly = self . _run_regularly . isChecked ( )
2019-05-01 21:24:42 +00:00
2018-11-14 23:10:55 +00:00
period = self . _period . GetValue ( )
2019-12-05 05:29:32 +00:00
2018-11-14 23:10:55 +00:00
if self . _path . GetPath ( ) in ( ' ' , None ) :
raise HydrusExceptions . VetoException ( ' You must enter a folder path to export to! ' )
2019-11-14 03:56:30 +00:00
phrase = self . _pattern . text ( )
2018-11-14 23:10:55 +00:00
try :
ClientExporting . ParseExportPhrase ( phrase )
except Exception as e :
2019-01-09 22:59:03 +00:00
raise HydrusExceptions . VetoException ( ' Could not parse that export phrase! ' + str ( e ) )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
run_now = self . _run_now . isChecked ( )
2019-05-01 21:24:42 +00:00
2019-11-14 03:56:30 +00:00
paused = self . _paused . isChecked ( )
2019-05-01 21:24:42 +00:00
export_folder = ClientExporting . ExportFolder ( name , path = path , export_type = export_type , delete_from_client_after_export = delete_from_client_after_export , file_search_context = file_search_context , run_regularly = run_regularly , period = period , phrase = phrase , last_checked = self . _last_checked , paused = paused , run_now = run_now )
2018-11-14 23:10:55 +00:00
return export_folder
class ReviewExportFilesPanel ( ClientGUIScrolledPanels . ReviewPanel ) :
def __init__ ( self , parent , flat_media , do_export_and_then_quit = False ) :
ClientGUIScrolledPanels . ReviewPanel . __init__ ( self , parent )
new_options = HG . client_controller . new_options
self . _media_to_paths = { }
self . _existing_filenames = set ( )
self . _last_phrase_used = ' '
self . _last_dir_used = ' '
2020-03-25 21:15:57 +00:00
self . _tags_box = ClientGUIListBoxes . StaticBoxSorterForListBoxTags ( self , ' files \' tags ' )
2018-11-14 23:10:55 +00:00
services_manager = HG . client_controller . services_manager
self . _neighbouring_txt_tag_service_keys = services_manager . FilterValidServiceKeys ( new_options . GetKeyList ( ' default_neighbouring_txt_tag_service_keys ' ) )
2020-03-25 21:15:57 +00:00
t = ClientGUIListBoxes . ListBoxTagsMedia ( self . _tags_box , ClientTags . TAG_DISPLAY_SIBLINGS_AND_PARENTS , include_counts = True )
2018-11-14 23:10:55 +00:00
self . _tags_box . SetTagsBox ( t )
2020-02-26 22:28:52 +00:00
self . _tags_box . setMinimumSize ( QC . QSize ( 220 , 300 ) )
2018-11-14 23:10:55 +00:00
2020-02-19 21:48:36 +00:00
columns = [ ( ' number ' , 8 ) , ( ' filetype ' , 20 ) , ( ' expected path ' , - 1 ) ]
2018-11-14 23:10:55 +00:00
self . _paths = ClientGUIListCtrl . BetterListCtrl ( self , ' export_files ' , 24 , 64 , columns , self . _ConvertDataToListCtrlTuples , use_simple_delete = True )
self . _paths . Sort ( 0 )
self . _export_path_box = ClientGUICommon . StaticBox ( self , ' export path ' )
2019-11-14 03:56:30 +00:00
self . _directory_picker = QP . DirPickerCtrl ( self . _export_path_box )
self . _directory_picker . dirPickerChanged . connect ( self . _RefreshPaths )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _open_location = QW . QPushButton ( ' open this location ' , self . _export_path_box )
self . _open_location . clicked . connect ( self . EventOpenLocation )
2018-11-14 23:10:55 +00:00
self . _filenames_box = ClientGUICommon . StaticBox ( self , ' filenames ' )
2019-11-14 03:56:30 +00:00
self . _pattern = QW . QLineEdit ( self . _filenames_box )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _update = QW . QPushButton ( ' update ' , self . _filenames_box )
self . _update . clicked . connect ( self . _RefreshPaths )
2018-11-14 23:10:55 +00:00
self . _examples = ClientGUICommon . ExportPatternButton ( self . _filenames_box )
2019-11-14 03:56:30 +00:00
self . _delete_files_after_export = QW . QCheckBox ( ' delete files from client after export? ' , self )
2020-02-26 22:28:52 +00:00
self . _delete_files_after_export . setObjectName ( ' HydrusWarning ' )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _export_symlinks = QW . QCheckBox ( ' EXPERIMENTAL: export symlinks ' , self )
2020-02-26 22:28:52 +00:00
self . _export_symlinks . setObjectName ( ' HydrusWarning ' )
2019-01-23 22:19:16 +00:00
2018-11-14 23:10:55 +00:00
text = ' This will export all the files \' tags, newline separated, into .txts beside the files themselves. '
2019-09-25 21:34:18 +00:00
self . _export_tag_txts_services_button = ClientGUICommon . BetterButton ( self , ' set .txt services ' , self . _SetTxtServices )
2019-11-14 03:56:30 +00:00
self . _export_tag_txts = QW . QCheckBox ( ' export tags to .txt files? ' , self )
self . _export_tag_txts . setToolTip ( text )
self . _export_tag_txts . clicked . connect ( self . EventExportTagTxtsChanged )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _export = QW . QPushButton ( ' export ' , self )
self . _export . clicked . connect ( self . _DoExport )
2018-11-14 23:10:55 +00:00
#
export_path = ClientExporting . GetExportPath ( )
self . _directory_picker . SetPath ( export_path )
phrase = new_options . GetString ( ' export_phrase ' )
2019-11-14 03:56:30 +00:00
self . _pattern . setText ( phrase )
2018-11-14 23:10:55 +00:00
if len ( self . _neighbouring_txt_tag_service_keys ) > 0 :
2019-11-14 03:56:30 +00:00
self . _export_tag_txts . setChecked ( True )
2018-11-14 23:10:55 +00:00
self . _paths . SetData ( list ( enumerate ( flat_media ) ) )
2019-11-14 03:56:30 +00:00
self . _delete_files_after_export . setChecked ( HG . client_controller . new_options . GetBoolean ( ' delete_files_after_export ' ) )
self . _delete_files_after_export . clicked . connect ( self . EventDeleteFilesChanged )
2018-11-14 23:10:55 +00:00
2019-01-23 22:19:16 +00:00
if not HG . client_controller . new_options . GetBoolean ( ' advanced_mode ' ) :
2019-11-14 03:56:30 +00:00
self . _export_symlinks . setVisible ( False )
2019-01-23 22:19:16 +00:00
2018-11-14 23:10:55 +00:00
#
2019-11-14 03:56:30 +00:00
top_hbox = QP . HBoxLayout ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( top_hbox , self . _tags_box , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( top_hbox , self . _paths , CC . FLAGS_EXPAND_BOTH_WAYS )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
hbox = QP . HBoxLayout ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( hbox , self . _directory_picker , CC . FLAGS_EXPAND_BOTH_WAYS )
QP . AddToLayout ( hbox , self . _open_location , CC . FLAGS_VCENTER )
2018-11-14 23:10:55 +00:00
self . _export_path_box . Add ( hbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
2019-11-14 03:56:30 +00:00
hbox = QP . HBoxLayout ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( hbox , self . _pattern , CC . FLAGS_EXPAND_BOTH_WAYS )
QP . AddToLayout ( hbox , self . _update , CC . FLAGS_VCENTER )
QP . AddToLayout ( hbox , self . _examples , CC . FLAGS_VCENTER )
2018-11-14 23:10:55 +00:00
self . _filenames_box . Add ( hbox , CC . FLAGS_EXPAND_SIZER_PERPENDICULAR )
2019-11-14 03:56:30 +00:00
txt_hbox = QP . HBoxLayout ( )
2019-09-25 21:34:18 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( txt_hbox , self . _export_tag_txts_services_button , CC . FLAGS_VCENTER )
QP . AddToLayout ( txt_hbox , self . _export_tag_txts , CC . FLAGS_VCENTER )
2019-09-25 21:34:18 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP . VBoxLayout ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . AddToLayout ( vbox , top_hbox , CC . FLAGS_EXPAND_SIZER_BOTH_WAYS )
QP . AddToLayout ( vbox , self . _export_path_box , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( vbox , self . _filenames_box , CC . FLAGS_EXPAND_PERPENDICULAR )
QP . AddToLayout ( vbox , self . _delete_files_after_export , CC . FLAGS_LONE_BUTTON )
QP . AddToLayout ( vbox , self . _export_symlinks , CC . FLAGS_LONE_BUTTON )
QP . AddToLayout ( vbox , txt_hbox , CC . FLAGS_LONE_BUTTON )
QP . AddToLayout ( vbox , self . _export , CC . FLAGS_LONE_BUTTON )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . widget ( ) . setLayout ( vbox )
2018-11-14 23:10:55 +00:00
self . _RefreshTags ( )
2019-09-25 21:34:18 +00:00
self . _UpdateTxtButton ( )
2020-03-11 21:52:11 +00:00
HG . client_controller . CallAfterQtSafe ( self . _export , self . _export . setFocus , QC . Qt . OtherFocusReason )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . _paths . itemSelectionChanged . connect ( self . _RefreshTags )
2018-11-14 23:10:55 +00:00
if do_export_and_then_quit :
2019-11-14 03:56:30 +00:00
QP . CallAfter ( self . _DoExport , True )
2018-11-14 23:10:55 +00:00
def _ConvertDataToListCtrlTuples ( self , data ) :
2019-04-10 22:50:53 +00:00
directory = self . _directory_picker . GetPath ( )
2018-11-14 23:10:55 +00:00
( ordering_index , media ) = data
number = ordering_index
mime = media . GetMime ( )
try :
path = self . _GetPath ( media )
except Exception as e :
2019-01-09 22:59:03 +00:00
path = str ( e )
2018-11-14 23:10:55 +00:00
pretty_number = HydrusData . ToHumanInt ( ordering_index + 1 )
pretty_mime = HC . mime_string_lookup [ mime ]
2019-04-10 22:50:53 +00:00
2018-11-14 23:10:55 +00:00
pretty_path = path
2019-04-10 22:50:53 +00:00
if not path . startswith ( directory ) :
pretty_path = ' INVALID, above destination directory: ' + path
2018-11-14 23:10:55 +00:00
display_tuple = ( pretty_number , pretty_mime , pretty_path )
sort_tuple = ( number , pretty_mime , path )
return ( display_tuple , sort_tuple )
def _DoExport ( self , quit_afterwards = False ) :
2019-11-14 03:56:30 +00:00
delete_afterwards = self . _delete_files_after_export . isChecked ( )
export_symlinks = self . _export_symlinks . isChecked ( ) and not delete_afterwards
2018-11-14 23:10:55 +00:00
if quit_afterwards :
message = ' Export as shown? '
if delete_afterwards :
message + = os . linesep * 2
message + = ' THE FILES WILL BE DELETED FROM THE CLIENT AFTERWARDS '
2019-09-05 00:05:32 +00:00
result = ClientGUIDialogsQuick . GetYesNo ( self , message )
2019-11-14 03:56:30 +00:00
if result != QW . QDialog . Accepted :
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self . parentWidget ( ) . close ( )
2019-09-05 00:05:32 +00:00
return
2018-11-14 23:10:55 +00:00
elif delete_afterwards :
message = ' THE FILES WILL BE DELETED FROM THE CLIENT AFTERWARDS '
2019-09-05 00:05:32 +00:00
result = ClientGUIDialogsQuick . GetYesNo ( self , message )
2019-11-14 03:56:30 +00:00
if result != QW . QDialog . Accepted :
2018-11-14 23:10:55 +00:00
2019-09-05 00:05:32 +00:00
return
2018-11-14 23:10:55 +00:00
self . _RefreshPaths ( )
2019-11-14 03:56:30 +00:00
export_tag_txts = self . _export_tag_txts . isChecked ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
if self . _export_tag_txts . isChecked ( ) :
2019-09-25 21:34:18 +00:00
neighbouring_txt_tag_service_keys = self . _neighbouring_txt_tag_service_keys
else :
neighbouring_txt_tag_service_keys = [ ]
2018-11-14 23:10:55 +00:00
directory = self . _directory_picker . GetPath ( )
HydrusPaths . MakeSureDirectoryExists ( directory )
2019-11-14 03:56:30 +00:00
pattern = self . _pattern . text ( )
2018-11-14 23:10:55 +00:00
2019-09-25 21:34:18 +00:00
HG . client_controller . new_options . SetString ( ' export_phrase ' , pattern )
2018-11-14 23:10:55 +00:00
try :
terms = ClientExporting . ParseExportPhrase ( pattern )
except Exception as e :
2019-11-14 03:56:30 +00:00
QW . QMessageBox . critical ( self , ' Error ' , str ( e ) )
2018-11-14 23:10:55 +00:00
return
client_files_manager = HG . client_controller . client_files_manager
2019-11-14 03:56:30 +00:00
self . _export . setEnabled ( False )
2018-11-14 23:10:55 +00:00
to_do = self . _paths . GetData ( )
num_to_do = len ( to_do )
2019-11-14 03:56:30 +00:00
def qt_update_label ( text ) :
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
if not QP . isValid ( self ) or not QP . isValid ( self . _export ) or not self . _export :
2018-11-14 23:10:55 +00:00
return
2019-11-14 03:56:30 +00:00
self . _export . setText ( text )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
def qt_done ( quit_afterwards ) :
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
if not QP . isValid ( self ) or not QP . isValid ( self . _export ) or not self . _export :
2018-11-14 23:10:55 +00:00
return
2019-11-14 03:56:30 +00:00
self . _export . setEnabled ( True )
2018-11-14 23:10:55 +00:00
if quit_afterwards :
2019-11-14 03:56:30 +00:00
QP . CallAfter ( self . parentWidget ( ) . close ( ) )
2018-11-14 23:10:55 +00:00
2019-04-10 22:50:53 +00:00
def do_it ( directory , neighbouring_txt_tag_service_keys , delete_afterwards , export_symlinks , quit_afterwards ) :
pauser = HydrusData . BigJobPauser ( )
2018-11-14 23:10:55 +00:00
for ( index , ( ordering_index , media ) ) in enumerate ( to_do ) :
try :
2019-11-14 03:56:30 +00:00
QP . CallAfter ( qt_update_label , HydrusData . ConvertValueRangeToPrettyString ( index + 1 , num_to_do ) )
2018-11-14 23:10:55 +00:00
hash = media . GetHash ( )
mime = media . GetMime ( )
path = self . _GetPath ( media )
2019-04-10 22:50:53 +00:00
path = os . path . normpath ( path )
if not path . startswith ( directory ) :
raise Exception ( ' It seems a destination path was above the main export directory! The file was " {} " and its destination path was " {} " . ' . format ( hash . hex ( ) , path ) )
2018-11-14 23:10:55 +00:00
path_dir = os . path . dirname ( path )
HydrusPaths . MakeSureDirectoryExists ( path_dir )
if export_tag_txts :
tags_manager = media . GetTagsManager ( )
tags = set ( )
for service_key in neighbouring_txt_tag_service_keys :
2019-10-02 23:38:59 +00:00
current_tags = tags_manager . GetCurrent ( service_key , ClientTags . TAG_DISPLAY_SIBLINGS_AND_PARENTS )
2018-11-14 23:10:55 +00:00
tags . update ( current_tags )
tags = list ( tags )
tags . sort ( )
txt_path = path + ' .txt '
2019-01-16 22:40:53 +00:00
with open ( txt_path , ' w ' , encoding = ' utf-8 ' ) as f :
2018-11-14 23:10:55 +00:00
2019-01-09 22:59:03 +00:00
f . write ( os . linesep . join ( tags ) )
2018-11-14 23:10:55 +00:00
source_path = client_files_manager . GetFilePath ( hash , mime , check_file_exists = False )
2019-01-23 22:19:16 +00:00
if export_symlinks :
os . symlink ( source_path , path )
else :
HydrusPaths . MirrorFile ( source_path , path )
2019-04-03 22:45:57 +00:00
HydrusPaths . MakeFileWritable ( path )
2019-01-23 22:19:16 +00:00
2018-11-14 23:10:55 +00:00
except :
2019-11-14 03:56:30 +00:00
QP . CallAfter ( QW . QMessageBox . information , self , ' Information ' , ' Encountered a problem while attempting to export file with index ' + str ( ordering_index + 1 ) + ' : ' + os . linesep * 2 + traceback . format_exc ( ) )
2018-11-14 23:10:55 +00:00
break
2019-04-10 22:50:53 +00:00
pauser . Pause ( )
2018-11-14 23:10:55 +00:00
if delete_afterwards :
2019-11-14 03:56:30 +00:00
QP . CallAfter ( qt_update_label , ' deleting ' )
2018-11-14 23:10:55 +00:00
deletee_hashes = { media . GetHash ( ) for ( ordering_index , media ) in to_do }
chunks_of_hashes = HydrusData . SplitListIntoChunks ( deletee_hashes , 64 )
2019-04-10 22:50:53 +00:00
reason = ' Deleted after manual export to " {} " . ' . format ( directory )
content_updates = [ HydrusData . ContentUpdate ( HC . CONTENT_TYPE_FILES , HC . CONTENT_UPDATE_DELETE , chunk_of_hashes , reason = reason ) for chunk_of_hashes in chunks_of_hashes ]
2018-11-14 23:10:55 +00:00
for content_update in content_updates :
HG . client_controller . WriteSynchronous ( ' content_updates ' , { CC . LOCAL_FILE_SERVICE_KEY : [ content_update ] } )
2019-11-14 03:56:30 +00:00
QP . CallAfter ( qt_update_label , ' done! ' )
2018-11-14 23:10:55 +00:00
time . sleep ( 1 )
2019-11-14 03:56:30 +00:00
QP . CallAfter ( qt_update_label , ' export ' )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP . CallAfter ( qt_done , quit_afterwards )
2018-11-14 23:10:55 +00:00
2019-09-25 21:34:18 +00:00
HG . client_controller . CallToThread ( do_it , directory , neighbouring_txt_tag_service_keys , delete_afterwards , export_symlinks , quit_afterwards )
2018-11-14 23:10:55 +00:00
def _GetPath ( self , media ) :
if media in self . _media_to_paths :
return self . _media_to_paths [ media ]
2019-01-09 22:59:03 +00:00
directory = self . _directory_picker . GetPath ( )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
pattern = self . _pattern . text ( )
2018-11-14 23:10:55 +00:00
terms = ClientExporting . ParseExportPhrase ( pattern )
filename = ClientExporting . GenerateExportFilename ( directory , media , terms )
i = 1
while filename in self . _existing_filenames :
2019-09-05 00:05:32 +00:00
filename = ClientExporting . GenerateExportFilename ( directory , media , terms , append_number = i )
2018-11-14 23:10:55 +00:00
i + = 1
path = os . path . join ( directory , filename )
2019-04-10 22:50:53 +00:00
path = os . path . normpath ( path )
2018-11-14 23:10:55 +00:00
self . _existing_filenames . add ( filename )
self . _media_to_paths [ media ] = path
return path
def _RefreshPaths ( self ) :
2019-11-14 03:56:30 +00:00
pattern = self . _pattern . text ( )
2018-11-14 23:10:55 +00:00
dir_path = self . _directory_picker . GetPath ( )
if pattern == self . _last_phrase_used and dir_path == self . _last_dir_used :
return
self . _last_phrase_used = pattern
self . _last_dir_used = dir_path
HG . client_controller . new_options . SetString ( ' export_phrase ' , pattern )
self . _existing_filenames = set ( )
self . _media_to_paths = { }
self . _paths . UpdateDatas ( )
def _RefreshTags ( self ) :
data = self . _paths . GetData ( only_selected = True )
if len ( data ) == 0 :
data = self . _paths . GetData ( )
all_media = [ media for ( ordering_index , media ) in data ]
self . _tags_box . SetTagsByMedia ( all_media )
2019-09-25 21:34:18 +00:00
def _SetTxtServices ( self ) :
services_manager = HG . client_controller . services_manager
2020-03-11 21:52:11 +00:00
tag_services = services_manager . GetServices ( HC . REAL_TAG_SERVICES )
2019-09-25 21:34:18 +00:00
choice_tuples = [ ( service . GetName ( ) , service . GetServiceKey ( ) , service . GetServiceKey ( ) in self . _neighbouring_txt_tag_service_keys ) for service in tag_services ]
with ClientGUITopLevelWindows . DialogEdit ( self , ' select tag services ' ) as dlg :
panel = ClientGUIScrolledPanelsEdit . EditChooseMultiple ( dlg , choice_tuples )
dlg . SetPanel ( panel )
2019-11-14 03:56:30 +00:00
if dlg . exec ( ) == QW . QDialog . Accepted :
2019-09-25 21:34:18 +00:00
self . _neighbouring_txt_tag_service_keys = panel . GetValue ( )
HG . client_controller . new_options . SetKeyList ( ' default_neighbouring_txt_tag_service_keys ' , self . _neighbouring_txt_tag_service_keys )
if len ( self . _neighbouring_txt_tag_service_keys ) == 0 :
2019-11-14 03:56:30 +00:00
self . _export_tag_txts . setChecked ( False )
2019-09-25 21:34:18 +00:00
self . _UpdateTxtButton ( )
def _UpdateTxtButton ( self ) :
2019-11-14 03:56:30 +00:00
if self . _export_tag_txts . isChecked ( ) :
2019-09-25 21:34:18 +00:00
2019-11-14 03:56:30 +00:00
self . _export_tag_txts_services_button . setEnabled ( True )
2019-09-25 21:34:18 +00:00
else :
2019-11-14 03:56:30 +00:00
self . _export_tag_txts_services_button . setEnabled ( False )
2019-09-25 21:34:18 +00:00
if len ( self . _neighbouring_txt_tag_service_keys ) == 0 :
tt = ' No services set. '
else :
names = [ HG . client_controller . services_manager . GetName ( service_key ) for service_key in self . _neighbouring_txt_tag_service_keys ]
tt = ' , ' . join ( names )
2019-11-14 03:56:30 +00:00
self . _export_tag_txts_services_button . setToolTip ( tt )
2019-09-25 21:34:18 +00:00
2018-11-14 23:10:55 +00:00
def EventExport ( self , event ) :
self . _DoExport ( )
2019-11-14 03:56:30 +00:00
def EventDeleteFilesChanged ( self ) :
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
value = self . _delete_files_after_export . isChecked ( )
2019-01-23 22:19:16 +00:00
HG . client_controller . new_options . SetBoolean ( ' delete_files_after_export ' , value )
if value :
2019-11-14 03:56:30 +00:00
self . _export_symlinks . setChecked ( False )
2019-01-23 22:19:16 +00:00
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
def EventExportTagTxtsChanged ( self ) :
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
turning_on = self . _export_tag_txts . isChecked ( )
2019-09-25 21:34:18 +00:00
self . _UpdateTxtButton ( )
if turning_on :
2018-11-14 23:10:55 +00:00
2019-09-25 21:34:18 +00:00
self . _SetTxtServices ( )
2018-11-14 23:10:55 +00:00
else :
2019-09-25 21:34:18 +00:00
HG . client_controller . new_options . SetKeyList ( ' default_neighbouring_txt_tag_service_keys ' , [ ] )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
def EventOpenLocation ( self ) :
2018-11-14 23:10:55 +00:00
directory = self . _directory_picker . GetPath ( )
if directory is not None and directory != ' ' :
HydrusPaths . LaunchDirectory ( directory )