Skip to content

Commit

Permalink
Merge pull request #42 from fearnlj01/config-tweak
Browse files Browse the repository at this point in the history
Add sortable checkboxes for description source
  • Loading branch information
revam authored Apr 13, 2024
2 parents 985d858 + e167ba8 commit 00705a3
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 62 deletions.
12 changes: 9 additions & 3 deletions Shokofin/Configuration/PluginConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,14 @@ public virtual string PrettyUrl
public bool MarkSpecialsWhenGrouped { get; set; }

/// <summary>
/// The description source. This will be replaced in the future.
/// The collection of providers for descriptions. Replaces the former `DescriptionSource`.
/// </summary>
public TextSourceType DescriptionSource { get; set; }
public TextSourceType[] DescriptionSourceList { get; set; }

/// <summary>
/// The prioritisation order of source providers for description sources.
/// </summary>
public TextSourceType[] DescriptionSourceOrder { get; set; }

/// <summary>
/// Clean up links within the AniDB description for entries.
Expand Down Expand Up @@ -286,7 +291,8 @@ public PluginConfiguration()
TitleMainType = DisplayLanguageType.Default;
TitleAlternateType = DisplayLanguageType.Origin;
TitleAllowAny = false;
DescriptionSource = TextSourceType.Default;
DescriptionSourceList = new[] { TextSourceType.AniDb, TextSourceType.TvDb, TextSourceType.TMDB };
DescriptionSourceOrder = new[] { TextSourceType.AniDb, TextSourceType.TvDb, TextSourceType.TMDB };
VirtualFileSystem = true;
VirtualFileSystemThreads = 10;
UseGroupsForShows = false;
Expand Down
94 changes: 91 additions & 3 deletions Shokofin/Configuration/configController.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,56 @@ const Messages = {

// Convert it back into an array.
return Array.from(filteredSet).sort((a, b) => a - b);
}

function adjustSortableListElement(element) {
const btnSortable = element.querySelector(".btnSortable");
const inner = btnSortable.querySelector(".material-icons");

if (element.previousElementSibling) {
btnSortable.title = "Up";
btnSortable.classList.add("btnSortableMoveUp");
inner.classList.add("keyboard_arrow_up");

btnSortable.classList.remove("btnSortableMoveDown");
inner.classList.remove("keyboard_arrow_down");
} else {
btnSortable.title = "Down";
btnSortable.classList.add("btnSortableMoveDown");
inner.classList.add("keyboard_arrow_down");

btnSortable.classList.remove("btnSortableMoveUp");
inner.classList.remove("keyboard_arrow_up");
}
}

/** @param {PointerEvent} event */
function onSortableContainerClick(event) {
const parentWithClass = (element, className) => {
return (element.parentElement.classList.contains(className)) ? element.parentElement : null;
}
const btnSortable = parentWithClass(event.target, "btnSortable");
if (btnSortable) {
const listItem = parentWithClass(btnSortable, "sortableOption");
const list = parentWithClass(listItem, "paperList");
if (btnSortable.classList.contains("btnSortableMoveDown")) {
const next = listItem.nextElementSibling;
if (next) {
listItem.parentElement.removeChild(listItem);
next.parentElement.insertBefore(listItem, next.nextSibling);
}
} else {
const prev = listItem.previousElementSibling;
if (prev) {
listItem.parentElement.removeChild(listItem);
prev.parentElement.insertBefore(listItem, prev);
}
}

for (const option of list.querySelectorAll(".sortableOption")) {
adjustSortableListElement(option)
};
}
}

async function loadUserConfig(form, userId, config) {
Expand Down Expand Up @@ -227,7 +277,7 @@ async function defaultSubmit(form) {
config.TitleAllowAny = form.querySelector("#TitleAllowAny").checked;
config.TitleAddForMultipleEpisodes = form.querySelector("#TitleAddForMultipleEpisodes").checked;
config.MarkSpecialsWhenGrouped = form.querySelector("#MarkSpecialsWhenGrouped").checked;
config.DescriptionSource = form.querySelector("#DescriptionSource").value;
setDescriptionSourcesIntoConfig(form, config);
config.SynopsisCleanLinks = form.querySelector("#CleanupAniDBDescriptions").checked;
config.SynopsisCleanMultiEmptyLines = form.querySelector("#CleanupAniDBDescriptions").checked;
config.SynopsisCleanMiscLines = form.querySelector("#MinimalAniDBDescriptions").checked;
Expand Down Expand Up @@ -417,7 +467,7 @@ async function syncSettings(form) {
config.TitleAllowAny = form.querySelector("#TitleAllowAny").checked;
config.TitleAddForMultipleEpisodes = form.querySelector("#TitleAddForMultipleEpisodes").checked;
config.MarkSpecialsWhenGrouped = form.querySelector("#MarkSpecialsWhenGrouped").checked;
config.DescriptionSource = form.querySelector("#DescriptionSource").value;
setDescriptionSourcesIntoConfig(form, config);
config.SynopsisCleanLinks = form.querySelector("#CleanupAniDBDescriptions").checked;
config.SynopsisCleanMultiEmptyLines = form.querySelector("#CleanupAniDBDescriptions").checked;
config.SynopsisCleanMiscLines = form.querySelector("#MinimalAniDBDescriptions").checked;
Expand Down Expand Up @@ -689,6 +739,8 @@ export default function (page) {
}
});

form.querySelector("#descriptionSourceList").addEventListener("click", onSortableContainerClick);

page.addEventListener("viewshow", async function () {
Dashboard.showLoadingMsg();
try {
Expand All @@ -707,7 +759,7 @@ export default function (page) {
form.querySelector("#TitleAllowAny").checked = config.TitleAllowAny;
form.querySelector("#TitleAddForMultipleEpisodes").checked = config.TitleAddForMultipleEpisodes != null ? config.TitleAddForMultipleEpisodes : true;
form.querySelector("#MarkSpecialsWhenGrouped").checked = config.MarkSpecialsWhenGrouped;
form.querySelector("#DescriptionSource").value = config.DescriptionSource;
await setDescriptionSourcesFromConfig(form, config);
form.querySelector("#CleanupAniDBDescriptions").checked = config.SynopsisCleanMultiEmptyLines || config.SynopsisCleanLinks;
form.querySelector("#MinimalAniDBDescriptions").checked = config.SynopsisRemoveSummary || config.SynopsisCleanMiscLines;

Expand Down Expand Up @@ -819,3 +871,39 @@ export default function (page) {
return false;
});
}

function setDescriptionSourcesIntoConfig(form, config) {
const descriptionElements = form.querySelectorAll(`#descriptionSourceList .chkDescriptionSource`);
config.DescriptionSourceList = Array.prototype.filter.call(descriptionElements,
(el) => el.checked)
.map((el) => el.dataset.descriptionsource);

config.DescriptionSourceOrder = Array.prototype.map.call(descriptionElements,
(el) => el.dataset.descriptionsource
);
}

async function setDescriptionSourcesFromConfig(form, config) {
const list = form.querySelector("#descriptionSourceList .checkboxList");
const listItems = list.querySelectorAll('.listItem');

for (const item of listItems) {
const source = item.dataset.descriptionsource;
if (config.DescriptionSourceList.includes(source)) {
item.querySelector(".chkDescriptionSource").checked = true;
}
if (config.DescriptionSourceOrder.includes(source)) {
list.removeChild(item); // This is safe to be removed as we can re-add it in the next loop
}
}

for (const source of config.DescriptionSourceOrder) {
const targetElement = Array.prototype.find.call(listItems, (el) => el.dataset.descriptionsource === source);
if (targetElement) {
list.append(targetElement);
}
}
for (const option of list.querySelectorAll(".sortableOption")) {
adjustSortableListElement(option)
};
}
32 changes: 21 additions & 11 deletions Shokofin/Configuration/configPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,26 @@ <h3>Metadata Settings</h3>
</label>
<div class="fieldDescription checkboxFieldDescription">Add a number to the title of each specials episode</div>
</div>
<div class="selectContainer selectContainer-withDescription">
<label class="selectLabel" for="DescriptionSource">Description source:</label>
<select is="emby-select" id="DescriptionSource" name="DescriptionSource" class="emby-select-withcolor emby-select">
<option value="Default">Use default source for selected Series/Season grouping</option>
<option value="OnlyAniDb">Only use AniDB</option>
<option value="PreferAniDb">Prefer AniDB if available, otherwise use TvDB/TMDB</option>
<option value="OnlyOther">Only use TvDB/TMDB</option>
<option value="PreferOther">Prefer TvDB/TMDB if available, otherwise use AniDB</option>
</select>
<div class="fieldDescription selectFieldDescription">How to select the description to use for each item.</div>
<div id="descriptionSourceList" style="margin-bottom: 2em;">
<h3 class="checkboxListLabel">Description source:</h3>
<div class="checkboxList paperList checkboxList-paperList">
<div class="listItem descriptionSourceItem sortableOption" data-descriptionsource="AniDb">
<label class="listItemCheckboxContainer"><input class="chkDescriptionSource" type="checkbox" is="emby-checkbox" data-descriptionsource="AniDb"><span></span></label>
<div class="listItemBody"><h3 class="listItemBodyText">AniDB</h3></div>
<button type="button" is="paper-icon-button-light" title="Down" class="btnSortableMoveDown btnSortable"><span class="material-icons keyboard_arrow_down" aria-hidden="true"></span></button>
</div>
<div class="listItem descriptionSourceItem sortableOption" data-descriptionsource="TvDb">
<label class="listItemCheckboxContainer"><input class="chkDescriptionSource" type="checkbox" is="emby-checkbox" data-descriptionsource="TvDb"><span></span></label>
<div class="listItemBody"><h3 class="listItemBodyText">TVDB</h3></div>
<button type="button" is="paper-icon-button-light" title="Up" class="btnSortableMoveUp btnSortable"><span class="material-icons keyboard_arrow_up" aria-hidden="true"></span></button>
</div>
<div class="listItem descriptionSourceItem sortableOption" data-descriptionsource="TMDB">
<label class="listItemCheckboxContainer"><input class="chkDescriptionSource" type="checkbox" is="emby-checkbox" data-descriptionsource="TMDB"><span></span></label>
<div class="listItemBody"><h3 class="listItemBodyText">TMDB</h3></div>
<button type="button" is="paper-icon-button-light" title="Up" class="btnSortableMoveUp btnSortable"><span class="material-icons keyboard_arrow_up" aria-hidden="true"></span></button>
</div>
</div>
<div class="fieldDescription">The metadata providers to use as the source of episode/series/season descriptions.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
Expand Down Expand Up @@ -109,7 +119,7 @@ <h3>Metadata Settings</h3>
<input is="emby-checkbox" type="checkbox" id="MinimalAniDBDescriptions" />
<span>Minimalistic AniDB descriptions</span>
</label>
<div class="fieldDescription checkboxFieldDescription">Trim any lines starting with '* ', '-- ', '~ ', 'Note', 'Source', and/or 'Summery'</div>
<div class="fieldDescription checkboxFieldDescription">Trim any lines starting with '* ', '-- ', '~ ', 'Note', 'Source', and/or 'Summary'</div>
</div>
<button is="emby-button" type="submit" name="settings" class="raised button-submit block emby-button">
<span>Save</span>
Expand Down
88 changes: 43 additions & 45 deletions Shokofin/Utils/Text.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,19 @@ public static class Text
/// </summary>
public enum TextSourceType {
/// <summary>
/// Use the default source for the current series grouping.
/// Use data from AniDB.
/// </summary>
Default = 1,

/// <summary>
/// Only use AniDb, or null if no data is available.
/// </summary>
OnlyAniDb = 2,
AniDb = 0,

/// <summary>
/// Prefer the AniDb data, but use the other provider if there is no
/// AniDb data available.
/// Use data from TvDB.
/// </summary>
PreferAniDb = 3,
TvDb = 1,

/// <summary>
/// Prefer the other provider (e.g. TvDB/TMDB)
/// Use data from TMDB
/// </summary>
PreferOther = 4,

/// <summary>
/// Only use the other provider, or null if no data is available.
/// </summary>
OnlyOther = 5,
TMDB = 2
}

/// <summary>
Expand Down Expand Up @@ -149,40 +138,49 @@ public enum DisplayTitleType {
FullTitle = 3,
}

public static string? GetDescription(ShowInfo show)
public static string GetDescription(ShowInfo show)
=> GetDescription(show.DefaultSeason);

public static string? GetDescription(SeasonInfo season)
=> GetDescription(season.AniDB.Description, season.TvDB?.Description);
public static string GetDescription(SeasonInfo season)
=> GetDescription(new Dictionary<TextSourceType, string>() {
{TextSourceType.AniDb, season.AniDB.Description ?? string.Empty},
{TextSourceType.TvDb, season.TvDB?.Description ?? string.Empty},
});

public static string? GetDescription(EpisodeInfo episode)
=> GetDescription(episode.AniDB.Description, episode.TvDB?.Description);
public static string GetDescription(EpisodeInfo episode)
=> GetDescription(new Dictionary<TextSourceType, string>() {
{TextSourceType.AniDb, episode.AniDB.Description ?? string.Empty},
{TextSourceType.TvDb, episode.TvDB?.Description ?? string.Empty},
});

public static string? GetDescription(IEnumerable<EpisodeInfo> episodeList)
=> JoinText(episodeList.Select(episode => GetDescription(episode)));
public static string GetDescription(IEnumerable<EpisodeInfo> episodeList)
=> JoinText(episodeList.Select(episode => GetDescription(episode))) ?? string.Empty;

private static string GetDescription(string aniDbDescription, string? otherDescription)
private static string GetDescription(Dictionary<TextSourceType, string> descriptions)
{
string overview;
switch (Plugin.Instance.Configuration.DescriptionSource) {
default:
case TextSourceType.PreferAniDb:
overview = SanitizeTextSummary(aniDbDescription);
if (string.IsNullOrEmpty(overview))
goto case TextSourceType.OnlyOther;
break;
case TextSourceType.PreferOther:
overview = otherDescription ?? string.Empty;
if (string.IsNullOrEmpty(overview))
goto case TextSourceType.OnlyAniDb;
break;
case TextSourceType.OnlyAniDb:
overview = SanitizeTextSummary(aniDbDescription);
break;
case TextSourceType.OnlyOther:
overview = otherDescription ?? string.Empty;
break;
var overview = string.Empty;

var providerOrder = Plugin.Instance.Configuration.DescriptionSourceOrder;
var providers = Plugin.Instance.Configuration.DescriptionSourceList;

if (providers.Length == 0) {
return overview; // This is what they want if everything is unticked...
}

foreach (var provider in providerOrder.Where(provider => providers.Contains(provider)))
{
if (!string.IsNullOrEmpty(overview)) {
return overview;
}

overview = provider switch
{
TextSourceType.AniDb => descriptions.TryGetValue(TextSourceType.AniDb, out var desc) ? SanitizeTextSummary(desc) : string.Empty,
TextSourceType.TvDb => descriptions.TryGetValue(TextSourceType.TvDb, out var desc) ? desc : string.Empty,
_ => string.Empty
};
}

return overview;
}

Expand Down Expand Up @@ -217,7 +215,7 @@ public static (string?, string?) GetEpisodeTitles(IEnumerable<Title> seriesTitle
=> GetTitles(seriesTitles, episodeTitles, null, episodeTitle, DisplayTitleType.SubTitle, metadataLanguage);

public static (string?, string?) GetSeriesTitles(IEnumerable<Title> seriesTitles, string seriesTitle, string metadataLanguage)
=> GetTitles(seriesTitles, null, seriesTitle, null, DisplayTitleType.MainTitle, metadataLanguage);
=> GetTitles(seriesTitles, null, seriesTitle, null, DisplayTitleType.MainTitle, metadataLanguage);

public static (string?, string?) GetMovieTitles(IEnumerable<Title> seriesTitles, IEnumerable<Title> episodeTitles, string seriesTitle, string episodeTitle, string metadataLanguage)
=> GetTitles(seriesTitles, episodeTitles, seriesTitle, episodeTitle, DisplayTitleType.FullTitle, metadataLanguage);
Expand Down

0 comments on commit 00705a3

Please sign in to comment.