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
|
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 HTTP400(HTTP4XX): pass
|
||||||
class HTTP401(HTTP4XX): pass
|
class HTTP401(HTTP4XX): pass
|
||||||
class HTTP402(HTTP4XX): pass
|
class HTTP402(HTTP4XX): pass
|
||||||
|
|
@ -137,7 +141,7 @@ class HTTP497(HTTP4XX): pass
|
||||||
class HTTP498(HTTP4XX): pass
|
class HTTP498(HTTP4XX): pass
|
||||||
class HTTP499(HTTP4XX): pass
|
class HTTP499(HTTP4XX): pass
|
||||||
|
|
||||||
class HTTP5XX(requests.exceptions.HTTPError): pass
|
class HTTP5XX(HTTPError): pass
|
||||||
class HTTP500(HTTP5XX): pass
|
class HTTP500(HTTP5XX): pass
|
||||||
class HTTP501(HTTP5XX): pass
|
class HTTP501(HTTP5XX): pass
|
||||||
class HTTP502(HTTP5XX): pass
|
class HTTP502(HTTP5XX): pass
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ class Path:
|
||||||
self._parts = path._parts
|
self._parts = path._parts
|
||||||
self._absolute_path = path._absolute_path
|
self._absolute_path = path._absolute_path
|
||||||
self._extension = path._extension
|
self._extension = path._extension
|
||||||
|
self._case_correct = path._case_correct
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(path, (tuple, list)):
|
if isinstance(path, (tuple, list)):
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,7 @@ def copy_directory(
|
||||||
written_bytes = 0
|
written_bytes = 0
|
||||||
|
|
||||||
for (source_file, destination_file) in walker:
|
for (source_file, destination_file) in walker:
|
||||||
|
log.loud(f'Working on file {source_file.absolute_path}.')
|
||||||
if not source_file.is_file:
|
if not source_file.is_file:
|
||||||
log.warning('%s disappeared during directory copy.', source_file.absolute_path)
|
log.warning('%s disappeared during directory copy.', source_file.absolute_path)
|
||||||
continue
|
continue
|
||||||
|
|
@ -482,6 +483,7 @@ def copy_file(
|
||||||
)
|
)
|
||||||
|
|
||||||
if not should_overwrite:
|
if not should_overwrite:
|
||||||
|
log.loud(f'Don\'t need to overwrite {destination.absolute_path}.')
|
||||||
return results
|
return results
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
|
|
@ -489,6 +491,7 @@ def copy_file(
|
||||||
return results
|
return results
|
||||||
|
|
||||||
if callback_pre_copy(source, destination, dry_run=dry_run) is BAIL:
|
if callback_pre_copy(source, destination, dry_run=dry_run) is BAIL:
|
||||||
|
log.loud('Bailing due to callback_pre_copy.')
|
||||||
return results
|
return results
|
||||||
|
|
||||||
destination.parent.makedirs(exist_ok=True)
|
destination.parent.makedirs(exist_ok=True)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ log = vlogging.getLogger(__name__, 'windrives')
|
||||||
|
|
||||||
def get_all_volumes():
|
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.
|
whether they are mounted or not.
|
||||||
|
|
||||||
Note: This will include recovery / EFI partitions, which may not be what
|
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):
|
def get_drive_info(path):
|
||||||
'''
|
'''
|
||||||
Given a drive path as either:
|
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 mount path, or
|
||||||
- a volume path like \\?\Volume{GUID},
|
- a volume path like \\\\?\\Volume{GUID},
|
||||||
return a dictionary containing its attributes.
|
return a dictionary containing its attributes.
|
||||||
|
|
||||||
Thanks Nicholas Orlowski
|
Thanks Nicholas Orlowski
|
||||||
|
|
@ -132,7 +132,7 @@ def get_drive_map():
|
||||||
def get_drive_mounts():
|
def get_drive_mounts():
|
||||||
'''
|
'''
|
||||||
Return a list of all connected drives as either:
|
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.
|
- a mount path if the volume does not have a letter.
|
||||||
'''
|
'''
|
||||||
mounts = []
|
mounts = []
|
||||||
|
|
@ -157,8 +157,8 @@ def get_drive_mount_by_name(name):
|
||||||
|
|
||||||
def get_volume_mount(volume):
|
def get_volume_mount(volume):
|
||||||
'''
|
'''
|
||||||
Given a volume path like \\?\Volume{GUID}\, return either:
|
Given a volume path like \\\\?\\Volume{GUID}\\, return 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, or
|
- a mount path if the volume does not have a letter, or
|
||||||
- emptystring if the volume is not mounted at all.
|
- emptystring if the volume is not mounted at all.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ function register_hotkey(hotkey, action, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = hotkey.pop();
|
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 ctrlKey = modifiers.includes("control") || modifiers.includes("ctrl");
|
||||||
const shiftKey = modifiers.includes("shift");
|
const shiftKey = modifiers.includes("shift");
|
||||||
const altKey = modifiers.includes("alt");
|
const altKey = modifiers.includes("alt");
|
||||||
|
|
@ -96,7 +96,7 @@ function hotkeys_listener(event)
|
||||||
return;
|
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);
|
//console.log(identifier);
|
||||||
if (identifier in hotkeys.HOTKEYS)
|
if (identifier in hotkeys.HOTKEYS)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue