ShareLinks: keep player back button, disable in-item links, hide add-to-playlist, generalize guest plugin hiding
Hiding .headerBackButton for guests also removed the video player's back button (same class), so a guest could not return from playback to the movie page. Stop hiding that class; keep home/drawer/search hidden. Cast, studio, genre and tag links on the detail page now have pointer-events disabled for guests so they stay visible but inert (clicking Studio no longer spins on a page the guest cannot load), and the add-to-playlist/add-to-collection actions are hidden. Add a GuestHiddenSelectors config field (comma-separated CSS selectors, surfaced through GuestState since guests cannot read plugin config) so other plugins injected UI can be suppressed for guest sessions without code changes; defaults to hiding the AI Search elements.
Cette révision appartient à :
@@ -91,6 +91,8 @@ public sealed class ShareLinkGuestStateDto
|
|||||||
public DateTimeOffset? ExpiresAtUtc { get; set; }
|
public DateTimeOffset? ExpiresAtUtc { get; set; }
|
||||||
|
|
||||||
public bool LockdownEnabled { get; set; }
|
public bool LockdownEnabled { get; set; }
|
||||||
|
|
||||||
|
public string? HiddenSelectors { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>ShareLinks API surface.</summary>
|
/// <summary>ShareLinks API surface.</summary>
|
||||||
@@ -291,7 +293,8 @@ public sealed class ShareLinksController : ControllerBase
|
|||||||
AllowedItemId = match.ItemId,
|
AllowedItemId = match.ItemId,
|
||||||
ShareId = match.Id,
|
ShareId = match.Id,
|
||||||
ExpiresAtUtc = match.ExpiresAtUtc,
|
ExpiresAtUtc = match.ExpiresAtUtc,
|
||||||
LockdownEnabled = config.GuestModeLockdownEnabled
|
LockdownEnabled = config.GuestModeLockdownEnabled,
|
||||||
|
HiddenSelectors = config.GuestHiddenSelectors
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +302,8 @@ public sealed class ShareLinksController : ControllerBase
|
|||||||
return Ok(new ShareLinkGuestStateDto
|
return Ok(new ShareLinkGuestStateDto
|
||||||
{
|
{
|
||||||
IsGuest = false,
|
IsGuest = false,
|
||||||
LockdownEnabled = config.GuestModeLockdownEnabled
|
LockdownEnabled = config.GuestModeLockdownEnabled,
|
||||||
|
HiddenSelectors = config.GuestHiddenSelectors
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,4 +41,12 @@ public class PluginConfiguration : BasePluginConfiguration
|
|||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether guest-mode lockdown is enabled.</summary>
|
/// <summary>Gets or sets a value indicating whether guest-mode lockdown is enabled.</summary>
|
||||||
public bool GuestModeLockdownEnabled { get; set; } = true;
|
public bool GuestModeLockdownEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a comma-separated list of CSS selectors that are hidden from guest
|
||||||
|
/// sessions in the web client. Used to suppress other plugins' injected UI (search
|
||||||
|
/// bars, floating buttons) so a guest only sees the shared title. Defaults to the
|
||||||
|
/// AI Search plugin's elements.
|
||||||
|
/// </summary>
|
||||||
|
public string GuestHiddenSelectors { get; set; } = ".ais-fab,.ais-overlay,#ais-styles";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,6 +123,11 @@
|
|||||||
<input is="emby-input" type="text" id="GuestUsernamePrefix" label="Guest username prefix" />
|
<input is="emby-input" type="text" id="GuestUsernamePrefix" label="Guest username prefix" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sl-field inputContainer" style="grid-column: 1 / -1;">
|
||||||
|
<input is="emby-input" type="text" id="GuestHiddenSelectors" label="Guest hidden selectors (CSS, comma-separated)" />
|
||||||
|
<div class="fieldDescription">Elements hidden from guest sessions, e.g. other plugins' buttons. Default hides the AI Search UI.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sl-field inputContainer">
|
<div class="sl-field inputContainer">
|
||||||
<input is="emby-input" type="number" id="CleanupIntervalMinutes" label="Cleanup interval (minutes)" min="5" max="10080" />
|
<input is="emby-input" type="number" id="CleanupIntervalMinutes" label="Cleanup interval (minutes)" min="5" max="10080" />
|
||||||
</div>
|
</div>
|
||||||
@@ -207,6 +212,7 @@
|
|||||||
page.querySelector('#MaxExpiryHours').value = cfg.MaxExpiryHours || 720;
|
page.querySelector('#MaxExpiryHours').value = cfg.MaxExpiryHours || 720;
|
||||||
page.querySelector('#PublicBaseUrlOverride').value = cfg.PublicBaseUrlOverride || '';
|
page.querySelector('#PublicBaseUrlOverride').value = cfg.PublicBaseUrlOverride || '';
|
||||||
page.querySelector('#GuestUsernamePrefix').value = cfg.GuestUsernamePrefix || 'share-';
|
page.querySelector('#GuestUsernamePrefix').value = cfg.GuestUsernamePrefix || 'share-';
|
||||||
|
page.querySelector('#GuestHiddenSelectors').value = cfg.GuestHiddenSelectors != null ? cfg.GuestHiddenSelectors : '.ais-fab,.ais-overlay,#ais-styles';
|
||||||
page.querySelector('#AllowTranscoding').checked = cfg.AllowTranscoding !== false;
|
page.querySelector('#AllowTranscoding').checked = cfg.AllowTranscoding !== false;
|
||||||
page.querySelector('#AllowRemuxing').checked = cfg.AllowRemuxing !== false;
|
page.querySelector('#AllowRemuxing').checked = cfg.AllowRemuxing !== false;
|
||||||
page.querySelector('#CleanupIntervalMinutes').value = cfg.CleanupIntervalMinutes || 60;
|
page.querySelector('#CleanupIntervalMinutes').value = cfg.CleanupIntervalMinutes || 60;
|
||||||
@@ -387,6 +393,7 @@
|
|||||||
cfg.MaxExpiryHours = parseInt(page.querySelector('#MaxExpiryHours').value, 10) || 720;
|
cfg.MaxExpiryHours = parseInt(page.querySelector('#MaxExpiryHours').value, 10) || 720;
|
||||||
cfg.PublicBaseUrlOverride = page.querySelector('#PublicBaseUrlOverride').value.trim();
|
cfg.PublicBaseUrlOverride = page.querySelector('#PublicBaseUrlOverride').value.trim();
|
||||||
cfg.GuestUsernamePrefix = page.querySelector('#GuestUsernamePrefix').value.trim() || 'share-';
|
cfg.GuestUsernamePrefix = page.querySelector('#GuestUsernamePrefix').value.trim() || 'share-';
|
||||||
|
cfg.GuestHiddenSelectors = page.querySelector('#GuestHiddenSelectors').value;
|
||||||
cfg.AllowTranscoding = page.querySelector('#AllowTranscoding').checked;
|
cfg.AllowTranscoding = page.querySelector('#AllowTranscoding').checked;
|
||||||
cfg.AllowRemuxing = page.querySelector('#AllowRemuxing').checked;
|
cfg.AllowRemuxing = page.querySelector('#AllowRemuxing').checked;
|
||||||
cfg.CleanupIntervalMinutes = parseInt(page.querySelector('#CleanupIntervalMinutes').value, 10) || 60;
|
cfg.CleanupIntervalMinutes = parseInt(page.querySelector('#CleanupIntervalMinutes').value, 10) || 60;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
var pluginId = '68540b76-ee74-436d-85ff-2abc884bbea6';
|
var pluginId = '68540b76-ee74-436d-85ff-2abc884bbea6';
|
||||||
var copyLabel = 'Copy Stream URL';
|
var copyLabel = 'Copy Stream URL';
|
||||||
var actionLabel = 'Create guest link';
|
var actionLabel = 'Create guest link';
|
||||||
var clientVersion = '1.0.0-ui-modal-7';
|
var clientVersion = '1.0.0-ui-modal-8';
|
||||||
var allowedItemStorageKey = 'sharelinks.allowedItemId';
|
var allowedItemStorageKey = 'sharelinks.allowedItemId';
|
||||||
var guestClassName = 'sharelinks-guest';
|
var guestClassName = 'sharelinks-guest';
|
||||||
var hiddenAttr = 'data-sharelinks-hidden';
|
var hiddenAttr = 'data-sharelinks-hidden';
|
||||||
@@ -172,6 +172,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureGuestStyle();
|
ensureGuestStyle();
|
||||||
|
ensurePluginHideStyle(context.hiddenSelectors);
|
||||||
hideGuestControls();
|
hideGuestControls();
|
||||||
|
|
||||||
// UX-only lockdown: the guest user's real access boundary is still the
|
// UX-only lockdown: the guest user's real access boundary is still the
|
||||||
@@ -202,7 +203,8 @@
|
|||||||
allowedItemId: extractAllowedItemId(state) || sessionStorage.getItem(allowedItemStorageKey) || null,
|
allowedItemId: extractAllowedItemId(state) || sessionStorage.getItem(allowedItemStorageKey) || null,
|
||||||
username: username,
|
username: username,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
lockdownEnabled: lockdownEnabled
|
lockdownEnabled: lockdownEnabled,
|
||||||
|
hiddenSelectors: (state && (state.HiddenSelectors || state.hiddenSelectors)) || ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,10 +229,43 @@
|
|||||||
var style = document.createElement('style');
|
var style = document.createElement('style');
|
||||||
style.id = 'ShareLinksGuestStyle';
|
style.id = 'ShareLinksGuestStyle';
|
||||||
style.textContent = 'body.' + guestClassName + ' [' + hiddenAttr + '="1"],'
|
style.textContent = 'body.' + guestClassName + ' [' + hiddenAttr + '="1"],'
|
||||||
+ ' body.' + guestClassName + ' .headerBackButton,'
|
|
||||||
+ ' body.' + guestClassName + ' .headerHomeButton,'
|
+ ' body.' + guestClassName + ' .headerHomeButton,'
|
||||||
+ ' body.' + guestClassName + ' .mainDrawerButton,'
|
+ ' body.' + guestClassName + ' .mainDrawerButton,'
|
||||||
+ ' body.' + guestClassName + ' .headerSearchButton { display: none !important; }';
|
+ ' body.' + guestClassName + ' .headerSearchButton,'
|
||||||
|
+ ' body.' + guestClassName + ' [data-action="addtoplaylist"],'
|
||||||
|
+ ' body.' + guestClassName + ' [data-action="addtocollection"] { display: none !important; }'
|
||||||
|
+ ' body.' + guestClassName + ' .card,'
|
||||||
|
+ ' body.' + guestClassName + ' #castCollapsible a,'
|
||||||
|
+ ' body.' + guestClassName + ' .detailsGroupItem a,'
|
||||||
|
+ ' body.' + guestClassName + ' .genresGroup a,'
|
||||||
|
+ ' body.' + guestClassName + ' .studiosGroup a,'
|
||||||
|
+ ' body.' + guestClassName + ' .mediaInfoItem a { pointer-events: none !important; cursor: default !important; }';
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensurePluginHideStyle(selectors) {
|
||||||
|
var value = String(selectors || '').split(',').map(function (part) {
|
||||||
|
return part.trim();
|
||||||
|
}).filter(Boolean);
|
||||||
|
var existing = document.getElementById('ShareLinksPluginHideStyle');
|
||||||
|
if (value.length === 0) {
|
||||||
|
if (existing) {
|
||||||
|
existing.remove();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var css = value.join(', ') + ' { display: none !important; }';
|
||||||
|
if (existing) {
|
||||||
|
if (existing.textContent !== css) {
|
||||||
|
existing.textContent = css;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var style = document.createElement('style');
|
||||||
|
style.id = 'ShareLinksPluginHideStyle';
|
||||||
|
style.textContent = css;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Référencer dans un nouveau ticket
Bloquer un utilisateur