Last active
January 17, 2017 22:47
-
-
Save stuxcrystal/3c4cdeee5b8eaba3552e0dbefdd11dcb to your computer and use it in GitHub Desktop.
Adds Vapoursynth to MetaPath
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import vsimport | |
from ffms2 import Source | |
clip = Source("/path/to/your/clip.mkv") | |
clip.set_output() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
VSImport. Supercharge your VapourSynth-Code | |
VSImport lets you import VapourSynth-Namespaces as if they were normal python modules. | |
To enable this feature just use: | |
>>> import vsimport | |
As soon as you did that, any vapoursynth-namespace can be imported into your module. | |
>>> from std import AssumeFPS, BlankClip | |
>>> c = BlankClip(length=240) | |
>>> c = AssumeFPS(c, fpsnum=60) | |
>>> c.set_output() | |
It even work on *-imports | |
>>> from std import * | |
>>> BlankClip(length=240) # doctest: +ELLIPSIS | |
<vapoursynth.VideoNode object at 0x...> | |
If a normal python-module already shadowed the namespace, | |
you can use the virtual 'vpy'-package to import your namespaces. | |
>>> from vpy.std import BlankClip | |
>>> BlankClip(length=240).set_output() | |
The vpy-package itself exposes a variable containing the current core. | |
>>> import vpy | |
>>> vpy.core # doctest: +ELLIPSIS | |
<vapoursynth.Core object at ...> | |
It also lists all plugins. | |
>>> vpy.plugins # doctest: +ELLIPSIS | |
[...] | |
Internal Notes: | |
The core-singleton will be created with the first import. | |
Create the core before you import this module for custom settings. | |
>>> import vapoursynth | |
>>> vapoursynth.get_core() # doctest: +SKIP | |
<vapoursynth.Core object at ...> | |
>>> import vsimport | |
>>> from vpy import core | |
>>> core # doctest: +ELLIPSIS | |
<vapoursynth.Core object at ...> | |
Additinally, some software only create a single python interpreter | |
for multiple passes thus creating multiple vapoursynth-cores. All | |
filters are deferred so that we always use the current core. | |
To access the deferred filter directly the filter-attribute will | |
contain the current filter. | |
>>> from std import AssumeFPS | |
>>> AssumeFPS | |
<DeferredFilter std.AssumeFPS> | |
>>> AssumeFPS.filter # doctest: +ELLIPSIS | |
<vapoursynth.Function object at ...> | |
""" | |
import sys | |
import functools | |
from types import ModuleType | |
from importlib.abc import Loader, Finder | |
import vapoursynth | |
class DeferredFilter(ModuleType): | |
def __init__(self, namespace, name): | |
self.namespace = namespace | |
self.name = name | |
@property | |
def filter(self): | |
"""Returns the current function object""" | |
return getattr(getattr(vapoursynth.get_core(), self.namespace), self.name) | |
def __call__(self, *args, **kwargs): | |
return self.filter(*args, **kwargs) | |
def __repr__(self): | |
return "<DeferredFilter %s.%s>" % (self.namespace, self.name) | |
class VapoursynthExtension(ModuleType): | |
@classmethod | |
def from_name(cls, name): | |
class FakeNS: | |
pass | |
fns = FakeNS() | |
fns.name = name | |
return cls(fns) | |
def __init__(self, spec): | |
super(VapoursynthExtension, self).__init__(spec.name) | |
self._name = spec.name | |
if self._name.startswith("vpy."): | |
self.__package__ = "vpy" | |
self._name = self._name[4:] | |
def __getattr__(self, name): | |
if name in self.get_functions(): | |
return DeferredFilter(self._name, name) | |
return super(VapoursynthExtension, self).__getattr__(name) | |
@property | |
def __all__(self): | |
return list(self.get_functions()) | |
def get_functions(self): | |
data = set() | |
for plugin in vapoursynth.get_core().get_plugins().values(): | |
if plugin['namespace'] == self._name: | |
data |= set(plugin['functions'].keys()) | |
return data | |
class VpyModule(ModuleType): | |
def __init__(self, spec): | |
super(VpyModule, self).__init__(spec.name) | |
self.DeferredFilter = DeferredFilter | |
@property | |
def __all__(self): | |
return self.plugins + ['core', 'DeferredFilter'] | |
@property | |
def core(self): | |
"""Returns the current core of this environment""" | |
return vapoursynth.get_core() | |
@property | |
def plugins(self): | |
"""Returns the plugins in this environment.""" | |
return list(importer.namespaces) | |
def __getattr__(self, name): | |
if name in self.plugins: | |
return VapoursynthExtension.from_name(name) | |
return super(VpyModule, self).__getattr__(name) | |
class CoreModuleLoader(Loader): | |
""" | |
Loads vapoursynth-extensions as modules. | |
""" | |
def create_module(self, spec): | |
if spec.name == "vpy": | |
module = VpyModule(spec) | |
module.__package__ = "vpy" | |
module.__path__ = None | |
return module | |
return VapoursynthExtension(spec) | |
def exec_module(self, module): | |
pass | |
def module_repr(self, module): | |
return "<VapourSynthNamespace %s>"%(module._name) | |
class VapoursynthPluginImporter(Finder): | |
def __init__(self): | |
self.loader = CoreModuleLoader() | |
@property | |
def namespaces(self): | |
namespaces = set() | |
for plugin in vapoursynth.get_core().get_plugins().values(): | |
namespaces.add(plugin['namespace']) | |
return namespaces | |
def find_module(self, fullname, path=None): | |
if fullname == "vpy": | |
return self.loader | |
if fullname.startswith("vpy."): | |
fullname = fullname[4:] | |
if fullname in self.namespaces: | |
return self.loader | |
return None | |
importer = VapoursynthPluginImporter() | |
sys.meta_path.append(importer) | |
if __name__ == "__main__": | |
import doctest | |
doctest.testmod() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment