Module does not update in the Player

On my data provider
I did the following:

 public function fetchDuration(DurationProviderInterface $durationProvider): WidgetProviderInterface
    {
        return $this;
    }

    public function getDataCacheKey(DataProviderInterface $dataProvider): ?string
    {
        // Returns a fixed key to keep the cache working, but with controlled behavior
        return 'gumDataProvider-' . md5('G1RSSFeed');
    }
    
    public function getDataModifiedDt(DataProviderInterface $dataProvider): ?Carbon
    {
        // Returns the current date/time to force the cache to always be refreshed
        return Carbon::now();
    }

So with each update from my Android client to the CMS, that is, every 5 minutes I should update this module, right? More repair that continues to use the same data as if the cache was not updated. Even getDataModifiedDt returning as it does now. Is there anything else I’m doing wrong?

When the module has a data provider, should this update happen automatically? Do I need to open the Layout and save for this to happen?

@Nadz Any suggestions what I’m doing wrong?

Is XTR and the Widget Sync task running in your CMS installation? Data is kept up to date in the background via XTR, if it isn’t running then you won’t see any updates.

It’s like this:

My installation was done using Docker, so from what I understand I wouldn’t need to create a routine on my server to run xtr as one would already be configured?

I believe it is in fact something in my data provider that is not updating, because when using the native RSS ticjer module with an external RSS the updates worked normally, seeing the RSS module code is with

public function getDataModifiedDt(DataProviderInterface $dataProvider): ?Carbon
    {
        return null;
    }

tentei fazer isso no meu modulo cutomizado, mais infelizmente sem atualização ainda.

@dan Unfortunately, so far I haven’t achieved anything. Follow my current full provider. It’s just the problem of updating that doesn’t happen, only if you open the Layout and save manually, otherwise it always displays the same content in the player.

<?php
namespace Xibo\Custom;

use Carbon\Carbon;
use GuzzleHttp\Client;
use Xibo\Widget\Provider\DataProviderInterface;
use Xibo\Widget\Provider\DurationProviderInterface;
use Xibo\Widget\Provider\WidgetProviderInterface;
use Xibo\Widget\Provider\WidgetProviderTrait;

class g1DataProvider implements WidgetProviderInterface
{
    use WidgetProviderTrait;

    public function fetchData(DataProviderInterface $dataProvider): WidgetProviderInterface
    {
        // Criando uma instância do cliente HTTP (Guzzle)
        $client = new Client();


        // URL do feed RSS do G1
        $url = $dataProvider->getProperty('uri');

        if (empty($url)) {
            throw new InvalidArgumentException(__('Please enter the URI to a valid RSS feed.'), 'uri');
        }


        // Realizando a requisição GET para o feed RSS
        $response = $client->get($url);
        $rssContent = $response->getBody()->getContents();

        // Carregando o XML
        $rss = simplexml_load_string($rssContent, "SimpleXMLElement", LIBXML_NOCDATA);

        // Determinar o número total de itens disponíveis no feed (ou no máximo 10)
        $totalItems = min(count($rss->channel->item), 10); // Limita o total a 10

        // Gerar um índice aleatório dentro do intervalo válido
        $aleatorio = rand(0, $totalItems - 1);

        // Pegar uma notĂ­cia aleatĂłria
        $item = $rss->channel->item[$aleatorio];

        $title = (string) $item->title; // TĂ­tulo da notĂ­cia
        $mediaContent = $item->children('media', true)->content->attributes()->url ?? null; // URL da imagem

        if ($mediaContent) {
            // URL da logo do G1
            $logoUrl = 'https://raw.githubusercontent.com/henriquelucas/modulo-g1-rss-xibo/refs/heads/main/g1.png';

            // Gerar o HTML com a imagem responsiva, título no rodapé e logo no canto superior esquerdo
            $html = "
            <div style='
                display: flex;
                align-items: center;
                justify-content: center;
                width: 1920px;
                height: 1080px;
                background-color: #000;'>

                <div style='
                    width: 100%;
                    height: 100%;
                    position: relative;'>
                    
                    <!-- Imagem de fundo -->
                    <div style='
                        width: 100%;
                        height: 100%;
                        background-image: url(\"{$mediaContent}\");
                        background-size: cover;
                        background-position: center;
                        position: absolute;
                        top: 0;
                        left: 0;
                        z-index: 1;'>

                    </div>
                    
                    <!-- Background preto semi-transparente -->
                    <div style='
                        position: absolute;
                        top: 0;
                        left: 0;
                        width: 100%;
                        height: 100%;
                        background: rgba(0, 0, 0, 0.5); /* TransparĂŞncia no fundo */
                        z-index: 2;'>
                    </div>
                    
                    <!-- Logo do G1 -->
                    <img src=\"{$logoUrl}\" alt=\"G1 Logo\" style='
                        position: absolute;
                        top: 20px;
                        left: 20px;
                        width: 200px;
                        height: auto;
                        z-index: 3;'>

                    <!-- TĂ­tulo da notĂ­cia -->
                    <div style='
                        position: absolute;
                        bottom: 0;
                        width: 100%;
                        background: linear-gradient(to top, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0));
                        color: white;
                        text-align: left;
                        padding: 20px;
                        font-size: 80px;
                        word-wrap: break-word;
                        overflow-wrap: break-word;
                        z-index: 3;'>
                        <p style='margin: 0; padding: 20px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);font-weight: 600;'>{$title}</p>
                    </div>

                </div>
            </div>";

            // Adicionando os dados ao provider
            $dataProvider->addItem([
                'subject' => $title,
                'body' => $html,
                'date' => Carbon::now(),
                'createdAt' => Carbon::now(),
            ]);
        }

        // Marcando que os dados foram processados
        $dataProvider->setIsHandled();

        return $this;
    }

    public function fetchDuration(DurationProviderInterface $durationProvider): WidgetProviderInterface
    {
        return $this;
    }

    public function getDataCacheKey(DataProviderInterface $dataProvider): ?string
    {
        // Retorna um cache chave Ăşnico com base no timestamp atual
        return 'custom_data_' . time();
    }

    public function getDataModifiedDt(DataProviderInterface $dataProvider): ?Carbon
    {
        return null;
    }
}

Hi @Henrique_Lucas !
Were you able to get the widget to update dynamically?

Not yet, the documentation isn’t very helpful. I’d appreciate some guidance.

It only updates if you open and close the Layout, but not automatically with Player calls. @dan The documentation for developers could be improved. Or provide clearer guidance on how the CMS updates the contents of these modules with dates?

@Nadz is looking at this for you.

1 Like

Hi @Henrique_Lucas,

I’ll assume you’re testing this in a local environment. If so, CMS tasks like the Widget Sync task don’t run automatically. To trigger it, while your Layout is scheduled to the Player, run:

clear && docker-compose exec web /bin/bash -c "cd /var/www/cms; LOG_TO_CONSOLE=true php bin/run.php 9"

This runs the designated task and prints the logs to the console.
Replace 9 with the actual Task ID from your CMS.

If you prefer to run it without console logging:

clear && docker-compose exec web /bin/bash -c "cd /var/www/cms; php bin/run.php 9"

Change web to match the name of the container you are using.

The reason it is not working on your local setup is that the CMS is responsible for refreshing the data and passing it on to the Player, and this is done by the Widget Sync task. On a production system this task runs automatically, but in a local setup it usually does not run unless you start it yourself. That is why you do not see updates until you intervene. Running the command above simply forces the Widget Sync task to run so the Player can receive the updated data.

I created two more modules based on Ticker RSS, and I’m facing the same problem. Custom “article”/rss/ticker modules are not being updated by the widget sync task (which, yes, is running).

I think I found one of the problems.

This “dataCacheKey” class from my custom module only had the “uri” key.

I copied it as it was from the original module with more tags, and it seems to have worked.
It ended up like this:

My next step is to test updating the widgetsync to a custom datatype.

@dan and @Nadz Hi guys!

Nothing doing.

Facing the same problem as Henrique with my custom RSS module.

It only updates when I manually save the layout.

FYI: the provider is basically a copy of the original RSS provider, the difference is that it uses pre-set links instead of the user inserting them in the layout editor.

In short, it seems that XTR doesn’t reach the custom modules.

Can you help us clarify how the widgetsync task currently updates RSS feeds?

How can I add my custom module to be updated by this task?

If that’s not possible, how can I create a task to automatically fetch these custom feeds?

The documentation on creating tasks is very basic at the moment. Could you please provide some feedback?

Thank you!

========
Edit:

Checking logs using:

set LOG_TO_CONSOLE=true && “php.exe” -f MYCMSPATH\bin\run.php 9

And found that error:
[2026-02-02 12:57:25] CONSOLE.CRITICAL: Setting value in cache caused exception. {“exception”:"[object] (Stash\Exception\WindowsPathMaxLengthException(code: 0): Cache path exceeds Windows PHP MAX_LENGTH of 260 characters.

Trying to enable Windows Long path, this may be the cause in my case.
Don’t know it’s the same with docker as @Henrique_Lucas told us