Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get "last on screens" working again #10

Open
Kunsi opened this issue Dec 29, 2023 · 4 comments
Open

Get "last on screens" working again #10

Kunsi opened this issue Dec 29, 2023 · 4 comments

Comments

@Kunsi
Copy link
Collaborator

Kunsi commented Dec 29, 2023

This probably requires some work on the info-beamer side as well, since this requires the devices to send the played assets back to us.

Maybe @dividuum can join in and tell us what we need to do?

@dividuum
Copy link

So I looked into how this works as I didn't really remember what I did back then in a hurry while waiting for the next talk and its only in some yolo fork probably only living on my old laptop :-}. It essentially works like this:

In the node.lua a new "ProofOfPlay" function was added like this:

local function ProofOfPlay()
    local device_id
    node.event("config_updated", function(config)
        device_id = config.__metadata.device_id
        print("my device id is", device_id)
    end)
    local function submit(asset_id)
        local now = os.time()
        local proof = json.encode{
            device_id = device_id,
            asset_id = asset_id,
            ts = now,
            id = md5.sumhexa(string.format(
                "%s:%s:%s", now, device_id, asset_id, math.random()
            ))
        }
        print("new proof:", proof)
        tcp_clients.send("root/proof", proof)
    end

    local function create(asset_id)
        local confirmed = false
        return function(obj)
            if confirmed then
                return
            end
            local state = obj:state()
            if state ~= "loaded" then
                return
            end
            submit(asset_id)
            confirmed = true
        end
    end

    return {
        create = create;
    }
end

local proof_of_play = ProofOfPlay()

So this basically receives the asset id in the create function, and returns a probe function that watches an asset to see if it is playing. Once that's confirmed it is sent to a local python process in submit.

The Image, Video and RawVideo code is augmented like this:

        ...
        local proof = proof_of_play.create(asset.asset_id)

        local function draw(now)
            local img_obj = img()
            proof(img_obj)
            ...

The python code receiving the JSON formatted lines containing the device_id, asset_id, timestamp and some randomly generated id was its own node directory (proof) with a service file like this:

#!/usr/bin/python
from threading import Lock
import sys, requests, time, threading, os, traceback
from hosted import api, config, node
from ibquery import InfoBeamerQuery

config.restart_on_update()

class SubmissionBuffer(object):
    def __init__(self):
        self._max_size = 2048
        self._buffer = []
        self._lock = Lock()

    def add(self, data):
        with self._lock:
            self._buffer.append(data)
            if len(self._buffer) > self._max_size:
                self._buffer = self._buffer[-self._max_size:]

    def borrow(self):
        with self._lock:
            return set(self._buffer)

    def confirm(self, confirmed_set):
        with self._lock:
            self._buffer = [
                item
                for item in self._buffer
                if item not in confirmed_set
            ]
SubmissionBuffer = SubmissionBuffer()

def gather():
    ib = InfoBeamerQuery("127.0.0.1")
    con = ib.node('root/proof').io(raw=True)
    for line in con:
        print >>sys.stderr, 'pop: %s' % line
        SubmissionBuffer.add(line.strip())

def submit():
    while 1:
        proofs = SubmissionBuffer.borrow()
        if config.target_url and proofs:
            try:
                body = '\n'.join(proofs)
                r = requests.post(config.target_url, timeout=5, data=body)
                r.raise_for_status()
                SubmissionBuffer.confirm(proofs)
            except:
                traceback.print_exc()
        time.sleep(config.interval)

def run_in_thread(fn):
    def wrap():
        try:
            while 1:
                fn()
        except Exception:
            traceback.print_exc()
            os._exit(1)
    thread = threading.Thread(target=wrap)
    thread.daemon = True
    thread.start()

if __name__ == "__main__":
    run_in_thread(submit)
    gather()

Basically one thread uses ibquery to connect to the info-beamer process to listen for those messages sent from Lua (see gather) and another thread uses the collected set of lines and sends them to the cms web server which maps the asset ids to the content uploaded from users. I've invited you to the old 36c3 account so you can look at the configuration as it was before it was all shut down.

Not sure if that's useful. If you need more info, let me know.

@Kunsi
Copy link
Collaborator Author

Kunsi commented Jan 1, 2024

Thanks, @dividuum!

Would you accept a pull request to the upstream scheduled-player package re-adding that feature?

@Kunsi
Copy link
Collaborator Author

Kunsi commented Jan 9, 2024

I just noticed the ProofOfPlay feature is included in hosted.py - this seems to use OnDeviceAPIs.pop, which in turn seems to be an APIProxy object - do you have any information on how to actually use that?

@dividuum
Copy link

dividuum commented Jan 9, 2024

That unfortunately won't really be helpful in the case of the congress info screens as that mechanism isn't anywhere close to real time. So it's probably a bad fit. I think the way to reimplement the "shown on screen" feature would be to decouple the proof of play feature in the scheduled player package to make it usable from addon packages as well.

Something like using node.dispatch to broadcast playback of a video/image and then using node.event to register a callback. This could then also be used within a custom package to send out playback information in real time to a custom backend. Think that might work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants