-
Notifications
You must be signed in to change notification settings - Fork 2
Adapters guidelines and rules
As per the current web standards, the current situation in web media handling could still be described as a whole mess and a far west (in the author's opinion). So, to avoid further contributing to the entropy of the web itself, adapters should follow some well-precise rules, built to make them safer.
Each adapter must be a class, which will get instanced by @sub37/server
when used. Each session creates a new adapter instance. By doing this way, different adapters could hold session information without polluting the module scope. Yet, this is still possible, but it will be your choice to decide whether risk suffering by doing this.
Adapters should own a meaningful class name, that will describe exactly which format the adapter will support. The adapter also shouldn't be an anonymous class**.
This is not strict and necessary but very suggested. Starting from v1.1, BaseAdapter
overrides the toString()
method to print the class name when invoked. This happens when emitting errors or printing logs. Therefore, having a meaningful name will allow you to have better logs.
As per the title, @sub37/server
exposes a class, BaseAdapter
which must be used by every adapter. This base class will create the required methods so that, if they do not get overridden, they will end up throwing a meaningful error for adapters developers and users.
There are some methods and properties that the server requires to override.
-
supportedType
static property. This property will return the mime-type of the supported format. That will be used to select the adapter when a track is passed. An adapter will be selected if the track'smimeType
and the adaptersupportedType
will match.
The mimeType will never get changed by @sub37/server
, but if you are concerned by this possibility, you can use a getter as well.
import { BaseAdapter } from "@sub37/server";
export class MyAdapter extends BaseAdapter {
public static get supportedType(): string {
return "...";
}
...
}
-
toString
method. This override will let some errors have a meaningful error message, containing your adapter's name. An adapter will be completely rejected iftoString
is not overridden.
Warning Starting from v1.1, this won't be needed anymore. ⚠
Both static and instance toString
methods are required to get overridden.
import { BaseAdapter } from "@sub37/server";
export class MyAdapter extends BaseAdapter {
...
public static toString(): string {
return "MyAdapter";
}
public toString(): string {
return "MyAdapter";
}
...
}
-
parse
instance method. This is the core of your logic..parse()
is the entry point of your adapter, and will be invoked when some content is ready to be parsed. This method will receive the content passed to the tracks.
As the content can be of several formats, for Typescript folks, the parameter is marked to be unknown
, so that you can specify the type when overriding the .parse()
method.
Not returning a ParseResult
will make your parsed result get discarded and an error getting thrown.
A ParseResult
is a container that will expose two properties: data
, which is an Array<CueNode>
, and errors
, which is an Array<ParseError>
.
ParseError
is an object with the following structure:
interface ParseError {
error: Error;
isCritical: boolean;
failedChunk: unknown;
}
errors
will therefore contain any error that will happen during the parsing phase, whether it is critical or not.
ParseError.prototype.error
will contain an Error instance about what happened. A critical error is expected to make the adapter perform an early return and to have the isCritical
flag marked as true
. You can populate failedChunk
to let the error carry more information about that failure.
data
will instead contain the CueNode
you were able to create through your parsing.
ParseResult
(both instance and type) and ParseError
(as type) are exposed as a property of BaseAdapter
.
import { BaseAdapter } from "@sub37/server";
export class MyAdapter extends BaseAdapter {
...
public override parse(content: unknown): BaseAdapter.ParseResult {
/** Non critical errors **/
const failures: BaseAdapter.ParseError[] = [];
let result: CueNode[];
try {
result = attemptConvertIntoCueNodeSomehow(content);
} catch (err: unknown) {
/** Critical error */
return BaseAdapter.ParseResult(undefined, [
{
error: new CriticalError(),
failedChunk: "...",
isCritical: true,
},
]);
}
return BaseAdapter.ParseResult(result, failures);
}
}
}