Skip to content
On this page

Structure

Since extensions are just a folders with bunch of files, each extension must contain a few specific files in order to be functional.

extension
├── passport.json
├── tester.lua
└── scraper.lua

Passport

Passport contains information about extension's name, description, requirements (operating system, external programs) and such.

JSON schema coming soon...

Tester

Tester is a file, that implements just one function

lua
function test() end

This function can be run by the CI in order to verify extension's status.

INFO

While it's better to include tester.lua with some meaningful tests in your extension, its absence won't cause any errors.

You can also import scraper module to test it's steps. For example:

lua
local scraper = require('scraper')

local M = {}

function M.test()
   local results = scraper.search.handler('test query')
   assert(#results > 0, 'Search did not return any results')
end

return M

Scraper

Scraper is the extension's core. It defines the functionality of the extension.

In order to work, scraper.lua must implement some steps.

Steps, in essence, are just a handler functions that accept Media or text query and may output other medias.

Before going in further to what steps should be defined, let's run through the basic types.

Media

Media is an intermediate object (a lua table, basically) that represents a single media.

For example, imagine your extension has some sort of search function that gets the query and returns found results. Those results are essentially are lists of media. Sounds not too complex right?

Now, let's see how media looks like

FieldTypeDescription
titlestringTitle that is used a string representation of the media.
descriptionstring?A short description of the media. Keep it one line.
info(fun(self: Media): string)?Additional long info about this media. Supports markdown

Question mark ? means that type is optional -- it can be absent

Any other fields, e.g. url or my_super_important_field, will be preserved when media is passed between states. So, while the fields above are reserved by Awirix and considered special (they provide additional functionality based on their presence) anything else is left for your needs.

Noun

A small object that represents singular and plural forms of the noun.

FieldTypeDescription
singularstring?Singular form
pluralstring?Plural form

Context

Context is object that is passed to the each state handler and is used to report progress or raise errors.

FieldTypeDescription
progressfunc(message: string)Sets the progress message to the given one
errorfunc(message: string)Stops the execution and raises an error with the given message

In generated extension's template you would see something like

lua
-- @param media Media
-- @param ctx Context
function(media, ctx) end

That means you can use ctx param like this

lua
ctx.progress('Sending request...')
ctx.progress('Parsing response...')
ctx.error('Oops!')

Alright, now let's move to the steps themselves

A step that accepts text query by providing a user a text input screen.

This step may be omitted if this extension does not provide searching functionality. Might be the case if it is dedicated to the single show, movie, book etc.

lua
local M = {}

M.search = {
    handler = function(query, ctx) return {} end
}

return M
FieldTypeDescription
titlestring?Title to show when typing query.
subtitlestring?Title in the list of search results.
placeholderstring?Placeholder to show in the search input
handlerfun(query: string, ctx: Context): Media[]Function that will be called to search for the media.
nounNoun?Noun that is used to name a single media.

Layers

Each layer returns a list of sub-media for the given one. For example, you can search for a show, then selected show will be passed to the first layer that's responsible for returning show's seasons. After that, the selected season will be passed to the second layer that would return season's episodes.

Layers may be omitted (nil or 0 length) if this extension does not provide such functionality (e.g. just search and watch, no seasons, no episodes). Each layer receives a single media selected from the search step or previous layer. If search step is omitted, first layer will receive a nil instead of the media.

However, scraper must have either layers, search or both

lua
local M = {}

M.layers = {
   {
      title = 'Layer 1',
      handler = function(media, ctx) return {} end
   },
   {
      title = 'Layer 2',
      handler = function(media, ctx) return {} end
   }
}


return M
FieldTypeDescription
titlestringTitle to show in the list of layer's results.
handlerfun(media: Media, ctx: Context): Media[]Function that will be called to run the layer.
nounNoun?Noun that is used to name a single media.

Actions

Actions are further actions that can be performed on the selected media. Something like streaming or downloading the selected media.

Actions may be omitted (nil or 0 length) if this extension does not provide such functionality (e.g. just media browsing, no actions), which is rare case but is allowed if you want to.

lua
local M = {}

M.actions = {
   {
      title = 'Stream',
      description = 'Plays in your default video player'
      max = 1,
      handler = function (medias, ctx) end
   },
   {
      title = 'Download',
      handler = function (medias, ctx) end
   }
}

return M

Note, that actions accept Media[] in their handlers. It makes sense for Download action to accept 10 or 20 medias to download them all at once but looks weird for the Stream action. Therefore, you can control how many medias an action can accept by changing min/max fields.

In this example, Stream action has field max set to 1. That way, if user selected multiple medias, they would not be able to select this action.

lua
{
    title = 'Stream',
    description = 'Plays in your default video player'
    max = 1,
    handler = function (medias, ctx)
        -- here we 100% than we won't receive more than 1 medias
        local media = medias[1]
    end
}
FieldTypeDescription
titlestringTitle to show in the list of actions.
descriptionstring?Short description of the action.
handlerfun(medias: Media[], ctx: Context)Function that will be called to perform the action.
minnumber?Minimum number of the selected medias to perform this action
maxnumber?Maximum number of the selected medias to perform this action

Released under MIT License.