The 2026 edition of the GARR Conference, titled “The Shape of change. Between digital ethics and shared roots” - La forma del cambiamento. Tra etica digitale e radici condivise, will be hosted by the University of Pisa from 19 to 21 May at the Pontecorvo Area.
The GARR Conference is the event where experiences are shared on the use of digital infrastructures and services by those who use the GARR network and those who actively contribute to building it and improving its services.
This edition offers a reflection on the ongoing transformation in digital technologies and knowledge infrastructures, placing at its core the relationship between digital sovereignty, AI ethics, system resilience, sustainability, historical memory and new scientific frontiers. A dialogue between future and roots, between technological innovation and the role of the scientific community, in a context marked by profound social, economic and geopolitical transformations.
/* Stili della galleria (prefisso garr- per non collidere con la pagina ospite) */
.garr-video-facade {
position: relative;
display: block;
width: 100%;
padding: 0;
border: 0;
aspect-ratio: 16 / 9;
background-color: #000;
background-size: cover;
background-position: center;
cursor: pointer;
overflow: hidden;
}
.garr-play-icon {
position: absolute;
top: 50%;
left: 50%;
width: 68px;
height: 48px;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.6);
border-radius: 12px;
transition: background 0.2s ease;
}
.garr-play-icon::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-40%, -50%);
border-style: solid;
border-width: 11px 0 11px 19px;
border-color: transparent transparent transparent #fff;
}
.garr-video-facade:hover .garr-play-icon,
.garr-video-facade:focus .garr-play-icon {
background: #c00;
}
.garr-video-frame {
display: block;
width: 100%;
aspect-ratio: 16 / 9;
height: auto;
border: 0;
}
// Configurazione
// MAX_VIDEOS: numero intero positivo per limitare i video mostrati.
// Lascia null / undefined / -1 (o qualunque valore non intero-positivo)
// per mostrare TUTTI i video disponibili con il tag.
const MAX_VIDEOS = null;
const TAG_FILTER = 'interviste-conf26';
const API_BASE_URL = 'https://garr.tv/api/v1/videos';
const SITE_BASE_URL = 'https://garr.tv';
const PAGE_SIZE = 100; // massimo consentito dall'API PeerTube per singola richiesta
// True se MAX_VIDEOS non rappresenta un limite valido => mostra tutti i video
function shouldFetchAll() {
return !(Number.isInteger(MAX_VIDEOS) && MAX_VIDEOS > 0);
}
// Escape dei caratteri speciali HTML (testo e attributi)
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(//g, '>');
}
// Funzione per recuperare i video (gestisce limite e paginazione)
async function fetchRecentVideos() {
const limit = shouldFetchAll() ? Infinity : MAX_VIDEOS;
const collected = [];
let start = 0;
let total = Infinity;
try {
while (collected.length < limit && start < total) {
const pageCount = Math.min(PAGE_SIZE, limit - collected.length);
const apiUrl = API_BASE_URL +
'?tagsOneOf=' + encodeURIComponent(TAG_FILTER) +
'&sort=-publishedAt' +
'&start=' + start +
'&count=' + pageCount;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error('Request failed with status: ' + response.status);
}
const data = await response.json();
total = data.total || 0;
const pageVideos = data.data || [];
if (pageVideos.length === 0) {
break;
}
collected.push.apply(collected, pageVideos);
start += pageVideos.length;
}
return processVideoData(collected);
} catch (error) {
console.error('Error fetching videos:', error);
return [];
}
}
// Funzione per processare i dati dei video
function processVideoData(videoList) {
return videoList.map(function (video) {
// Usa embedPath / previewPath forniti dall'API (più robusti di un URL costruito a mano)
const embedUrl = video.embedPath
? SITE_BASE_URL + video.embedPath
: SITE_BASE_URL + '/videos/embed/' + video.uuid;
const thumbnailPath = video.previewPath || video.thumbnailPath || '';
const thumbnailUrl = thumbnailPath ? SITE_BASE_URL + thumbnailPath : '';
return {
title: video.name || '',
videoSrc: embedUrl,
thumbnailUrl: thumbnailUrl,
publishedAt: video.originallyPublishedAt || video.publishedAt
};
});
}
// Funzione per creare una card video (facade: anteprima + tasto play, l'iframe si carica al click)
function createVideoCard(video) {
const safeTitle = escapeHtml(video.title);
const safeEmbed = escapeHtml(video.videoSrc);
const safeThumb = escapeHtml(video.thumbnailUrl);
const thumbStyle = safeThumb
? ' style="background-image:url(\'' + safeThumb + '\');"'
: '';
return '
' +
'
' +
'
' + safeTitle + '
' +
'
' +
'' +
'' +
'' +
'
' +
'
' +
'
';
}
// Sostituisce la facade cliccata con l'iframe del player (carica un solo player, su gesto utente)
function handleFacadeClick(event) {
const facade = event.target.closest('.garr-video-facade');
if (!facade) {
return;
}
const embedUrl = facade.getAttribute('data-embed');
const title = facade.getAttribute('data-title') || '';
const iframe = document.createElement('iframe');
iframe.className = 'garr-video-frame';
iframe.setAttribute('title', title);
iframe.setAttribute('src', embedUrl + (embedUrl.indexOf('?') === -1 ? '?' : '&') + 'autoplay=1');
iframe.setAttribute('allow', 'autoplay; fullscreen');
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('frameborder', '0');
iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms');
facade.replaceWith(iframe);
}
// Funzione per renderizzare i video
function renderVideos(videos) {
const container = document.getElementById('garr-videos-container');
if (videos.length === 0) {
container.innerHTML = '
Nessun video trovato.
';
return;
}
const videosHTML = videos.map(createVideoCard).join('');
container.innerHTML = '
' +
videosHTML +
'
';
// Delego il click sul container: il player parte solo quando l'utente clicca
container.addEventListener('click', handleFacadeClick);
}
// Funzione principale
async function initVideoGallery() {
const container = document.getElementById('garr-videos-container');
container.innerHTML = '
Caricamento video...
';
const videos = await fetchRecentVideos();
renderVideos(videos);
}
// Avvia l'applicazione quando il DOM è pronto
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initVideoGallery);
} else {
initVideoGallery();
}
/* Stili della galleria (prefisso garr- per non collidere con la pagina ospite) */
.garr-video-facade {
position: relative;
display: block;
width: 100%;
padding: 0;
border: 0;
aspect-ratio: 16 / 9;
background-color: #000;
background-size: cover;
background-position: center;
cursor: pointer;
overflow: hidden;
}
.garr-play-icon {
position: absolute;
top: 50%;
left: 50%;
width: 68px;
height: 48px;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.6);
border-radius: 12px;
transition: background 0.2s ease;
}
.garr-play-icon::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-40%, -50%);
border-style: solid;
border-width: 11px 0 11px 19px;
border-color: transparent transparent transparent #fff;
}
.garr-video-facade:hover .garr-play-icon,
.garr-video-facade:focus .garr-play-icon {
background: #c00;
}
.garr-video-frame {
display: block;
width: 100%;
aspect-ratio: 16 / 9;
height: auto;
border: 0;
}
// Configurazione
// MAX_VIDEOS: numero intero positivo per limitare i video mostrati.
// Lascia null / undefined / -1 (o qualunque valore non intero-positivo)
// per mostrare TUTTI i video disponibili con il tag.
const MAX_VIDEOS = 6;
const TAG_FILTER = 'servizi';
const API_BASE_URL = 'https://garr.tv/api/v1/videos';
const SITE_BASE_URL = 'https://garr.tv';
const PAGE_SIZE = 100; // massimo consentito dall'API PeerTube per singola richiesta
// True se MAX_VIDEOS non rappresenta un limite valido => mostra tutti i video
function shouldFetchAll() {
return !(Number.isInteger(MAX_VIDEOS) && MAX_VIDEOS > 0);
}
// Escape dei caratteri speciali HTML (testo e attributi)
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(//g, '>');
}
// Funzione per recuperare i video (gestisce limite e paginazione)
async function fetchRecentVideos() {
const limit = shouldFetchAll() ? Infinity : MAX_VIDEOS;
const collected = [];
let start = 0;
let total = Infinity;
try {
while (collected.length < limit && start < total) {
const pageCount = Math.min(PAGE_SIZE, limit - collected.length);
const apiUrl = API_BASE_URL +
'?tagsOneOf=' + encodeURIComponent(TAG_FILTER) +
'&sort=-publishedAt' +
'&start=' + start +
'&count=' + pageCount;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error('Request failed with status: ' + response.status);
}
const data = await response.json();
total = data.total || 0;
const pageVideos = data.data || [];
if (pageVideos.length === 0) {
break;
}
collected.push.apply(collected, pageVideos);
start += pageVideos.length;
}
return processVideoData(collected);
} catch (error) {
console.error('Error fetching videos:', error);
return [];
}
}
// Funzione per processare i dati dei video
function processVideoData(videoList) {
return videoList.map(function (video) {
// Usa embedPath / previewPath forniti dall'API (più robusti di un URL costruito a mano)
const embedUrl = video.embedPath
? SITE_BASE_URL + video.embedPath
: SITE_BASE_URL + '/videos/embed/' + video.uuid;
const thumbnailPath = video.previewPath || video.thumbnailPath || '';
const thumbnailUrl = thumbnailPath ? SITE_BASE_URL + thumbnailPath : '';
return {
title: video.name || '',
videoSrc: embedUrl,
thumbnailUrl: thumbnailUrl,
publishedAt: video.originallyPublishedAt || video.publishedAt
};
});
}
// Funzione per creare una card video (facade: anteprima + tasto play, l'iframe si carica al click)
function createVideoCard(video) {
const safeTitle = escapeHtml(video.title);
const safeEmbed = escapeHtml(video.videoSrc);
const safeThumb = escapeHtml(video.thumbnailUrl);
const thumbStyle = safeThumb
? ' style="background-image:url(\'' + safeThumb + '\');"'
: '';
return '
' +
'
' +
'
' + safeTitle + '
' +
'
' +
'' +
'' +
'' +
'
' +
'
' +
'
';
}
// Sostituisce la facade cliccata con l'iframe del player (carica un solo player, su gesto utente)
function handleFacadeClick(event) {
const facade = event.target.closest('.garr-video-facade');
if (!facade) {
return;
}
const embedUrl = facade.getAttribute('data-embed');
const title = facade.getAttribute('data-title') || '';
const iframe = document.createElement('iframe');
iframe.className = 'garr-video-frame';
iframe.setAttribute('title', title);
iframe.setAttribute('src', embedUrl + (embedUrl.indexOf('?') === -1 ? '?' : '&') + 'autoplay=1');
iframe.setAttribute('allow', 'autoplay; fullscreen');
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('frameborder', '0');
iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms');
facade.replaceWith(iframe);
}
// Funzione per renderizzare i video
function renderVideos(videos) {
const container = document.getElementById('garr-videos-container');
if (videos.length === 0) {
container.innerHTML = '
Nessun video trovato.
';
return;
}
const videosHTML = videos.map(createVideoCard).join('');
container.innerHTML = '
' +
videosHTML +
'
';
// Delego il click sul container: il player parte solo quando l'utente clicca
container.addEventListener('click', handleFacadeClick);
}
// Funzione principale
async function initVideoGallery() {
const container = document.getElementById('garr-videos-container');
container.innerHTML = '
Caricamento video...
';
const videos = await fetchRecentVideos();
renderVideos(videos);
}
// Avvia l'applicazione quando il DOM è pronto
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initVideoGallery);
} else {
initVideoGallery();
}