def _GetPath(self, media): if media in self._media_to_paths: return self._media_to_paths[media] directory = self._directory_picker.GetPath() pattern = self._pattern.text() terms = ClientExporting.ParseExportPhrase(pattern) filename = ClientExporting.GenerateExportFilename( directory, media, terms, do_not_use_filenames=self._existing_filenames) path = os.path.join(directory, filename) path = os.path.normpath(path) self._existing_filenames.add(filename) self._media_to_paths[media] = path return path
def GetValue(self): name = self._name.text() path = self._path.GetPath() export_type = self._type.GetValue() delete_from_client_after_export = self._delete_from_client_after_export.isChecked( ) file_search_context = self._tag_autocomplete.GetFileSearchContext() run_regularly = self._run_regularly.isChecked() period = self._period.GetValue() if self._path.GetPath() in ('', None): raise HydrusExceptions.VetoException( 'You must enter a folder path to export to!') phrase = self._pattern.text() try: ClientExporting.ParseExportPhrase(phrase) except Exception as e: raise HydrusExceptions.VetoException( 'Could not parse that export phrase! ' + str(e)) run_now = self._run_now.isChecked() paused = self._paused.isChecked() last_error = self._export_folder.GetLastError() 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, last_error=last_error) return export_folder
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 delete_from_client_after_export = False file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY ) period = 15 * 60 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 ) with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit export folder' ) as dlg: panel = EditExportFolderPanel( dlg, export_folder ) dlg.SetPanel( panel ) if dlg.exec() == QW.QDialog.Accepted: export_folder = panel.GetValue() export_folder.SetNonDupeName( self._GetExistingNames() ) self._export_folders.AddDatas( ( export_folder, ) )
def DoFileExportDragDrop( window, page_key, media, alt_down ): drop_source = QG.QDrag( window ) data_object = QMimeDataHydrusFiles() # new_options = HG.client_controller.new_options do_secret_discord_dnd_fix = new_options.GetBoolean( 'secret_discord_dnd_fix' ) and alt_down # client_files_manager = HG.client_controller.client_files_manager original_paths = [] media_and_original_paths = [] total_size = 0 for m in media: hash = m.GetHash() mime = m.GetMime() total_size += m.GetSize() original_path = client_files_manager.GetFilePath( hash, mime, check_file_exists = False ) original_paths.append( original_path ) media_and_original_paths.append( ( m, original_path ) ) # discord_dnd_fix_possible = new_options.GetBoolean( 'discord_dnd_fix' ) and len( original_paths ) <= 50 and total_size < 200 * 1048576 temp_dir = HG.client_controller.temp_dir if do_secret_discord_dnd_fix: dnd_paths = original_paths flags = QC.Qt.MoveAction elif discord_dnd_fix_possible and os.path.exists( temp_dir ): fallback_filename_terms = ClientExporting.ParseExportPhrase( '{hash}' ) try: filename_pattern = new_options.GetString( 'discord_dnd_filename_pattern' ) filename_terms = ClientExporting.ParseExportPhrase( filename_pattern ) if len( filename_terms ) == 0: raise Exception() except: filename_terms = fallback_filename_terms dnd_paths = [] for ( m, original_path ) in media_and_original_paths: filename = ClientExporting.GenerateExportFilename( temp_dir, m, filename_terms ) if filename == HC.mime_ext_lookup[ m.GetMime() ]: filename = ClientExporting.GenerateExportFilename( temp_dir, m, fallback_filename_terms ) dnd_path = os.path.join( temp_dir, filename ) if not os.path.exists( dnd_path ): HydrusPaths.MirrorFile( original_path, dnd_path ) dnd_paths.append( dnd_path ) flags = QC.Qt.MoveAction | QC.Qt.CopyAction else: dnd_paths = original_paths flags = QC.Qt.CopyAction uri_list = [] for path in dnd_paths: uri_list.append( QC.QUrl.fromLocalFile( path ) ) data_object.setUrls( uri_list ) # hashes = [ m.GetHash() for m in media ] data_object.setHydrusFiles( page_key, hashes ) # old way of doing this that makes some external programs (discord) reject it ''' if page_key is None: encoded_page_key = None else: encoded_page_key = page_key.hex() data_obj = ( encoded_page_key, [ hash.hex() for hash in hashes ] ) data_str = json.dumps( data_obj ) data_bytes = bytes( data_str, 'utf-8' ) data_object.setData( 'application/hydrus-media', data_bytes ) ''' # drop_source.setMimeData( data_object ) result = drop_source.exec_( flags, QC.Qt.CopyAction ) return result
def _DoExport(self, quit_afterwards=False): delete_afterwards = self._delete_files_after_export.isChecked() export_symlinks = self._export_symlinks.isChecked( ) and not delete_afterwards if quit_afterwards: message = 'Export as shown?' if delete_afterwards: message += os.linesep * 2 message += 'THE FILES WILL BE DELETED FROM THE CLIENT AFTERWARDS' result = ClientGUIDialogsQuick.GetYesNo(self, message) if result != QW.QDialog.Accepted: self.parentWidget().close() return elif delete_afterwards: message = 'THE FILES WILL BE DELETED FROM THE CLIENT AFTERWARDS' result = ClientGUIDialogsQuick.GetYesNo(self, message) if result != QW.QDialog.Accepted: return self._RefreshPaths() export_tag_txts = self._export_tag_txts.isChecked() if self._export_tag_txts.isChecked(): neighbouring_txt_tag_service_keys = self._neighbouring_txt_tag_service_keys else: neighbouring_txt_tag_service_keys = [] directory = self._directory_picker.GetPath() HydrusPaths.MakeSureDirectoryExists(directory) pattern = self._pattern.text() HG.client_controller.new_options.SetString('export_phrase', pattern) try: terms = ClientExporting.ParseExportPhrase(pattern) except Exception as e: QW.QMessageBox.critical(self, 'Error', str(e)) return client_files_manager = HG.client_controller.client_files_manager self._export.setEnabled(False) to_do = self._paths.GetData() to_do = [(ordering_index, media, self._GetPath(media)) for (ordering_index, media) in to_do] num_to_do = len(to_do) def qt_update_label(text): if not QP.isValid(self) or not QP.isValid( self._export) or not self._export: return self._export.setText(text) def qt_done(quit_afterwards): if not QP.isValid(self) or not QP.isValid( self._export) or not self._export: return self._export.setEnabled(True) if quit_afterwards: QP.CallAfter(self.parentWidget().close) def do_it(directory, neighbouring_txt_tag_service_keys, delete_afterwards, export_symlinks, quit_afterwards): job_key = ClientThreading.JobKey(cancellable=True) job_key.SetStatusTitle('file export') HG.client_controller.pub('message', job_key) pauser = HydrusData.BigJobPauser() for (index, (ordering_index, media, path)) in enumerate(to_do): if job_key.IsCancelled(): break try: x_of_y = HydrusData.ConvertValueRangeToPrettyString( index + 1, num_to_do) job_key.SetVariable('popup_text_1', 'Done {}'.format(x_of_y)) job_key.SetVariable('popup_gauge_1', (index + 1, num_to_do)) QP.CallAfter(qt_update_label, x_of_y) hash = media.GetHash() mime = media.GetMime() 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)) 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: current_tags = tags_manager.GetCurrent( service_key, ClientTags.TAG_DISPLAY_ACTUAL) tags.update(current_tags) tags = sorted(tags) txt_path = path + '.txt' with open(txt_path, 'w', encoding='utf-8') as f: f.write(os.linesep.join(tags)) source_path = client_files_manager.GetFilePath( hash, mime, check_file_exists=False) if export_symlinks: os.symlink(source_path, path) else: HydrusPaths.MirrorFile(source_path, path) HydrusPaths.MakeFileWriteable(path) except: 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()) break pauser.Pause() if not job_key.IsCancelled() and delete_afterwards: QP.CallAfter(qt_update_label, 'deleting') delete_lock_for_archived_files = HG.client_controller.new_options.GetBoolean( 'delete_lock_for_archived_files') if delete_lock_for_archived_files: deletee_hashes = { media.GetHash() for (ordering_index, media, path) in to_do if not media.HasArchive() } else: deletee_hashes = { media.GetHash() for (ordering_index, media, path) in to_do } chunks_of_hashes = HydrusData.SplitListIntoChunks( deletee_hashes, 64) 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 ] for content_update in content_updates: HG.client_controller.WriteSynchronous( 'content_updates', {CC.LOCAL_FILE_SERVICE_KEY: [content_update]}) job_key.DeleteVariable('popup_gauge_1') job_key.SetVariable('popup_text_1', 'Done!') job_key.Finish() job_key.Delete(5) QP.CallAfter(qt_update_label, 'done!') time.sleep(1) QP.CallAfter(qt_update_label, 'export') QP.CallAfter(qt_done, quit_afterwards) HG.client_controller.CallToThread(do_it, directory, neighbouring_txt_tag_service_keys, delete_afterwards, export_symlinks, quit_afterwards)
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 = '' self._tags_box = ClientGUIListBoxes.StaticBoxSorterForListBoxTags( self, 'files\' tags') 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')) t = ClientGUIListBoxes.ListBoxTagsMedia(self._tags_box, ClientTags.TAG_DISPLAY_ACTUAL, include_counts=True) self._tags_box.SetTagsBox(t) self._tags_box.setMinimumSize(QC.QSize(220, 300)) self._paths = ClientGUIListCtrl.BetterListCtrl( self, CGLC.COLUMN_LIST_EXPORT_FILES.ID, 24, self._ConvertDataToListCtrlTuples, use_simple_delete=True) self._paths.Sort() self._export_path_box = ClientGUICommon.StaticBox(self, 'export path') self._directory_picker = QP.DirPickerCtrl(self._export_path_box) self._directory_picker.dirPickerChanged.connect(self._RefreshPaths) self._open_location = QW.QPushButton('open this location', self._export_path_box) self._open_location.clicked.connect(self.EventOpenLocation) self._filenames_box = ClientGUICommon.StaticBox(self, 'filenames') self._pattern = QW.QLineEdit(self._filenames_box) self._update = QW.QPushButton('update', self._filenames_box) self._update.clicked.connect(self._RefreshPaths) self._examples = ClientGUICommon.ExportPatternButton( self._filenames_box) self._delete_files_after_export = QW.QCheckBox( 'delete files from client after export?', self) self._delete_files_after_export.setObjectName('HydrusWarning') self._export_symlinks = QW.QCheckBox('EXPERIMENTAL: export symlinks', self) self._export_symlinks.setObjectName('HydrusWarning') text = 'This will export all the files\' tags, newline separated, into .txts beside the files themselves.' self._export_tag_txts_services_button = ClientGUICommon.BetterButton( self, 'set .txt services', self._SetTxtServices) 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) self._export = QW.QPushButton('export', self) self._export.clicked.connect(self._DoExport) # export_path = ClientExporting.GetExportPath() if export_path is not None: self._directory_picker.SetPath(export_path) phrase = new_options.GetString('export_phrase') self._pattern.setText(phrase) if len(self._neighbouring_txt_tag_service_keys) > 0: self._export_tag_txts.setChecked(True) self._paths.SetData(list(enumerate(flat_media))) 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) if not HG.client_controller.new_options.GetBoolean('advanced_mode'): self._export_symlinks.setVisible(False) # top_hbox = QP.HBoxLayout() QP.AddToLayout(top_hbox, self._tags_box, CC.FLAGS_EXPAND_PERPENDICULAR) QP.AddToLayout(top_hbox, self._paths, CC.FLAGS_EXPAND_BOTH_WAYS) hbox = QP.HBoxLayout() QP.AddToLayout(hbox, self._directory_picker, CC.FLAGS_EXPAND_BOTH_WAYS) QP.AddToLayout(hbox, self._open_location, CC.FLAGS_CENTER_PERPENDICULAR) self._export_path_box.Add(hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR) hbox = QP.HBoxLayout() QP.AddToLayout(hbox, self._pattern, CC.FLAGS_EXPAND_BOTH_WAYS) QP.AddToLayout(hbox, self._update, CC.FLAGS_CENTER_PERPENDICULAR) QP.AddToLayout(hbox, self._examples, CC.FLAGS_CENTER_PERPENDICULAR) self._filenames_box.Add(hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR) txt_hbox = QP.HBoxLayout() QP.AddToLayout(txt_hbox, self._export_tag_txts_services_button, CC.FLAGS_CENTER_PERPENDICULAR) QP.AddToLayout(txt_hbox, self._export_tag_txts, CC.FLAGS_CENTER_PERPENDICULAR) vbox = QP.VBoxLayout() 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_ON_RIGHT) QP.AddToLayout(vbox, self._export_symlinks, CC.FLAGS_ON_RIGHT) QP.AddToLayout(vbox, txt_hbox, CC.FLAGS_ON_RIGHT) QP.AddToLayout(vbox, self._export, CC.FLAGS_ON_RIGHT) self.widget().setLayout(vbox) self._RefreshTags() self._UpdateTxtButton() ClientGUIFunctions.SetFocusLater(self._export) self._paths.itemSelectionChanged.connect(self._RefreshTags) if do_export_and_then_quit: HG.client_controller.CallAfterQtSafe( self, 'doing export before dialog quit', self._DoExport, True)
def GetValue(self): sidecar_exporter = ClientExporting.SidecarExporter( service_keys_to_tag_data=self._service_keys_to_tag_data) return sidecar_exporter