mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Auto-Organize: Added feature to remember/persist series matching in manual organization dialog #2
When a filename cannot be auto-matched to an existing series name, the organization must be performed manually. Unfortunately not just once, but again and again for each episode coming in. This change proposes a simple but solid method to optionally persist the matching condition from within the manual organization dialog. This approach will make Emby "learn" how to organize files in the future without user interaction.
This commit is contained in:
parent
4c2a7ed02d
commit
a252b74db2
8 changed files with 196 additions and 1 deletions
|
@ -12,6 +12,7 @@
|
||||||
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
||||||
<a href="#" data-role="button" class="ui-btn-active">${TabActivityLog}</a>
|
<a href="#" data-role="button" class="ui-btn-active">${TabActivityLog}</a>
|
||||||
<a href="autoorganizetv.html" data-role="button">${TabTV}</a>
|
<a href="autoorganizetv.html" data-role="button">${TabTV}</a>
|
||||||
|
<a href="autoorganizesmart.html" data-role="button">${TabSmartMatch}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin: -25px 0 1em; text-align: right;">
|
<div style="margin: -25px 0 1em; text-align: right;">
|
||||||
|
@ -80,6 +81,11 @@
|
||||||
<input id="txtEndingEpisode" type="number" pattern="[0-9]*" min="0" />
|
<input id="txtEndingEpisode" type="number" pattern="[0-9]*" min="0" />
|
||||||
<div class="fieldDescription">${LabelEndingEpisodeNumberHelp}</div>
|
<div class="fieldDescription">${LabelEndingEpisodeNumberHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin: 1em 0;">
|
||||||
|
<label>${TabSmartMatch}</label>
|
||||||
|
<input type="checkbox" id="chkRememberCorrection" name="chkRememberCorrection" data-mini="true" />
|
||||||
|
<label for="chkRememberCorrection">${LabelOrganizeSmartMatchOption} '<span class="extractedName" />' </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button type="submit" data-theme="b" data-icon="check" data-mini="true">
|
<button type="submit" data-theme="b" data-icon="check" data-mini="true">
|
||||||
|
|
29
dashboard-ui/autoorganizesmart.html
Normal file
29
dashboard-ui/autoorganizesmart.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>${TitleAutoOrganize}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="libraryFileOrganizerSmartMatchPage" data-role="page" class="page type-interior organizePage" data-helpurl="https://github.com/MediaBrowser/Wiki/wiki/Auto-Organize" data-require="jqmtable,jqmpopup,scripts/autoorganizesmart,scripts/taskbutton,paperbuttonstyle,detailtablecss">
|
||||||
|
|
||||||
|
<div data-role="content">
|
||||||
|
<div class="content-primary">
|
||||||
|
|
||||||
|
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
||||||
|
<a href="autoorganizelog.html" data-role="button">${TabActivityLog}</a>
|
||||||
|
<a href="autoorganizetv.html" data-role="button">${TabTV}</a>
|
||||||
|
<a href="#" data-role="button" class="ui-btn-active">${TabSmartMatch}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="readOnlyContent">
|
||||||
|
<p>${TabSmartMatchInfo}</p>
|
||||||
|
<div class="divMatchInfos"></div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -11,6 +11,7 @@
|
||||||
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
<div data-role="controlgroup" data-type="horizontal" class="localnav" data-mini="true">
|
||||||
<a href="autoorganizelog.html" data-role="button">${TabActivityLog}</a>
|
<a href="autoorganizelog.html" data-role="button">${TabActivityLog}</a>
|
||||||
<a href="#" data-role="button" class="ui-btn-active">${TabTV}</a>
|
<a href="#" data-role="button" class="ui-btn-active">${TabTV}</a>
|
||||||
|
<a href="autoorganizesmart.html" data-role="button">${TabSmartMatch}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="libraryFileOrganizerForm">
|
<form class="libraryFileOrganizerForm">
|
||||||
|
|
|
@ -1129,6 +1129,29 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.getSmartMatchInfos = function (options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var url = self.getUrl("Library/FileOrganizationSmartMatch", options);
|
||||||
|
|
||||||
|
return self.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: url,
|
||||||
|
dataType: "json"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.deleteSmartMatchEntry = function (id, options) {
|
||||||
|
|
||||||
|
var url = self.getUrl("Library/FileOrganizationSmartMatch/" + id + "/Delete", options || {});
|
||||||
|
|
||||||
|
return self.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
self.getLiveTvSeriesTimer = function (id) {
|
self.getLiveTvSeriesTimer = function (id) {
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
|
|
@ -76,6 +76,9 @@
|
||||||
$('#txtEpisode', popup).val(item.ExtractedEpisodeNumber);
|
$('#txtEpisode', popup).val(item.ExtractedEpisodeNumber);
|
||||||
$('#txtEndingEpisode', popup).val(item.ExtractedEndingEpisodeNumber);
|
$('#txtEndingEpisode', popup).val(item.ExtractedEndingEpisodeNumber);
|
||||||
|
|
||||||
|
$('#chkRememberCorrection', popup).val(false);
|
||||||
|
$('.extractedName', popup).html(item.ExtractedName);
|
||||||
|
|
||||||
$('#hfResultId', popup).val(item.Id);
|
$('#hfResultId', popup).val(item.Id);
|
||||||
|
|
||||||
var seriesHtml = allSeries.map(function (s) {
|
var seriesHtml = allSeries.map(function (s) {
|
||||||
|
@ -146,7 +149,8 @@
|
||||||
SeriesId: $('#selectSeries', form).val(),
|
SeriesId: $('#selectSeries', form).val(),
|
||||||
SeasonNumber: $('#txtSeason', form).val(),
|
SeasonNumber: $('#txtSeason', form).val(),
|
||||||
EpisodeNumber: $('#txtEpisode', form).val(),
|
EpisodeNumber: $('#txtEpisode', form).val(),
|
||||||
EndingEpisodeNumber: $('#txtEndingEpisode', form).val()
|
EndingEpisodeNumber: $('#txtEndingEpisode', form).val(),
|
||||||
|
RememberCorrection: $('#chkRememberCorrection', form).checked()
|
||||||
};
|
};
|
||||||
|
|
||||||
ApiClient.performEpisodeOrganization(resultId, options).then(function () {
|
ApiClient.performEpisodeOrganization(resultId, options).then(function () {
|
||||||
|
|
126
dashboard-ui/scripts/autoorganizesmart.js
Normal file
126
dashboard-ui/scripts/autoorganizesmart.js
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
(function ($, document, window) {
|
||||||
|
|
||||||
|
var query = {
|
||||||
|
|
||||||
|
StartIndex: 0,
|
||||||
|
Limit: 100000
|
||||||
|
};
|
||||||
|
|
||||||
|
var currentResult;
|
||||||
|
|
||||||
|
function reloadList(page) {
|
||||||
|
|
||||||
|
Dashboard.showLoadingMsg();
|
||||||
|
|
||||||
|
ApiClient.getSmartMatchInfos(query).done(function (infos) {
|
||||||
|
|
||||||
|
currentResult = infos;
|
||||||
|
|
||||||
|
populateList(page, infos);
|
||||||
|
|
||||||
|
Dashboard.hideLoadingMsg();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateList(page, result) {
|
||||||
|
|
||||||
|
var infos = result.Items;
|
||||||
|
|
||||||
|
if (infos.length > 0) {
|
||||||
|
infos = infos.sort(function (a, b) {
|
||||||
|
|
||||||
|
a = a.OrganizerType + " " + a.Name;
|
||||||
|
b = b.OrganizerType + " " + b.Name;
|
||||||
|
|
||||||
|
if (a == b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a < b) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = "";
|
||||||
|
var currentType;
|
||||||
|
|
||||||
|
for (var i = 0, length = infos.length; i < length; i++) {
|
||||||
|
|
||||||
|
var info = infos[i];
|
||||||
|
|
||||||
|
if (info.OrganizerType != currentType) {
|
||||||
|
currentType = info.OrganizerType;
|
||||||
|
|
||||||
|
if (html.length > 0)
|
||||||
|
{
|
||||||
|
html += "</ul>";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "<h2>" + currentType + "</h2>";
|
||||||
|
|
||||||
|
html += '<ul data-role="listview" data-inset="true" data-auto-enhanced="false" data-split-icon="action">';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "<li data-role='list-divider'><h3 style='font-weight:bold'>" + info.Name + "</h3></li>";
|
||||||
|
|
||||||
|
for (var n = 0; n < info.MatchStrings.length; n++) {
|
||||||
|
html += "<li title='" + info.MatchStrings[n] + "'>";
|
||||||
|
|
||||||
|
html += "<a style='padding-top: 0.5em; padding-bottom: 0.5em'>";
|
||||||
|
|
||||||
|
html += "<p>" + info.MatchStrings[n] + "</p>";
|
||||||
|
|
||||||
|
html += "<a id='btnDeleteMatchEntry" + info.Id + "' class='btnDeleteMatchEntry' href='#' data-id='" + info.Id + "' data-matchstring='" + info.MatchStrings[n] + "' data-icon='delete'>" + Globalize.translate('ButtonDelete') + "</a>";
|
||||||
|
|
||||||
|
html += "</a>";
|
||||||
|
|
||||||
|
html += "</li>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</ul>";
|
||||||
|
|
||||||
|
$('.divMatchInfos', page).html(html).trigger('create');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).on('pageinit', "#libraryFileOrganizerSmartMatchPage", function () {
|
||||||
|
|
||||||
|
var page = this;
|
||||||
|
|
||||||
|
$('.divMatchInfos', page).on('click', '.btnDeleteMatchEntry', function () {
|
||||||
|
|
||||||
|
var button = this;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
|
||||||
|
MatchString: button.getAttribute('data-matchstring')
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiClient.deleteSmartMatchEntry(id, options).done(function () {
|
||||||
|
|
||||||
|
reloadList(page);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}).on('pageshowready', "#libraryFileOrganizerSmartMatchPage", function () {
|
||||||
|
|
||||||
|
var page = this;
|
||||||
|
|
||||||
|
Dashboard.showLoadingMsg();
|
||||||
|
|
||||||
|
reloadList(page);
|
||||||
|
|
||||||
|
}).on('pagebeforehide', "#libraryFileOrganizerSmartMatchPage", function () {
|
||||||
|
|
||||||
|
var page = this;
|
||||||
|
currentResult = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery, document, window);
|
|
@ -574,6 +574,8 @@
|
||||||
"LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable",
|
"LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable",
|
||||||
"TitleAutoOrganize": "Auto-Organize",
|
"TitleAutoOrganize": "Auto-Organize",
|
||||||
"TabActivityLog": "Activity Log",
|
"TabActivityLog": "Activity Log",
|
||||||
|
"TabSmartMatch": "Smart Match",
|
||||||
|
"TabSmartMatchInfo": "This page lists the match-strings for all library items which were added from within the Auto-Organize correction dialog",
|
||||||
"HeaderName": "Name",
|
"HeaderName": "Name",
|
||||||
"HeaderDate": "Date",
|
"HeaderDate": "Date",
|
||||||
"HeaderSource": "Source",
|
"HeaderSource": "Source",
|
||||||
|
@ -589,6 +591,7 @@
|
||||||
"LabelEpisodeNumber": "Episode number",
|
"LabelEpisodeNumber": "Episode number",
|
||||||
"LabelEndingEpisodeNumber": "Ending episode number:",
|
"LabelEndingEpisodeNumber": "Ending episode number:",
|
||||||
"LabelEndingEpisodeNumberHelp": "Only required for multi-episode files",
|
"LabelEndingEpisodeNumberHelp": "Only required for multi-episode files",
|
||||||
|
"LabelOrganizeSmartMatchOption": "In the future, organize all files into the selected series if the name contains",
|
||||||
"HeaderSupportTheTeam": "Support the Emby Team",
|
"HeaderSupportTheTeam": "Support the Emby Team",
|
||||||
"LabelSupportAmount": "Amount (USD)",
|
"LabelSupportAmount": "Amount (USD)",
|
||||||
"HeaderSupportTheTeamHelp": "Help ensure the continued development of this project by purchasing Emby Premiere. A portion of all income will be contributed to other free tools we depend on.",
|
"HeaderSupportTheTeamHelp": "Help ensure the continued development of this project by purchasing Emby Premiere. A portion of all income will be contributed to other free tools we depend on.",
|
||||||
|
|
|
@ -576,6 +576,8 @@
|
||||||
"LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable",
|
"LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable",
|
||||||
"TitleAutoOrganize": "Auto-Organize",
|
"TitleAutoOrganize": "Auto-Organize",
|
||||||
"TabActivityLog": "Activity Log",
|
"TabActivityLog": "Activity Log",
|
||||||
|
"TabSmartMatch": "Smart Match",
|
||||||
|
"TabSmartMatchInfo": "This page lists the match-strings for all library items which were added from within the Auto-Organize correction dialog",
|
||||||
"HeaderName": "Name",
|
"HeaderName": "Name",
|
||||||
"HeaderDate": "Date",
|
"HeaderDate": "Date",
|
||||||
"HeaderSource": "Source",
|
"HeaderSource": "Source",
|
||||||
|
@ -592,6 +594,7 @@
|
||||||
"LabelEpisodeNumber": "Episode number:",
|
"LabelEpisodeNumber": "Episode number:",
|
||||||
"LabelEndingEpisodeNumber": "Ending episode number:",
|
"LabelEndingEpisodeNumber": "Ending episode number:",
|
||||||
"LabelEndingEpisodeNumberHelp": "Only required for multi-episode files",
|
"LabelEndingEpisodeNumberHelp": "Only required for multi-episode files",
|
||||||
|
"LabelOrganizeSmartMatchOption": "In the future, organize all files into the selected series if the name contains",
|
||||||
"HeaderSupportTheTeam": "Support the Emby Team",
|
"HeaderSupportTheTeam": "Support the Emby Team",
|
||||||
"LabelSupportAmount": "Amount (USD)",
|
"LabelSupportAmount": "Amount (USD)",
|
||||||
"HeaderSupportTheTeamHelp": "Help ensure the continued development of this project by purchasing Emby Premiere. A portion of all income will be contributed to other free tools we depend on.",
|
"HeaderSupportTheTeamHelp": "Help ensure the continued development of this project by purchasing Emby Premiere. A portion of all income will be contributed to other free tools we depend on.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue