def _write_entry_on_external_device(metadata, file_path, ready_callback=None): """Create and update an entry copied from the DS to an external storage device. Besides copying the associated file a file for the preview and one for the metadata are stored in the hidden directory .Sugar-Metadata. This function handles renames of an entry on the external device and avoids name collisions. Renames are handled failsafe. """ def _ready_cb(): if ready_callback: ready_callback(metadata, file_path, destination_path) def _splice_cb(*args): created.send(None, object_id=destination_path) _ready_cb() if 'uid' in metadata and os.path.exists(metadata['uid']): file_path = metadata['uid'] if not file_path or not os.path.exists(file_path): raise ValueError('Entries without a file cannot be copied to ' 'removable devices') if not metadata.get('title'): metadata['title'] = _('Untitled') file_name = get_file_name(metadata['title'], metadata['mime_type']) destination_path = os.path.join(metadata['mountpoint'], file_name) if destination_path != file_path: file_name = get_unique_file_name(metadata['mountpoint'], file_name) destination_path = os.path.join(metadata['mountpoint'], file_name) clean_name, extension_ = os.path.splitext(file_name) metadata['title'] = clean_name metadata_copy = metadata.copy() metadata_copy.pop('mountpoint', None) metadata_copy.pop('uid', None) metadata_copy.pop('filesize', None) metadata_dir_path = os.path.join(metadata['mountpoint'], JOURNAL_METADATA_DIR) if not os.path.exists(metadata_dir_path): os.mkdir(metadata_dir_path) # Set the HIDDEN attrib even when the metadata directory already # exists for backward compatibility; but don't set it in ~/Documents if not metadata['mountpoint'] == get_documents_path(): if not SugarExt.fat_set_hidden_attrib(metadata_dir_path): logging.error('Could not set hidden attribute on %s' % (metadata_dir_path)) preview = None if 'preview' in metadata_copy: preview = metadata_copy['preview'] preview_fname = file_name + '.preview' metadata_copy.pop('preview', None) try: metadata_json = json.dumps(metadata_copy) except (UnicodeDecodeError, EnvironmentError): logging.error('Could not convert metadata to json.') else: (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) os.write(fh, metadata_json) os.close(fh) os.rename(fn, os.path.join(metadata_dir_path, file_name + '.metadata')) if preview: (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) os.write(fh, preview) os.close(fh) os.rename(fn, os.path.join(metadata_dir_path, preview_fname)) if not os.path.dirname(destination_path) == os.path.dirname(file_path): input_stream = Gio.File.new_for_path(file_path).read(None) output_stream = Gio.File.new_for_path(destination_path)\ .append_to(Gio.FileCreateFlags.PRIVATE | Gio.FileCreateFlags.REPLACE_DESTINATION, None) # TODO: use Gio.File.copy_async, when implemented output_stream.splice_async( input_stream, Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_LOW, None, _splice_cb, None) else: _rename_entry_on_external_device(file_path, destination_path, metadata_dir_path) _ready_cb()
def _write_entry_on_external_device(metadata, file_path): """Create and update an entry copied from the DS to an external storage device. Besides copying the associated file a file for the preview and one for the metadata are stored in the hidden directory .Sugar-Metadata. This function handles renames of an entry on the external device and avoids name collisions. Renames are handled failsafe. """ if 'uid' in metadata and os.path.exists(metadata['uid']): file_path = metadata['uid'] if not file_path or not os.path.exists(file_path): raise ValueError('Entries without a file cannot be copied to ' 'removable devices') if not metadata.get('title'): metadata['title'] = _('Untitled') file_name = get_file_name(metadata['title'], metadata['mime_type']) destination_path = os.path.join(metadata['mountpoint'], file_name) if destination_path != file_path: file_name = get_unique_file_name(metadata['mountpoint'], file_name) destination_path = os.path.join(metadata['mountpoint'], file_name) clean_name, extension_ = os.path.splitext(file_name) metadata['title'] = clean_name metadata_copy = metadata.copy() metadata_copy.pop('mountpoint', None) metadata_copy.pop('uid', None) metadata_copy.pop('filesize', None) metadata_dir_path = os.path.join(metadata['mountpoint'], JOURNAL_METADATA_DIR) if not os.path.exists(metadata_dir_path): os.mkdir(metadata_dir_path) # Set the HIDDEN attrib even when the metadata directory already # exists for backward compatibility. if not SugarExt.fat_set_hidden_attrib(metadata_dir_path): logging.error('Could not set hidden attribute on %s' % (metadata_dir_path)) preview = None if 'preview' in metadata_copy: preview = metadata_copy['preview'] preview_fname = file_name + '.preview' metadata_copy.pop('preview', None) try: metadata_json = json.dumps(metadata_copy) except (UnicodeDecodeError, EnvironmentError): logging.error('Could not convert metadata to json.') else: (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) os.write(fh, metadata_json) os.close(fh) os.rename(fn, os.path.join(metadata_dir_path, file_name + '.metadata')) if preview: (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) os.write(fh, preview) os.close(fh) os.rename(fn, os.path.join(metadata_dir_path, preview_fname)) if not os.path.dirname(destination_path) == os.path.dirname(file_path): shutil.copy(file_path, destination_path) else: _rename_entry_on_external_device(file_path, destination_path, metadata_dir_path) object_id = destination_path created.send(None, object_id=object_id) return object_id
def _write_entry_on_external_device(metadata, file_path, ready_callback=None): """Create and update an entry copied from the DS to an external storage device. Besides copying the associated file a file for the preview and one for the metadata are stored in the hidden directory .Sugar-Metadata. This function handles renames of an entry on the external device and avoids name collisions. Renames are handled failsafe. """ def _ready_cb(): if ready_callback: ready_callback(metadata, file_path, destination_path) def _updated_cb(*args): updated.send(None, object_id=destination_path) _ready_cb() def _splice_cb(*args): created.send(None, object_id=destination_path) _ready_cb() if 'uid' in metadata and os.path.exists(metadata['uid']): file_path = metadata['uid'] if not file_path or not os.path.exists(file_path): raise ValueError('Entries without a file cannot be copied to ' 'removable devices') if not metadata.get('title'): metadata['title'] = _('Untitled') original_file_name = os.path.basename(file_path) original_dir_name = os.path.dirname(file_path) # if is a file in the exernal device or Documents # and the title is equal to the file name don't change it if original_file_name == metadata['title'] and \ original_dir_name.startswith(metadata['mountpoint']): destination_path = file_path file_name = original_file_name else: file_name = metadata['title'] # only change the extension if the title don't have a good extension clean_name, extension = os.path.splitext(file_name) extension = extension.replace('.', '').lower() mime_extensions = mime.get_extensions_by_mimetype( metadata['mime_type']) if extension not in mime_extensions: file_name = get_file_name(metadata['title'], metadata['mime_type']) destination_path = os.path.join(metadata['mountpoint'], file_name) if destination_path != file_path: file_name = get_unique_file_name(metadata['mountpoint'], file_name) destination_path = os.path.join(metadata['mountpoint'], file_name) metadata['title'] = file_name metadata_copy = metadata.copy() metadata_copy.pop('mountpoint', None) metadata_copy.pop('uid', None) metadata_copy.pop('filesize', None) metadata_dir_path = os.path.join(metadata['mountpoint'], JOURNAL_METADATA_DIR) # check if the file is in a subdirectory in Documents or device if original_dir_name.startswith(metadata['mountpoint']) and \ original_dir_name != metadata['mountpoint']: subdir = os.path.relpath(original_dir_name, metadata['mountpoint']) metadata_dir_path = os.path.join(metadata_dir_path, subdir) if not os.path.exists(metadata_dir_path): os.makedirs(metadata_dir_path) # Set the HIDDEN attrib even when the metadata directory already # exists for backward compatibility; but don't set it in ~/Documents if not metadata['mountpoint'] == get_documents_path(): if not SugarExt.fat_set_hidden_attrib(metadata_dir_path): logging.error('Could not set hidden attribute on %s' % (metadata_dir_path)) preview = None if 'preview' in metadata_copy: preview = metadata_copy['preview'] preview_fname = file_name + '.preview' metadata_copy.pop('preview', None) try: metadata_json = json.dumps(metadata_copy) except (UnicodeDecodeError, EnvironmentError): logging.error('Could not convert metadata to json.') else: (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) os.write(fh, metadata_json) os.close(fh) os.rename(fn, os.path.join(metadata_dir_path, file_name + '.metadata')) if preview: (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) os.write(fh, preview) os.close(fh) os.rename(fn, os.path.join(metadata_dir_path, preview_fname)) if not os.path.dirname(destination_path) == os.path.dirname(file_path): input_stream = Gio.File.new_for_path(file_path).read(None) output_stream = Gio.File.new_for_path(destination_path)\ .append_to(Gio.FileCreateFlags.PRIVATE | Gio.FileCreateFlags.REPLACE_DESTINATION, None) # TODO: use Gio.File.copy_async, when implemented output_stream.splice_async( input_stream, Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_LOW, None, _splice_cb, None) else: _rename_entry_on_external_device(file_path, destination_path, metadata_dir_path) _updated_cb()