Compare commits
6 commits
88c78f1aa7
...
189e15748a
| Author | SHA1 | Date | |
|---|---|---|---|
| 189e15748a | |||
| 892569a416 | |||
| cf1e35867c | |||
| 8d34d0aa9b | |||
| 545421ded4 | |||
| c30d584283 |
6 changed files with 63 additions and 10 deletions
45
voussoirkit/ffmpegtools.py
Normal file
45
voussoirkit/ffmpegtools.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import subprocess
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from voussoirkit import pathclass
|
||||
|
||||
def concatenate(input_files, output_file):
|
||||
'''
|
||||
Conveniently runs `ffmpeg -f concat` via subprocess.
|
||||
'''
|
||||
if len(input_files) < 2:
|
||||
raise ValueError('Only one input file.')
|
||||
|
||||
input_files = [pathclass.Path(f) for f in input_files]
|
||||
|
||||
for file in input_files:
|
||||
file.assert_is_file()
|
||||
|
||||
output_file = pathclass.Path(output_file)
|
||||
|
||||
names = [f.absolute_path for f in input_files]
|
||||
names = [name.replace("'", "'\\''") for name in names]
|
||||
cat_lines = [f'file \'{x}\'' for x in names]
|
||||
cat_text = '\n'.join(cat_lines)
|
||||
cat_file = tempfile.TemporaryFile('w', encoding='utf-8', delete=False)
|
||||
cat_file.write(cat_text)
|
||||
cat_file.close()
|
||||
|
||||
command = [
|
||||
'ffmpeg',
|
||||
'-f', 'concat',
|
||||
'-safe', '0',
|
||||
'-i', cat_file.name,
|
||||
'-map', '0:v?',
|
||||
'-map', '0:a?',
|
||||
'-map', '0:s?',
|
||||
'-c', 'copy',
|
||||
output_file.absolute_path,
|
||||
]
|
||||
|
||||
subprocess.check_call(command)
|
||||
|
||||
os.remove(cat_file.name)
|
||||
|
||||
return output_file
|
||||
|
|
@ -35,7 +35,11 @@ code will not be affected.
|
|||
'''
|
||||
import requests
|
||||
|
||||
class HTTP4XX(requests.exceptions.HTTPError): pass
|
||||
class HTTPError(requests.exceptions.HTTPError):
|
||||
def __str__(self):
|
||||
return type(self).__name__
|
||||
|
||||
class HTTP4XX(HTTPError): pass
|
||||
class HTTP400(HTTP4XX): pass
|
||||
class HTTP401(HTTP4XX): pass
|
||||
class HTTP402(HTTP4XX): pass
|
||||
|
|
@ -137,7 +141,7 @@ class HTTP497(HTTP4XX): pass
|
|||
class HTTP498(HTTP4XX): pass
|
||||
class HTTP499(HTTP4XX): pass
|
||||
|
||||
class HTTP5XX(requests.exceptions.HTTPError): pass
|
||||
class HTTP5XX(HTTPError): pass
|
||||
class HTTP500(HTTP5XX): pass
|
||||
class HTTP501(HTTP5XX): pass
|
||||
class HTTP502(HTTP5XX): pass
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ class Path:
|
|||
self._parts = path._parts
|
||||
self._absolute_path = path._absolute_path
|
||||
self._extension = path._extension
|
||||
self._case_correct = path._case_correct
|
||||
return
|
||||
|
||||
if isinstance(path, (tuple, list)):
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ def copy_directory(
|
|||
written_bytes = 0
|
||||
|
||||
for (source_file, destination_file) in walker:
|
||||
log.loud(f'Working on file {source_file.absolute_path}.')
|
||||
if not source_file.is_file:
|
||||
log.warning('%s disappeared during directory copy.', source_file.absolute_path)
|
||||
continue
|
||||
|
|
@ -482,6 +483,7 @@ def copy_file(
|
|||
)
|
||||
|
||||
if not should_overwrite:
|
||||
log.loud(f'Don\'t need to overwrite {destination.absolute_path}.')
|
||||
return results
|
||||
|
||||
if dry_run:
|
||||
|
|
@ -489,6 +491,7 @@ def copy_file(
|
|||
return results
|
||||
|
||||
if callback_pre_copy(source, destination, dry_run=dry_run) is BAIL:
|
||||
log.loud('Bailing due to callback_pre_copy.')
|
||||
return results
|
||||
|
||||
destination.parent.makedirs(exist_ok=True)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ log = vlogging.getLogger(__name__, 'windrives')
|
|||
|
||||
def get_all_volumes():
|
||||
'''
|
||||
Return a list of volume paths like \\?\Volume{GUID}\ for all volumes,
|
||||
Return a list of volume paths like \\\\?\\Volume{GUID}\\ for all volumes,
|
||||
whether they are mounted or not.
|
||||
|
||||
Note: This will include recovery / EFI partitions, which may not be what
|
||||
|
|
@ -55,9 +55,9 @@ def get_all_volumes():
|
|||
def get_drive_info(path):
|
||||
'''
|
||||
Given a drive path as either:
|
||||
- a letter like C or C:\, or
|
||||
- a letter like C or C:\\, or
|
||||
- a mount path, or
|
||||
- a volume path like \\?\Volume{GUID},
|
||||
- a volume path like \\\\?\\Volume{GUID},
|
||||
return a dictionary containing its attributes.
|
||||
|
||||
Thanks Nicholas Orlowski
|
||||
|
|
@ -132,7 +132,7 @@ def get_drive_map():
|
|||
def get_drive_mounts():
|
||||
'''
|
||||
Return a list of all connected drives as either:
|
||||
- a letter like C:\ if the volume has a letter, or
|
||||
- a letter like C:\\ if the volume has a letter, or
|
||||
- a mount path if the volume does not have a letter.
|
||||
'''
|
||||
mounts = []
|
||||
|
|
@ -157,8 +157,8 @@ def get_drive_mount_by_name(name):
|
|||
|
||||
def get_volume_mount(volume):
|
||||
'''
|
||||
Given a volume path like \\?\Volume{GUID}\, return either:
|
||||
- a letter like C:\ if the volume has a letter, or
|
||||
Given a volume path like \\\\?\\Volume{GUID}\\, return either:
|
||||
- a letter like C:\\ if the volume has a letter, or
|
||||
- a mount path if the volume does not have a letter, or
|
||||
- emptystring if the volume is not mounted at all.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ function register_hotkey(hotkey, action, description)
|
|||
}
|
||||
|
||||
const key = hotkey.pop();
|
||||
modifiers = hotkey.map(word => word.toLocaleLowerCase());
|
||||
const modifiers = hotkey.map(word => word.toLocaleLowerCase());
|
||||
const ctrlKey = modifiers.includes("control") || modifiers.includes("ctrl");
|
||||
const shiftKey = modifiers.includes("shift");
|
||||
const altKey = modifiers.includes("alt");
|
||||
|
|
@ -96,7 +96,7 @@ function hotkeys_listener(event)
|
|||
return;
|
||||
}
|
||||
|
||||
identifier = hotkeys.hotkey_identifier(event.key, event.ctrlKey, event.shiftKey, event.altKey);
|
||||
const identifier = hotkeys.hotkey_identifier(event.key, event.ctrlKey, event.shiftKey, event.altKey);
|
||||
//console.log(identifier);
|
||||
if (identifier in hotkeys.HOTKEYS)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue