VisibilityHandler #3
18
README.md
18
README.md
|
@ -183,7 +183,6 @@ Valid top-level keys and their expected values are:
|
|||
video. Relative to `upload_dir`.
|
||||
Details under the heading [Slides](#slides).
|
||||
|
||||
|
||||
### Subtitles
|
||||
|
||||
There are two top-level keys that deal with subtitles: `subtitles` and
|
||||
|
@ -265,13 +264,18 @@ These are the valid keys for a source object:
|
|||
only have a `true` value for one stream per presentation. If omitted on
|
||||
stream creation, this will defauilt to `false`.
|
||||
|
||||
* `enabled`: `bool`
|
||||
Whether this stream will be displayed in the player. At least one stream
|
||||
must be enabled. If omitted on stream creation, this will deafult to `true`.
|
||||
|
||||
A `sources` object would look like this:
|
||||
```json
|
||||
{
|
||||
"asourcename": {
|
||||
"video": "some/path",
|
||||
"poster": "some/other/path",
|
||||
"playAudio": someBool
|
||||
"playAudio": someBool,
|
||||
"enabled": somebool,
|
||||
},
|
||||
"anothersource": {...},
|
||||
...
|
||||
|
@ -377,12 +381,14 @@ This is a job specification that has all keys and values:
|
|||
"main": {
|
||||
"video": "videos/myvideo.mp4",
|
||||
"poster": "aposter.jpg",
|
||||
"playAudio": true
|
||||
"playAudio": true,
|
||||
"enabled": true
|
||||
},
|
||||
"second": {
|
||||
"video": "myothervideo.mp4",
|
||||
"poster": "anotherposter.jpg",
|
||||
"playAudio": false
|
||||
"playAudio": false,
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"slides": {
|
||||
|
@ -580,3 +586,7 @@ A source definition is a JSON object with the following keys:
|
|||
* `playAudio`: `bool`
|
||||
A boolean value denoting whether to this stream's audio track. This will
|
||||
only be set to `true` for one source in a given package.
|
||||
|
||||
* `enabled`: `bool`
|
||||
Whether this stream will be displayed in the player. At least one stream
|
||||
will be enabled.
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import logging
|
||||
import logging.handlers
|
||||
import multiprocessing as mp
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import multiprocessing as mp
|
||||
|
||||
from .audio import AudioHandler
|
||||
from .handler import Handler
|
||||
from .metadata import MetadataHandler
|
||||
from .poster import PosterHandler
|
||||
from .slides import SlidesHandler
|
||||
|
@ -9,6 +8,7 @@ from .subtitles_whisper_hack import SubtitlesWhisperHandler
|
|||
from .subtitles_import import SubtitlesImportHandler
|
||||
from .thumbnail import ThumbnailHandler
|
||||
from .transcode import TranscodeHandler
|
||||
from .visibility import VisibilityHandler
|
||||
from ..ldap import Ldap
|
||||
from ..utils import get_section
|
||||
|
||||
|
@ -20,6 +20,7 @@ allHandlers = [AudioHandler,
|
|||
SubtitlesWhisperHandler,
|
||||
ThumbnailHandler,
|
||||
TranscodeHandler,
|
||||
VisibilityHandler,
|
||||
]
|
||||
|
||||
def init_handlers(collector, worker, config):
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
from .handler import Handler
|
||||
from ..exceptions import ValidationException
|
||||
|
||||
|
||||
@Handler.register
|
||||
class VisibilityHandler(Handler):
|
||||
"""
|
||||
This class handles visibility settings for streams.
|
||||
"""
|
||||
@classmethod
|
||||
def wants(cls, jobspec, existing_package):
|
||||
"""
|
||||
Return True if this handler wants to process this jobspec.
|
||||
Raises an exception if the job is wanted but doesn't pass validation.
|
||||
|
||||
In order for a job to be wanted, the field 'sources' must exist and
|
||||
at least one of the source items must contain a 'enabled' field.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _validate(cls, jobspec, existing_package):
|
||||
"""
|
||||
Return True if the job is valid for this handler.
|
||||
|
||||
A job is valid as long as at least one of the package's source items
|
||||
"""
|
||||
pass
|
||||
|
||||
def _handle(self, jobspec, existing_package, tempdir):
|
||||
pass
|
|
@ -1,4 +1,3 @@
|
|||
import logging
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from pathlib import Path
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from .handler import Handler
|
||||
from ..exceptions import ValidationException
|
||||
|
||||
|
||||
@Handler.register
|
||||
|
|
|
@ -79,7 +79,7 @@ class PosterHandler(Handler):
|
|||
super()._validate(jobspec, existing_package)
|
||||
if uploaded:
|
||||
if 'upload_dir' not in jobspec:
|
||||
raise ValidationException(f"upload_dir missing")
|
||||
raise ValidationException("upload_dir missing")
|
||||
for name, poster in uploaded.items():
|
||||
if not path.isfile(path.join(jobspec['upload_dir'],
|
||||
poster)):
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from os import path, rename
|
||||
from shutil import rmtree
|
||||
|
||||
import ffmpeg
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
|
|
@ -6,12 +6,10 @@ from multiprocessing import Process, Queue
|
|||
from pathlib import Path
|
||||
from queue import Empty
|
||||
from time import sleep
|
||||
from threading import Event
|
||||
|
||||
from .handler import Handler
|
||||
from ..exceptions import ConfigurationException, ValidationException
|
||||
from ..exceptions import ValidationException
|
||||
|
||||
import torch
|
||||
import whisper
|
||||
import whisper.utils
|
||||
|
||||
|
@ -58,7 +56,7 @@ def _whisper_processor(inqueue,
|
|||
if language is None:
|
||||
out_language = result['language']
|
||||
logger.info(
|
||||
f"Detected language '%s' in %s.", out_language, inpath)
|
||||
"Detected language '%s' in %s.", out_language, inpath)
|
||||
else:
|
||||
out_language = language
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ from multiprocessing import Process, Queue
|
|||
from pathlib import Path
|
||||
from queue import Empty
|
||||
from time import sleep
|
||||
from threading import Event
|
||||
|
||||
from .handler import Handler
|
||||
from ..exceptions import ConfigurationException, ValidationException
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import logging
|
||||
|
||||
from os import path, remove, rename
|
||||
from shutil import rmtree
|
||||
|
||||
import ffmpeg
|
||||
|
||||
|
@ -169,7 +168,7 @@ class TranscodeHandler(Handler):
|
|||
"""
|
||||
super()._validate(jobspec, existing_package)
|
||||
if 'upload_dir' not in jobspec:
|
||||
raise ValidationException(f"upload_dir missing")
|
||||
raise ValidationException("upload_dir missing")
|
||||
for name, source in jobspec['sources'].items():
|
||||
if not path.isfile(path.join(jobspec['upload_dir'],
|
||||
source['video'])):
|
||||
|
@ -228,8 +227,10 @@ class TranscodeHandler(Handler):
|
|||
else:
|
||||
# Initialize a new source
|
||||
# set playAudio to False by default so we don't conflict
|
||||
# with the audio handler
|
||||
sources[name] = {'playAudio': False}
|
||||
# with the audio handler, set enabled to True so we don't
|
||||
# conflict with the visibility handler
|
||||
sources[name] = {'playAudio': False,
|
||||
'enabled': True}
|
||||
|
||||
# Save new source videos
|
||||
sources[name]['video'] = variants
|
||||
|
|
57
pipeline/handlers/visibility.py
Normal file
57
pipeline/handlers/visibility.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
import copy
|
||||
from pathlib import Path
|
||||
|
||||
from .handler import Handler
|
||||
from ..exceptions import ValidationException
|
||||
from ..package import Package
|
||||
|
||||
@Handler.register
|
||||
class VisibilityHandler(Handler):
|
||||
"""
|
||||
This class handles visibility settings for streams.
|
||||
"""
|
||||
@classmethod
|
||||
def wants(cls, jobspec: dict, existing_package: Package) -> bool | ValidationException:
|
||||
"""
|
||||
Return True if this handler wants to process this jobspec.
|
||||
Raises an exception if the job is wanted but doesn't pass validation.
|
||||
|
||||
In order for a job to be wanted, the field 'sources' must exist and
|
||||
at least one of the source items must contain a 'enabled' field.
|
||||
|
||||
"""
|
||||
if 'sources' in jobspec and any('enabled' in source for source in jobspec['sources'].values()):
|
||||
return cls._validate(jobspec, existing_package)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _validate(cls, jobspec: dict, existing_package: Package) -> bool | ValidationException:
|
||||
"""
|
||||
Return True if the job is valid for this handler.
|
||||
|
||||
A job is valid as long as at least one of the package's source items
|
||||
has its 'enabled' field set to True.
|
||||
"""
|
||||
sources = copy.deepcopy(existing_package.get('sources', {}))
|
||||
for name, source in jobspec['sources'].items():
|
||||
if name not in sources:
|
||||
sources[name] = {}
|
||||
if 'enabled' in source:
|
||||
sources[name]['enabled'] = source['enabled']
|
||||
for source in sources.values():
|
||||
if 'enabled' in source:
|
||||
return True
|
||||
raise ValidationException("No enabled sources")
|
||||
|
||||
def _handle(self, jobspec: dict, existing_package: Package, tempdir: Path) -> callable:
|
||||
"""
|
||||
"""
|
||||
def apply_func(package: Package) -> None:
|
||||
sources = package.get('sources', {})
|
||||
for name, source in jobspec['sources'].items():
|
||||
if name not in sources:
|
||||
sources[name] = {}
|
||||
if 'enabled' in source:
|
||||
sources[name]['enabled'] = source['enabled']
|
||||
|
||||
return apply_func
|
|
@ -1,5 +1,4 @@
|
|||
from ldap3 import Connection, ObjectDef, Reader, Server
|
||||
from ldap3.core.exceptions import LDAPSocketSendError
|
||||
|
||||
class Ldap:
|
||||
def __init__(self, conf):
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import json
|
||||
import logging
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from pprint import pformat
|
||||
|
|
|
@ -2,7 +2,7 @@ import json
|
|||
|
||||
from copy import deepcopy
|
||||
from os import mkdir, path, rename, remove
|
||||
from shutil import copy, copytree
|
||||
from shutil import copytree
|
||||
|
||||
import ffmpeg
|
||||
|
||||
|
|
|
@ -110,4 +110,4 @@ class CatturaProcessor(Preprocessor):
|
|||
for key in data.keys():
|
||||
if key.startswith('mediapackage:'):
|
||||
return data[key]
|
||||
raise KeyError(f"no 'mediapackage' key in job specification")
|
||||
raise KeyError("no 'mediapackage' key in job specification")
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from os import mkdir, path
|
||||
|
||||
from ..queuethread import QueueThread
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from os import chdir, path
|
||||
from types import FunctionType
|
||||
|
||||
|
||||
|
@ -23,6 +22,7 @@ canonical_jobspec = {
|
|||
'source': str}},
|
||||
'sources': {str: {'poster': str,
|
||||
'playAudio': bool,
|
||||
'enabled': bool,
|
||||
'video': str}},
|
||||
'slides': str,
|
||||
'notification_url': str,
|
||||
|
@ -49,6 +49,7 @@ canonical_manifest = {
|
|||
'subtitles': {str: str},
|
||||
'sources': {str: {'poster': str,
|
||||
'playAudio': bool,
|
||||
'enabled': bool,
|
||||
'video': {'720': str,
|
||||
'1080': str}}}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import logging
|
||||
import multiprocessing as mp
|
||||
|
||||
from collections import deque
|
||||
|
@ -6,7 +5,7 @@ from collections.abc import Iterable
|
|||
from dataclasses import dataclass
|
||||
from pprint import pformat
|
||||
from threading import Event
|
||||
from time import sleep, strftime
|
||||
from time import sleep
|
||||
from typing import Callable
|
||||
|
||||
from .queuethread import QueueThread
|
||||
|
|
|
@ -5,7 +5,6 @@ import sys
|
|||
|
||||
from configparser import ConfigParser
|
||||
from os import path
|
||||
from time import sleep
|
||||
|
||||
from pipeline import Pipeline
|
||||
|
||||
|
|
23
test.py
23
test.py
|
@ -272,7 +272,7 @@ class PipelineTest(DaemonTest):
|
|||
print("¤ Contents of invalid notification file ¤")
|
||||
print(f.read())
|
||||
print("¤ End invalid notification file contents ¤")
|
||||
self.fail(f"Invalid JSON in result file.")
|
||||
self.fail("Invalid JSON in result file.")
|
||||
|
||||
# Validate that this is the correct resultfile
|
||||
self.assertEqual(jobid, result['jobid'])
|
||||
|
@ -300,7 +300,7 @@ class PipelineTest(DaemonTest):
|
|||
return final_result
|
||||
|
||||
def init_job(self, pkgid=False, url=False, subs=False, thumb=False,
|
||||
source_count=0, poster_count=0):
|
||||
source_count=0, disabled_source_count=0, poster_count=0):
|
||||
jobspec = {}
|
||||
|
||||
if url == True:
|
||||
|
@ -333,7 +333,7 @@ class PipelineTest(DaemonTest):
|
|||
if source_count:
|
||||
# poster_count determines the number of posters to include.
|
||||
# Any remaining posters will be generated in pipeline
|
||||
self.add_sources(jobspec, source_count, poster_count)
|
||||
self.add_sources(jobspec, source_count, poster_count, disabled_source_count)
|
||||
|
||||
return jobspec
|
||||
|
||||
|
@ -343,7 +343,8 @@ class PipelineTest(DaemonTest):
|
|||
str(uuid.uuid4()))
|
||||
jobspec['upload_dir'] = uldir
|
||||
makedirs(uldir)
|
||||
return uldir
|
||||
return uldir
|
||||
return jobspec['upload_dir']
|
||||
|
||||
def add_subtitles(self, jobspec):
|
||||
uldir = self.ensure_uldir(jobspec)
|
||||
|
@ -356,16 +357,22 @@ class PipelineTest(DaemonTest):
|
|||
copyfile(subspath, path.join(uldir, subsfile))
|
||||
jobspec['subtitles'] = subspec
|
||||
|
||||
def add_sources(self, jobspec, count, poster_count=0):
|
||||
def add_sources(self, jobspec, count, poster_count=0, disabled_count=0):
|
||||
uldir = self.ensure_uldir(jobspec)
|
||||
jobspec['sources'] = {}
|
||||
if 'sources' not in jobspec:
|
||||
jobspec['sources'] = {}
|
||||
posters = 0
|
||||
disabled = 0
|
||||
for i in range(count):
|
||||
videopath = next(self.videopaths)
|
||||
videofile = path.basename(videopath)
|
||||
copyfile(videopath, path.join(uldir, videofile))
|
||||
sourcedef = {'video': videofile,
|
||||
'playAudio': False}
|
||||
'playAudio': False,
|
||||
'enabled': True}
|
||||
if disabled < disabled_count:
|
||||
sourcedef['enabled'] = False
|
||||
disabled += 1
|
||||
if i == 0:
|
||||
sourcedef['playAudio'] = True
|
||||
if posters < poster_count:
|
||||
|
@ -457,7 +464,7 @@ class PipelineTest(DaemonTest):
|
|||
|
||||
#@unittest.skip("This test is very slow")
|
||||
def test_transcoding(self):
|
||||
jobspec = self.init_job(source_count=4, poster_count=2)
|
||||
jobspec = self.init_job(source_count=3, poster_count=2, disabled_source_count=1)
|
||||
|
||||
jobid = self.submit_default_job(jobspec)
|
||||
result = self.wait_for_result(jobid, ['AudioHandler',
|
||||
|
|
Loading…
Reference in New Issue
Block a user