diff --git a/plugins/DupFileManager/DupFileManager.css b/plugins/DupFileManager/DupFileManager.css
new file mode 100644
index 00000000..05f75f14
--- /dev/null
+++ b/plugins/DupFileManager/DupFileManager.css
@@ -0,0 +1,67 @@
+.scene-card__date {
+  color: #bfccd6;
+  font-size: 0.85em;
+}
+
+.scene-card__performer {
+  display: inline-block;
+  font-weight: 500;
+  margin-right: 0.5em;
+}
+.scene-card__performer a {
+  color: #137cbd;
+}
+
+.scene-card__performers,
+.scene-card__tags {
+  -webkit-box-orient: vertical;
+  display: -webkit-box;
+  -webkit-line-clamp: 1;
+  overflow: hidden;
+}
+.scene-card__performers:hover,
+.scene-card__tags:hover {
+  -webkit-line-clamp: unset;
+  overflow: visible;
+}
+
+.scene-card__tags .tag-item {
+  margin-left: 0;
+}
+
+.scene-performer-popover .image-thumbnail {
+  margin: 1em;
+}
+
+/* Dashed border */
+hr.dashed {
+  border-top: 3px dashed #bbb;
+}
+
+/* Dotted border */
+hr.dotted {
+  border-top: 3px dotted #bbb;
+}
+
+/* Solid border */
+hr.solid {
+  border-top: 3px solid #bbb;
+}
+
+/* Rounded border */
+hr.rounded {
+  border-top: 8px solid #bbb;
+  border-radius: 5px;
+}
+
+h3.under_construction {
+  color: red;
+  background-color: yellow;
+}
+
+h3.submenu {
+  color: Tomato;
+  background-color: rgba(100, 100, 100);
+}
+
+/*# sourceMappingURL=DupFileManager.css.map */
diff --git a/plugins/DupFileManager/DupFileManager.css.map b/plugins/DupFileManager/DupFileManager.css.map
new file mode 100644
index 00000000..a4afe07b
--- /dev/null
+++ b/plugins/DupFileManager/DupFileManager.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../src/DupFileManager.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE","file":"DupFileManager.css"}
\ No newline at end of file
diff --git a/plugins/DupFileManager/DupFileManager.js b/plugins/DupFileManager/DupFileManager.js
new file mode 100644
index 00000000..c4d6b67c
--- /dev/null
+++ b/plugins/DupFileManager/DupFileManager.js
@@ -0,0 +1,695 @@
+(function () {
+  /*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */
+  // prettier-ignore
+  !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}function fe(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}ce.fn=ce.prototype={jquery:t,constructor:ce,length:0,toArray:function(){return ae.call(this)},get:function(e){return null==e?ae.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=ce.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return ce.each(this,e)},map:function(n){return this.pushStack(ce.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(ae.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(ce.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(ce.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:s,sort:oe.sort,splice:oe.splice},ce.extend=ce.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||v(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(ce.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||ce.isPlainObject(n)?n:{},i=!1,a[t]=ce.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},ce.extend({expando:"jQuery"+(t+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==i.call(e))&&(!(t=r(e))||"function"==typeof(n=ue.call(t,"constructor")&&t.constructor)&&o.call(n)===a)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){m(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(c(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},text:function(e){var t,n="",r=0,i=e.nodeType;if(!i)while(t=e[r++])n+=ce.text(t);return 1===i||11===i?e.textContent:9===i?e.documentElement.textContent:3===i||4===i?e.nodeValue:n},makeArray:function(e,t){var n=t||[];return null!=e&&(c(Object(e))?ce.merge(n,"string"==typeof e?[e]:e):s.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:se.call(t,e,n)},isXMLDoc:function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!l.test(t||n&&n.nodeName||"HTML")},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(c(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:le}),"function"==typeof Symbol&&(ce.fn[Symbol.iterator]=oe[Symbol.iterator]),ce.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var pe=oe.pop,de=oe.sort,he=oe.splice,ge="[\\x20\\t\\r\\n\\f]",ve=new RegExp("^"+ge+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ge+"+$","g");ce.contains=function(e,t){var n=t&&t.parentNode;return e===n||!(!n||1!==n.nodeType||!(e.contains?e.contains(n):e.compareDocumentPosition&&16&e.compareDocumentPosition(n)))};var f=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;function p(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e}ce.escapeSelector=function(e){return(e+"").replace(f,p)};var ye=C,me=s;!function(){var e,b,w,o,a,T,r,C,d,i,k=me,S=ce.expando,E=0,n=0,s=W(),c=W(),u=W(),h=W(),l=function(e,t){return e===t&&(a=!0),0},f="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",t="(?:\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",p="\\["+ge+"*("+t+")(?:"+ge+"*([*^$|!~]?=)"+ge+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+t+"))|)"+ge+"*\\]",g=":("+t+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+p+")*)|.*)\\)|)",v=new RegExp(ge+"+","g"),y=new RegExp("^"+ge+"*,"+ge+"*"),m=new RegExp("^"+ge+"*([>+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="<a id='"+S+"' href='' disabled='disabled'></a><select id='"+S+"-\r\\' disabled='disabled'><option selected=''></option></select>",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0<I(t,T,null,[e]).length},I.contains=function(e,t){return(e.ownerDocument||e)!=T&&V(e),ce.contains(e,t)},I.attr=function(e,t){(e.ownerDocument||e)!=T&&V(e);var n=b.attrHandle[t.toLowerCase()],r=n&&ue.call(b.attrHandle,t.toLowerCase())?n(e,t,!C):void 0;return void 0!==r?r:e.getAttribute(t)},I.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},ce.uniqueSort=function(e){var t,n=[],r=0,i=0;if(a=!le.sortStable,o=!le.sortStable&&ae.call(e,0),de.call(e,l),a){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)he.call(e,n[r],1)}return o=null,e},ce.fn.uniqueSort=function(){return this.pushStack(ce.uniqueSort(ae.apply(this)))},(b=ce.expr={cacheLength:50,createPseudo:F,match:D,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(v," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(d,e,t,h,g){var v="nth"!==d.slice(0,3),y="last"!==d.slice(-4),m="of-type"===e;return 1===h&&0===g?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u=v!==y?"nextSibling":"previousSibling",l=e.parentNode,c=m&&e.nodeName.toLowerCase(),f=!n&&!m,p=!1;if(l){if(v){while(u){o=e;while(o=o[u])if(m?fe(o,c):1===o.nodeType)return!1;s=u="only"===d&&!s&&"nextSibling"}return!0}if(s=[y?l.firstChild:l.lastChild],y&&f){p=(a=(r=(i=l[S]||(l[S]={}))[d]||[])[0]===E&&r[1])&&r[2],o=a&&l.childNodes[a];while(o=++a&&o&&o[u]||(p=a=0)||s.pop())if(1===o.nodeType&&++p&&o===e){i[d]=[E,a,p];break}}else if(f&&(p=a=(r=(i=e[S]||(e[S]={}))[d]||[])[0]===E&&r[1]),!1===p)while(o=++a&&o&&o[u]||(p=a=0)||s.pop())if((m?fe(o,c):1===o.nodeType)&&++p&&(f&&((i=o[S]||(o[S]={}))[d]=[E,p]),o===e))break;return(p-=g)===h||p%h==0&&0<=p/h}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||I.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?F(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=se.call(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:F(function(e){var r=[],i=[],s=ne(e.replace(ve,"$1"));return s[S]?F(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:F(function(t){return function(e){return 0<I(t,e).length}}),contains:F(function(t){return t=t.replace(O,P),function(e){return-1<(e.textContent||ce.text(e)).indexOf(t)}}),lang:F(function(n){return A.test(n||"")||I.error("unsupported lang: "+n),n=n.replace(O,P).toLowerCase(),function(e){var t;do{if(t=C?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=ie.location&&ie.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===r},focus:function(e){return e===function(){try{return T.activeElement}catch(e){}}()&&T.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:z(!1),disabled:z(!0),checked:function(e){return fe(e,"input")&&!!e.checked||fe(e,"option")&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return q.test(e.nodeName)},input:function(e){return N.test(e.nodeName)},button:function(e){return fe(e,"input")&&"button"===e.type||fe(e,"button")},text:function(e){var t;return fe(e,"input")&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:X(function(){return[0]}),last:X(function(e,t){return[t-1]}),eq:X(function(e,t,n){return[n<0?n+t:n]}),even:X(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:X(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:X(function(e,t,n){var r;for(r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:X(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=B(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=_(e);function G(){}function Y(e,t){var n,r,i,o,a,s,u,l=c[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=y.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=m.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace(ve," ")}),a=a.slice(n.length)),b.filter)!(r=D[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?I.error(e):c(e,s).slice(0)}function Q(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function J(a,e,t){var s=e.dir,u=e.next,l=u||s,c=t&&"parentNode"===l,f=n++;return e.first?function(e,t,n){while(e=e[s])if(1===e.nodeType||c)return a(e,t,n);return!1}:function(e,t,n){var r,i,o=[E,f];if(n){while(e=e[s])if((1===e.nodeType||c)&&a(e,t,n))return!0}else while(e=e[s])if(1===e.nodeType||c)if(i=e[S]||(e[S]={}),u&&fe(e,u))e=e[s]||e;else{if((r=i[l])&&r[0]===E&&r[1]===f)return o[2]=r[2];if((i[l]=o)[2]=a(e,t,n))return!0}return!1}}function K(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Z(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function ee(d,h,g,v,y,e){return v&&!v[S]&&(v=ee(v)),y&&!y[S]&&(y=ee(y,e)),F(function(e,t,n,r){var i,o,a,s,u=[],l=[],c=t.length,f=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)I(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),p=!d||!e&&h?f:Z(f,u,d,n,r);if(g?g(p,s=y||(e?d:c||v)?[]:t,n,r):s=p,v){i=Z(s,l),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(s[l[o]]=!(p[l[o]]=a))}if(e){if(y||d){if(y){i=[],o=s.length;while(o--)(a=s[o])&&i.push(p[o]=a);y(null,s=[],i,r)}o=s.length;while(o--)(a=s[o])&&-1<(i=y?se.call(e,a):u[o])&&(e[i]=!(t[i]=a))}}else s=Z(s===t?s.splice(c,s.length):s),y?y(null,t,s,r):k.apply(t,s)})}function te(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=J(function(e){return e===i},a,!0),l=J(function(e){return-1<se.call(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!=w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[J(K(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return ee(1<s&&K(c),1<s&&Q(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(ve,"$1"),t,s<n&&te(e.slice(s,n)),n<r&&te(e=e.slice(n)),n<r&&Q(e))}c.push(t)}return K(c)}function ne(e,t){var n,v,y,m,x,r,i=[],o=[],a=u[e+" "];if(!a){t||(t=Y(e)),n=t.length;while(n--)(a=te(t[n]))[S]?i.push(a):o.push(a);(a=u(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=E+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==T||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==T||(V(o),n=!C);while(s=v[a++])if(s(o,t||T,n)){k.call(r,o);break}i&&(E=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=pe.call(r));f=Z(f)}k.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&ce.uniqueSort(r)}return i&&(E=h,w=p),c},m?F(r):r))).selector=e}return a}function re(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&Y(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&C&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(O,P),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=D.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(O,P),H.test(o[0].type)&&U(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&Q(o)))return k.apply(n,r),n;break}}}return(l||ne(e,c))(r,t,!C,n,!t||H.test(e)&&U(t.parentNode)||t),n}G.prototype=b.filters=b.pseudos,b.setFilters=new G,le.sortStable=S.split("").sort(l).join("")===S,V(),le.sortDetached=$(function(e){return 1&e.compareDocumentPosition(T.createElement("fieldset"))}),ce.find=I,ce.expr[":"]=ce.expr.pseudos,ce.unique=ce.uniqueSort,I.compile=ne,I.select=re,I.setDocument=V,I.tokenize=Y,I.escape=ce.escapeSelector,I.getText=ce.text,I.isXML=ce.isXMLDoc,I.selectors=ce.expr,I.support=ce.support,I.uniqueSort=ce.uniqueSort}();var d=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&ce(e).is(n))break;r.push(e)}return r},h=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},b=ce.expr.match.needsContext,w=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1<se.call(n,e)!==r}):ce.filter(n,e,r)}ce.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?ce.find.matchesSelector(r,e)?[r]:[]:ce.find.matches(e,ce.grep(t,function(e){return 1===e.nodeType}))},ce.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(ce(e).filter(function(){for(t=0;t<r;t++)if(ce.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)ce.find(e,i[t],n);return 1<r?ce.uniqueSort(n):n},filter:function(e){return this.pushStack(T(this,e||[],!1))},not:function(e){return this.pushStack(T(this,e||[],!0))},is:function(e){return!!T(this,"string"==typeof e&&b.test(e)?ce(e):e||[],!1).length}});var k,S=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(ce.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&ce(e);if(!b.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&ce.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?ce.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?se.call(ce(e),this[0]):se.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(ce.uniqueSort(ce.merge(this.get(),ce(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),ce.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return d(e,"parentNode")},parentsUntil:function(e,t,n){return d(e,"parentNode",n)},next:function(e){return A(e,"nextSibling")},prev:function(e){return A(e,"previousSibling")},nextAll:function(e){return d(e,"nextSibling")},prevAll:function(e){return d(e,"previousSibling")},nextUntil:function(e,t,n){return d(e,"nextSibling",n)},prevUntil:function(e,t,n){return d(e,"previousSibling",n)},siblings:function(e){return h((e.parentNode||{}).firstChild,e)},children:function(e){return h(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(fe(e,"template")&&(e=e.content||e),ce.merge([],e.childNodes))}},function(r,i){ce.fn[r]=function(e,t){var n=ce.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=ce.filter(t,n)),1<this.length&&(j[r]||ce.uniqueSort(n),E.test(r)&&n.reverse()),this.pushStack(n)}});var D=/[^\x20\t\r\n\f]+/g;function N(e){return e}function q(e){throw e}function L(e,t,n,r){var i;try{e&&v(i=e.promise)?i.call(e).done(t).fail(n):e&&v(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}ce.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},ce.each(e.match(D)||[],function(e,t){n[t]=!0}),n):ce.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){ce.each(e,function(e,t){v(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==x(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return ce.each(arguments,function(e,t){var n;while(-1<(n=ce.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<ce.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},ce.extend({Deferred:function(e){var o=[["notify","progress",ce.Callbacks("memory"),ce.Callbacks("memory"),2],["resolve","done",ce.Callbacks("once memory"),ce.Callbacks("once memory"),0,"resolved"],["reject","fail",ce.Callbacks("once memory"),ce.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return ce.Deferred(function(r){ce.each(o,function(e,t){var n=v(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&v(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,v(t)?s?t.call(e,l(u,o,N,s),l(u,o,q,s)):(u++,t.call(e,l(u,o,N,s),l(u,o,q,s),l(u,o,N,o.notifyWith))):(a!==N&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){ce.Deferred.exceptionHook&&ce.Deferred.exceptionHook(e,t.error),u<=i+1&&(a!==q&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(ce.Deferred.getErrorHook?t.error=ce.Deferred.getErrorHook():ce.Deferred.getStackHook&&(t.error=ce.Deferred.getStackHook()),ie.setTimeout(t))}}return ce.Deferred(function(e){o[0][3].add(l(0,e,v(r)?r:N,e.notifyWith)),o[1][3].add(l(0,e,v(t)?t:N)),o[2][3].add(l(0,e,v(n)?n:q))}).promise()},promise:function(e){return null!=e?ce.extend(e,a):a}},s={};return ce.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=ae.call(arguments),o=ce.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?ae.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(L(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||v(i[t]&&i[t].then)))return o.then();while(t--)L(i[t],a(t),o.reject);return o.promise()}});var H=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;ce.Deferred.exceptionHook=function(e,t){ie.console&&ie.console.warn&&e&&H.test(e.name)&&ie.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},ce.readyException=function(e){ie.setTimeout(function(){throw e})};var O=ce.Deferred();function P(){C.removeEventListener("DOMContentLoaded",P),ie.removeEventListener("load",P),ce.ready()}ce.fn.ready=function(e){return O.then(e)["catch"](function(e){ce.readyException(e)}),this},ce.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--ce.readyWait:ce.isReady)||(ce.isReady=!0)!==e&&0<--ce.readyWait||O.resolveWith(C,[ce])}}),ce.ready.then=O.then,"complete"===C.readyState||"loading"!==C.readyState&&!C.documentElement.doScroll?ie.setTimeout(ce.ready):(C.addEventListener("DOMContentLoaded",P),ie.addEventListener("load",P));var M=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n))for(s in i=!0,n)M(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,v(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(ce(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},R=/^-ms-/,I=/-([a-z])/g;function W(e,t){return t.toUpperCase()}function F(e){return e.replace(R,"ms-").replace(I,W)}var $=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function B(){this.expando=ce.expando+B.uid++}B.uid=1,B.prototype={cache:function(e){var t=e[this.expando];return t||(t={},$(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[F(t)]=n;else for(r in t)i[F(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][F(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(F):(t=F(t))in r?[t]:t.match(D)||[]).length;while(n--)delete r[t[n]]}(void 0===t||ce.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!ce.isEmptyObject(t)}};var _=new B,z=new B,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,U=/[A-Z]/g;function V(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(U,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:X.test(i)?JSON.parse(i):i)}catch(e){}z.set(e,t,n)}else n=void 0;return n}ce.extend({hasData:function(e){return z.hasData(e)||_.hasData(e)},data:function(e,t,n){return z.access(e,t,n)},removeData:function(e,t){z.remove(e,t)},_data:function(e,t,n){return _.access(e,t,n)},_removeData:function(e,t){_.remove(e,t)}}),ce.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=z.get(o),1===o.nodeType&&!_.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=F(r.slice(5)),V(o,r,i[r]));_.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){z.set(this,n)}):M(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=z.get(o,n))?t:void 0!==(t=V(o,n))?t:void 0;this.each(function(){z.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){z.remove(this,e)})}}),ce.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=_.get(e,t),n&&(!r||Array.isArray(n)?r=_.access(e,t,ce.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=ce.queue(e,t),r=n.length,i=n.shift(),o=ce._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){ce.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return _.get(e,n)||_.access(e,n,{empty:ce.Callbacks("once memory").add(function(){_.remove(e,[t+"queue",n])})})}}),ce.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?ce.queue(this[0],t):void 0===n?this:this.each(function(){var e=ce.queue(this,t,n);ce._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&ce.dequeue(this,t)})},dequeue:function(e){return this.each(function(){ce.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=ce.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=_.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var G=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,Y=new RegExp("^(?:([+-])=|)("+G+")([a-z%]*)$","i"),Q=["Top","Right","Bottom","Left"],J=C.documentElement,K=function(e){return ce.contains(e.ownerDocument,e)},Z={composed:!0};J.getRootNode&&(K=function(e){return ce.contains(e.ownerDocument,e)||e.getRootNode(Z)===e.ownerDocument});var ee=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&K(e)&&"none"===ce.css(e,"display")};function te(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return ce.css(e,t,"")},u=s(),l=n&&n[3]||(ce.cssNumber[t]?"":"px"),c=e.nodeType&&(ce.cssNumber[t]||"px"!==l&&+u)&&Y.exec(ce.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)ce.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,ce.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ne={};function re(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=_.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ee(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ne[s])||(o=a.body.appendChild(a.createElement(s)),u=ce.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ne[s]=u)))):"none"!==n&&(l[c]="none",_.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}ce.fn.extend({show:function(){return re(this,!0)},hide:function(){return re(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ee(this)?ce(this).show():ce(this).hide()})}});var xe,be,we=/^(?:checkbox|radio)$/i,Te=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="<textarea>x</textarea>",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="<option></option>",le.option=!!xe.lastChild;var ke={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n<r;n++)_.set(e[n],"globalEval",!t||_.get(t[n],"globalEval"))}ke.tbody=ke.tfoot=ke.colgroup=ke.caption=ke.thead,ke.th=ke.td,le.option||(ke.optgroup=ke.option=[1,"<select multiple='multiple'>","</select>"]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===x(o))ce.merge(p,o.nodeType?[o]:o);else if(je.test(o)){a=a||f.appendChild(t.createElement("div")),s=(Te.exec(o)||["",""])[1].toLowerCase(),u=ke[s]||ke._default,a.innerHTML=u[1]+ce.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;ce.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<ce.inArray(o,r))i&&i.push(o);else if(l=K(o),a=Se(f.appendChild(o),"script"),l&&Ee(a),n){c=0;while(o=a[c++])Ce.test(o.type||"")&&n.push(o)}return f}var De=/^([^.]*)(?:\.(.+)|)/;function Ne(){return!0}function qe(){return!1}function Le(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Le(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=qe;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return ce().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=ce.guid++)),e.each(function(){ce.event.add(this,t,i,r,n)})}function He(e,r,t){t?(_.set(e,r,!1),ce.event.add(e,r,{namespace:!1,handler:function(e){var t,n=_.get(this,r);if(1&e.isTrigger&&this[r]){if(n)(ce.event.special[r]||{}).delegateType&&e.stopPropagation();else if(n=ae.call(arguments),_.set(this,r,n),this[r](),t=_.get(this,r),_.set(this,r,!1),n!==t)return e.stopImmediatePropagation(),e.preventDefault(),t}else n&&(_.set(this,r,ce.event.trigger(n[0],n.slice(1),this)),e.stopPropagation(),e.isImmediatePropagationStopped=Ne)}})):void 0===_.get(e,r)&&ce.event.add(e,r,Ne)}ce.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=_.get(t);if($(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&ce.find.matchesSelector(J,i),n.guid||(n.guid=ce.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof ce&&ce.event.triggered!==e.type?ce.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(D)||[""]).length;while(l--)d=g=(s=De.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=ce.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=ce.event.special[d]||{},c=ce.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&ce.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),ce.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=_.hasData(e)&&_.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(D)||[""]).length;while(l--)if(d=g=(s=De.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=ce.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||ce.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)ce.event.remove(e,d+t[l],n,r,!0);ce.isEmptyObject(u)&&_.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=ce.event.fix(e),l=(_.get(this,"events")||Object.create(null))[u.type]||[],c=ce.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=ce.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((ce.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<ce(i,this).index(l):ce.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(ce.Event.prototype,t,{enumerable:!0,configurable:!0,get:v(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[ce.expando]?e:new ce.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return we.test(t.type)&&t.click&&fe(t,"input")&&He(t,"click",!0),!1},trigger:function(e){var t=this||e;return we.test(t.type)&&t.click&&fe(t,"input")&&He(t,"click"),!0},_default:function(e){var t=e.target;return we.test(t.type)&&t.click&&fe(t,"input")&&_.get(t,"click")||fe(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},ce.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},ce.Event=function(e,t){if(!(this instanceof ce.Event))return new ce.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ne:qe,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&ce.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[ce.expando]=!0},ce.Event.prototype={constructor:ce.Event,isDefaultPrevented:qe,isPropagationStopped:qe,isImmediatePropagationStopped:qe,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ne,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ne,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ne,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},ce.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},ce.event.addProp),ce.each({focus:"focusin",blur:"focusout"},function(r,i){function o(e){if(C.documentMode){var t=_.get(this,"handle"),n=ce.event.fix(e);n.type="focusin"===e.type?"focus":"blur",n.isSimulated=!0,t(e),n.target===n.currentTarget&&t(n)}else ce.event.simulate(i,e.target,ce.event.fix(e))}ce.event.special[r]={setup:function(){var e;if(He(this,r,!0),!C.documentMode)return!1;(e=_.get(this,i))||this.addEventListener(i,o),_.set(this,i,(e||0)+1)},trigger:function(){return He(this,r),!0},teardown:function(){var e;if(!C.documentMode)return!1;(e=_.get(this,i)-1)?_.set(this,i,e):(this.removeEventListener(i,o),_.remove(this,i))},_default:function(e){return _.get(e.target,r)},delegateType:i},ce.event.special[i]={setup:function(){var e=this.ownerDocument||this.document||this,t=C.documentMode?this:e,n=_.get(t,i);n||(C.documentMode?this.addEventListener(i,o):e.addEventListener(r,o,!0)),_.set(t,i,(n||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=C.documentMode?this:e,n=_.get(t,i)-1;n?_.set(t,i,n):(C.documentMode?this.removeEventListener(i,o):e.removeEventListener(r,o,!0),_.remove(t,i))}}}),ce.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){ce.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||ce.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),ce.fn.extend({on:function(e,t,n,r){return Le(this,e,t,n,r)},one:function(e,t,n,r){return Le(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,ce(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=qe),this.each(function(){ce.event.remove(this,e,n,t)})}});var Oe=/<script|<style|<link/i,Pe=/checked\s*(?:[^=]|=\s*.checked.)/i,Me=/^\s*<!\[CDATA\[|\]\]>\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)ce.event.add(t,i,s[i][n]);z.hasData(e)&&(o=z.access(e),a=ce.extend({},o),z.set(t,a))}}function $e(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=v(d);if(h||1<f&&"string"==typeof d&&!le.checkClone&&Pe.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),$e(t,r,i,o)});if(f&&(t=(e=Ae(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=ce.map(Se(e,"script"),Ie)).length;c<f;c++)u=e,c!==p&&(u=ce.clone(u,!0,!0),s&&ce.merge(a,Se(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,ce.map(a,We),c=0;c<s;c++)u=a[c],Ce.test(u.type||"")&&!_.access(u,"globalEval")&&ce.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?ce._evalUrl&&!u.noModule&&ce._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):m(u.textContent.replace(Me,""),u,l))}return n}function Be(e,t,n){for(var r,i=t?ce.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||ce.cleanData(Se(r)),r.parentNode&&(n&&K(r)&&Ee(Se(r,"script")),r.parentNode.removeChild(r));return e}ce.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=K(e);if(!(le.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||ce.isXMLDoc(e)))for(a=Se(c),r=0,i=(o=Se(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&we.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||Se(e),a=a||Se(c),r=0,i=o.length;r<i;r++)Fe(o[r],a[r]);else Fe(e,c);return 0<(a=Se(c,"script")).length&&Ee(a,!f&&Se(e,"script")),c},cleanData:function(e){for(var t,n,r,i=ce.event.special,o=0;void 0!==(n=e[o]);o++)if($(n)){if(t=n[_.expando]){if(t.events)for(r in t.events)i[r]?ce.event.remove(n,r):ce.removeEvent(n,r,t.handle);n[_.expando]=void 0}n[z.expando]&&(n[z.expando]=void 0)}}}),ce.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return M(this,function(e){return void 0===e?ce.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return $e(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Re(this,e).appendChild(e)})},prepend:function(){return $e(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Re(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(ce.cleanData(Se(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return ce.clone(this,e,t)})},html:function(e){return M(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Oe.test(e)&&!ke[(Te.exec(e)||["",""])[1].toLowerCase()]){e=ce.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(ce.cleanData(Se(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return $e(this,arguments,function(e){var t=this.parentNode;ce.inArray(this,n)<0&&(ce.cleanData(Se(this)),t&&t.replaceChild(e,this))},n)}}),ce.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){ce.fn[e]=function(e){for(var t,n=[],r=ce(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),ce(r[o])[a](t),s.apply(n,t.get());return this.pushStack(n)}});var _e=new RegExp("^("+G+")(?!px)[a-z%]+$","i"),ze=/^--/,Xe=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=ie),t.getComputedStyle(e)},Ue=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Ve=new RegExp(Q.join("|"),"i");function Ge(e,t,n){var r,i,o,a,s=ze.test(t),u=e.style;return(n=n||Xe(e))&&(a=n.getPropertyValue(t)||n[t],s&&a&&(a=a.replace(ve,"$1")||void 0),""!==a||K(e)||(a=ce.style(e,t)),!le.pixelBoxStyles()&&_e.test(a)&&Ve.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=n.width,u.width=r,u.minWidth=i,u.maxWidth=o)),void 0!==a?a+"":a}function Ye(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",J.appendChild(u).appendChild(l);var e=ie.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),J.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=C.createElement("div"),l=C.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",le.clearCloneStyle="content-box"===l.style.backgroundClip,ce.extend(le,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=C.createElement("table"),t=C.createElement("tr"),n=C.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="box-sizing:content-box;border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",J.appendChild(e).appendChild(t).appendChild(n),r=ie.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,J.removeChild(e)),a}}))}();var Qe=["Webkit","Moz","ms"],Je=C.createElement("div").style,Ke={};function Ze(e){var t=ce.cssProps[e]||Ke[e];return t||(e in Je?e:Ke[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Qe.length;while(n--)if((e=Qe[n]+t)in Je)return e}(e)||e)}var et=/^(none|table(?!-c[ea]).+)/,tt={position:"absolute",visibility:"hidden",display:"block"},nt={letterSpacing:"0",fontWeight:"400"};function rt(e,t,n){var r=Y.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function it(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0,l=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(l+=ce.css(e,n+Q[a],!0,i)),r?("content"===n&&(u-=ce.css(e,"padding"+Q[a],!0,i)),"margin"!==n&&(u-=ce.css(e,"border"+Q[a]+"Width",!0,i))):(u+=ce.css(e,"padding"+Q[a],!0,i),"padding"!==n?u+=ce.css(e,"border"+Q[a]+"Width",!0,i):s+=ce.css(e,"border"+Q[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u+l}function ot(e,t,n){var r=Xe(e),i=(!le.boxSizingReliable()||n)&&"border-box"===ce.css(e,"boxSizing",!1,r),o=i,a=Ge(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(_e.test(a)){if(!n)return a;a="auto"}return(!le.boxSizingReliable()&&i||!le.reliableTrDimensions()&&fe(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===ce.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===ce.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+it(e,t,n||(i?"border":"content"),o,r,a)+"px"}function at(e,t,n,r,i){return new at.prototype.init(e,t,n,r,i)}ce.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ge(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,aspectRatio:!0,borderImageSlice:!0,columnCount:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,scale:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeMiterlimit:!0,strokeOpacity:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=F(t),u=ze.test(t),l=e.style;if(u||(t=Ze(s)),a=ce.cssHooks[t]||ce.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=Y.exec(n))&&i[1]&&(n=te(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(ce.cssNumber[s]?"":"px")),le.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=F(t);return ze.test(t)||(t=Ze(s)),(a=ce.cssHooks[t]||ce.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Ge(e,t,r)),"normal"===i&&t in nt&&(i=nt[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),ce.each(["height","width"],function(e,u){ce.cssHooks[u]={get:function(e,t,n){if(t)return!et.test(ce.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?ot(e,u,n):Ue(e,tt,function(){return ot(e,u,n)})},set:function(e,t,n){var r,i=Xe(e),o=!le.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===ce.css(e,"boxSizing",!1,i),s=n?it(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-it(e,u,"border",!1,i)-.5)),s&&(r=Y.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=ce.css(e,u)),rt(0,t,s)}}}),ce.cssHooks.marginLeft=Ye(le.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Ge(e,"marginLeft"))||e.getBoundingClientRect().left-Ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),ce.each({margin:"",padding:"",border:"Width"},function(i,o){ce.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+Q[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(ce.cssHooks[i+o].set=rt)}),ce.fn.extend({css:function(e,t){return M(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Xe(e),i=t.length;a<i;a++)o[t[a]]=ce.css(e,t[a],!1,r);return o}return void 0!==n?ce.style(e,t,n):ce.css(e,t)},e,t,1<arguments.length)}}),((ce.Tween=at).prototype={constructor:at,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||ce.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(ce.cssNumber[n]?"":"px")},cur:function(){var e=at.propHooks[this.prop];return e&&e.get?e.get(this):at.propHooks._default.get(this)},run:function(e){var t,n=at.propHooks[this.prop];return this.options.duration?this.pos=t=ce.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):at.propHooks._default.set(this),this}}).init.prototype=at.prototype,(at.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=ce.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){ce.fx.step[e.prop]?ce.fx.step[e.prop](e):1!==e.elem.nodeType||!ce.cssHooks[e.prop]&&null==e.elem.style[Ze(e.prop)]?e.elem[e.prop]=e.now:ce.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=at.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},ce.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},ce.fx=at.prototype.init,ce.fx.step={};var st,ut,lt,ct,ft=/^(?:toggle|show|hide)$/,pt=/queueHooks$/;function dt(){ut&&(!1===C.hidden&&ie.requestAnimationFrame?ie.requestAnimationFrame(dt):ie.setTimeout(dt,ce.fx.interval),ce.fx.tick())}function ht(){return ie.setTimeout(function(){st=void 0}),st=Date.now()}function gt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=Q[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function vt(e,t,n){for(var r,i=(yt.tweeners[t]||[]).concat(yt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function yt(o,e,t){var n,a,r=0,i=yt.prefilters.length,s=ce.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=st||ht(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:ce.extend({},e),opts:ce.extend(!0,{specialEasing:{},easing:ce.easing._default},t),originalProperties:e,originalOptions:t,startTime:st||ht(),duration:t.duration,tweens:[],createTween:function(e,t){var n=ce.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=F(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=ce.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=yt.prefilters[r].call(l,o,c,l.opts))return v(n.stop)&&(ce._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return ce.map(c,vt,l),v(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),ce.fx.timer(ce.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}ce.Animation=ce.extend(yt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return te(n.elem,e,Y.exec(t),n),n}]},tweener:function(e,t){v(e)?(t=e,e=["*"]):e=e.match(D);for(var n,r=0,i=e.length;r<i;r++)n=e[r],yt.tweeners[n]=yt.tweeners[n]||[],yt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ee(e),v=_.get(e,"fxshow");for(r in n.queue||(null==(a=ce._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,ce.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ft.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||ce.style(e,r)}if((u=!ce.isEmptyObject(t))||!ce.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=_.get(e,"display")),"none"===(c=ce.css(e,"display"))&&(l?c=l:(re([e],!0),l=e.style.display||l,c=ce.css(e,"display"),re([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===ce.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=_.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&re([e],!0),p.done(function(){for(r in g||re([e]),_.remove(e,"fxshow"),d)ce.style(e,r,d[r])})),u=vt(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?yt.prefilters.unshift(e):yt.prefilters.push(e)}}),ce.speed=function(e,t,n){var r=e&&"object"==typeof e?ce.extend({},e):{complete:n||!n&&t||v(e)&&e,duration:e,easing:n&&t||t&&!v(t)&&t};return ce.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in ce.fx.speeds?r.duration=ce.fx.speeds[r.duration]:r.duration=ce.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){v(r.old)&&r.old.call(this),r.queue&&ce.dequeue(this,r.queue)},r},ce.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ee).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=ce.isEmptyObject(t),o=ce.speed(e,n,r),a=function(){var e=yt(this,ce.extend({},t),o);(i||_.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=ce.timers,r=_.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&pt.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||ce.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=_.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=ce.timers,o=n?n.length:0;for(t.finish=!0,ce.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),ce.each(["toggle","show","hide"],function(e,r){var i=ce.fn[r];ce.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(gt(r,!0),e,t,n)}}),ce.each({slideDown:gt("show"),slideUp:gt("hide"),slideToggle:gt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){ce.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),ce.timers=[],ce.fx.tick=function(){var e,t=0,n=ce.timers;for(st=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||ce.fx.stop(),st=void 0},ce.fx.timer=function(e){ce.timers.push(e),ce.fx.start()},ce.fx.interval=13,ce.fx.start=function(){ut||(ut=!0,dt())},ce.fx.stop=function(){ut=null},ce.fx.speeds={slow:600,fast:200,_default:400},ce.fn.delay=function(r,e){return r=ce.fx&&ce.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=ie.setTimeout(e,r);t.stop=function(){ie.clearTimeout(n)}})},lt=C.createElement("input"),ct=C.createElement("select").appendChild(C.createElement("option")),lt.type="checkbox",le.checkOn=""!==lt.value,le.optSelected=ct.selected,(lt=C.createElement("input")).value="t",lt.type="radio",le.radioValue="t"===lt.value;var mt,xt=ce.expr.attrHandle;ce.fn.extend({attr:function(e,t){return M(this,ce.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){ce.removeAttr(this,e)})}}),ce.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?ce.prop(e,t,n):(1===o&&ce.isXMLDoc(e)||(i=ce.attrHooks[t.toLowerCase()]||(ce.expr.match.bool.test(t)?mt:void 0)),void 0!==n?null===n?void ce.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=ce.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!le.radioValue&&"radio"===t&&fe(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(D);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),mt={set:function(e,t,n){return!1===t?ce.removeAttr(e,n):e.setAttribute(n,n),n}},ce.each(ce.expr.match.bool.source.match(/\w+/g),function(e,t){var a=xt[t]||ce.find.attr;xt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=xt[o],xt[o]=r,r=null!=a(e,t,n)?o:null,xt[o]=i),r}});var bt=/^(?:input|select|textarea|button)$/i,wt=/^(?:a|area)$/i;function Tt(e){return(e.match(D)||[]).join(" ")}function Ct(e){return e.getAttribute&&e.getAttribute("class")||""}function kt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(D)||[]}ce.fn.extend({prop:function(e,t){return M(this,ce.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[ce.propFix[e]||e]})}}),ce.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&ce.isXMLDoc(e)||(t=ce.propFix[t]||t,i=ce.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=ce.find.attr(e,"tabindex");return t?parseInt(t,10):bt.test(e.nodeName)||wt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),le.optSelected||(ce.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),ce.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){ce.propFix[this.toLowerCase()]=this}),ce.fn.extend({addClass:function(t){var e,n,r,i,o,a;return v(t)?this.each(function(e){ce(this).addClass(t.call(this,e,Ct(this)))}):(e=kt(t)).length?this.each(function(){if(r=Ct(this),n=1===this.nodeType&&" "+Tt(r)+" "){for(o=0;o<e.length;o++)i=e[o],n.indexOf(" "+i+" ")<0&&(n+=i+" ");a=Tt(n),r!==a&&this.setAttribute("class",a)}}):this},removeClass:function(t){var e,n,r,i,o,a;return v(t)?this.each(function(e){ce(this).removeClass(t.call(this,e,Ct(this)))}):arguments.length?(e=kt(t)).length?this.each(function(){if(r=Ct(this),n=1===this.nodeType&&" "+Tt(r)+" "){for(o=0;o<e.length;o++){i=e[o];while(-1<n.indexOf(" "+i+" "))n=n.replace(" "+i+" "," ")}a=Tt(n),r!==a&&this.setAttribute("class",a)}}):this:this.attr("class","")},toggleClass:function(t,n){var e,r,i,o,a=typeof t,s="string"===a||Array.isArray(t);return v(t)?this.each(function(e){ce(this).toggleClass(t.call(this,e,Ct(this),n),n)}):"boolean"==typeof n&&s?n?this.addClass(t):this.removeClass(t):(e=kt(t),this.each(function(){if(s)for(o=ce(this),i=0;i<e.length;i++)r=e[i],o.hasClass(r)?o.removeClass(r):o.addClass(r);else void 0!==t&&"boolean"!==a||((r=Ct(this))&&_.set(this,"__className__",r),this.setAttribute&&this.setAttribute("class",r||!1===t?"":_.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+Tt(Ct(n))+" ").indexOf(t))return!0;return!1}});var St=/\r/g;ce.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=v(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,ce(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=ce.map(t,function(e){return null==e?"":e+""})),(r=ce.valHooks[this.type]||ce.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=ce.valHooks[t.type]||ce.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(St,""):null==e?"":e:void 0}}),ce.extend({valHooks:{option:{get:function(e){var t=ce.find.attr(e,"value");return null!=t?t:Tt(ce.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!fe(n.parentNode,"optgroup"))){if(t=ce(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=ce.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<ce.inArray(ce.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),ce.each(["radio","checkbox"],function(){ce.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<ce.inArray(ce(e).val(),t)}},le.checkOn||(ce.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Et=ie.location,jt={guid:Date.now()},At=/\?/;ce.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new ie.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||ce.error("Invalid XML: "+(n?ce.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var Dt=/^(?:focusinfocus|focusoutblur)$/,Nt=function(e){e.stopPropagation()};ce.extend(ce.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||C],d=ue.call(e,"type")?e.type:e,h=ue.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||C,3!==n.nodeType&&8!==n.nodeType&&!Dt.test(d+ce.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[ce.expando]?e:new ce.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:ce.makeArray(t,[e]),c=ce.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!y(n)){for(s=c.delegateType||d,Dt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||C)&&p.push(a.defaultView||a.parentWindow||ie)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(_.get(o,"events")||Object.create(null))[e.type]&&_.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&$(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!$(n)||u&&v(n[d])&&!y(n)&&((a=n[u])&&(n[u]=null),ce.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,Nt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,Nt),ce.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=ce.extend(new ce.Event,n,{type:e,isSimulated:!0});ce.event.trigger(r,null,t)}}),ce.fn.extend({trigger:function(e,t){return this.each(function(){ce.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return ce.event.trigger(e,t,n,!0)}});var qt=/\[\]$/,Lt=/\r?\n/g,Ht=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;function Pt(n,e,r,i){var t;if(Array.isArray(e))ce.each(e,function(e,t){r||qt.test(n)?i(n,t):Pt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==x(e))i(n,e);else for(t in e)Pt(n+"["+t+"]",e[t],r,i)}ce.param=function(e,t){var n,r=[],i=function(e,t){var n=v(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!ce.isPlainObject(e))ce.each(e,function(){i(this.name,this.value)});else for(n in e)Pt(n,e[n],t,i);return r.join("&")},ce.fn.extend({serialize:function(){return ce.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=ce.prop(this,"elements");return e?ce.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!ce(this).is(":disabled")&&Ot.test(this.nodeName)&&!Ht.test(e)&&(this.checked||!we.test(e))}).map(function(e,t){var n=ce(this).val();return null==n?null:Array.isArray(n)?ce.map(n,function(e){return{name:t.name,value:e.replace(Lt,"\r\n")}}):{name:t.name,value:n.replace(Lt,"\r\n")}}).get()}});var Mt=/%20/g,Rt=/#.*$/,It=/([?&])_=[^&]*/,Wt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ft=/^(?:GET|HEAD)$/,$t=/^\/\//,Bt={},_t={},zt="*/".concat("*"),Xt=C.createElement("a");function Ut(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(D)||[];if(v(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Vt(t,i,o,a){var s={},u=t===_t;function l(e){var r;return s[e]=!0,ce.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function Gt(e,t){var n,r,i=ce.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&ce.extend(!0,e,r),e}Xt.href=Et.href,ce.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":ce.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Gt(Gt(e,ce.ajaxSettings),t):Gt(ce.ajaxSettings,e)},ajaxPrefilter:Ut(Bt),ajaxTransport:Ut(_t),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=ce.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?ce(y):ce.event,x=ce.Deferred(),b=ce.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Wt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Et.href)+"").replace($t,Et.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(D)||[""],null==v.crossDomain){r=C.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Xt.protocol+"//"+Xt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=ce.param(v.data,v.traditional)),Vt(Bt,v,t,T),h)return T;for(i in(g=ce.event&&v.global)&&0==ce.active++&&ce.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ft.test(v.type),f=v.url.replace(Rt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Mt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(At.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(It,"$1"),o=(At.test(f)?"&":"?")+"_="+jt.guid+++o),v.url=f+o),v.ifModified&&(ce.lastModified[f]&&T.setRequestHeader("If-Modified-Since",ce.lastModified[f]),ce.etag[f]&&T.setRequestHeader("If-None-Match",ce.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+zt+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Vt(_t,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=ie.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&ie.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<ce.inArray("script",v.dataTypes)&&ce.inArray("json",v.dataTypes)<0&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(ce.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(ce.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--ce.active||ce.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return ce.get(e,t,n,"json")},getScript:function(e,t){return ce.get(e,void 0,t,"script")}}),ce.each(["get","post"],function(e,i){ce[i]=function(e,t,n,r){return v(t)&&(r=r||n,n=t,t=void 0),ce.ajax(ce.extend({url:e,type:i,dataType:r,data:t,success:n},ce.isPlainObject(e)&&e))}}),ce.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),ce._evalUrl=function(e,t,n){return ce.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){ce.globalEval(e,t,n)}})},ce.fn.extend({wrapAll:function(e){var t;return this[0]&&(v(e)&&(e=e.call(this[0])),t=ce(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return v(n)?this.each(function(e){ce(this).wrapInner(n.call(this,e))}):this.each(function(){var e=ce(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=v(t);return this.each(function(e){ce(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){ce(this).replaceWith(this.childNodes)}),this}}),ce.expr.pseudos.hidden=function(e){return!ce.expr.pseudos.visible(e)},ce.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},ce.ajaxSettings.xhr=function(){try{return new ie.XMLHttpRequest}catch(e){}};var Yt={0:200,1223:204},Qt=ce.ajaxSettings.xhr();le.cors=!!Qt&&"withCredentials"in Qt,le.ajax=Qt=!!Qt,ce.ajaxTransport(function(i){var o,a;if(le.cors||Qt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Yt[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&ie.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),ce.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),ce.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return ce.globalEval(e),e}}}),ce.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),ce.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=ce("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=Tt(e.slice(s)),e=e.slice(0,s)),v(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&ce.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?ce("<div>").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var en=/^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;ce.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),v(e))return r=ae.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(ae.call(arguments)))}).guid=e.guid=e.guid||ce.guid++,i},ce.holdReady=function(e){e?ce.readyWait++:ce.ready(!0)},ce.isArray=Array.isArray,ce.parseJSON=JSON.parse,ce.nodeName=fe,ce.isFunction=v,ce.isWindow=y,ce.camelCase=F,ce.type=x,ce.now=Date.now,ce.isNumeric=function(e){var t=ce.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},ce.trim=function(e){return null==e?"":(e+"").replace(en,"$1")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return ce});var tn=ie.jQuery,nn=ie.$;return ce.noConflict=function(e){return ie.$===ce&&(ie.$=nn),e&&ie.jQuery===ce&&(ie.jQuery=tn),ce},"undefined"==typeof e&&(ie.jQuery=ie.$=ce),ce});
+  var AsyncResults = null;
+  function RunPluginDupFileManager(
+    Mode,
+    DataType = "text",
+    Async = false,
+    ActionID = 0
+  ) {
+    AsyncResults = null;
+    const AjaxData = $.ajax({
+      method: "POST",
+      url: "/graphql",
+      contentType: "application/json",
+      dataType: DataType,
+      cache: Async,
+      async: Async,
+      data: JSON.stringify({
+        query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
+        variables: {
+          plugin_id: "DupFileManager",
+          args: { Target: ActionID, mode: Mode },
+        },
+      }),
+      success: function (result) {
+        AsyncResults = result;
+        console.log(AsyncResults);
+        if (DataType == "text") return result;
+        return result;
+      },
+    });
+    if (Async == true) return true;
+    if (DataType == "text") {
+      console.log(AjaxData.responseText);
+      return AjaxData.responseText;
+    }
+    console.log(AjaxData.responseJSON);
+    return JSON.parse(
+      AjaxData.responseJSON.data.runPluginOperation.replaceAll("'", '"')
+    );
+  }
+  var LocalDupReportExist = false;
+  var AdvanceMenuOptionUrl = "";
+  function GetLocalDuplicateReportPath() {
+    var LocalDuplicateReport = RunPluginDupFileManager(
+      "getLocalDupReportPath",
+      "json"
+    );
+    var LocalDuplicateReportPath = "file://" + LocalDuplicateReport.Path;
+    console.log(LocalDuplicateReportPath);
+    AdvanceMenuOptionUrl = LocalDuplicateReportPath.replace(
+      "report\\DuplicateTagScenes.html",
+      "advance_options.html"
+    );
+    console.log(AdvanceMenuOptionUrl);
+    LocalDupReportExist = LocalDuplicateReport.LocalDupReportExist;
+    return LocalDuplicateReportPath;
+  }
+
+  const PluginApi = window.PluginApi;
+  const React = PluginApi.React;
+  const GQL = PluginApi.GQL;
+  const { Button } = PluginApi.libraries.Bootstrap;
+  const { faEthernet } = PluginApi.libraries.FontAwesomeSolid;
+  const { Link, NavLink } = PluginApi.libraries.ReactRouterDOM;
+  // ToolTip text
+  const CreateReportButtonToolTip =
+    "Tag duplicate files, and create a new duplicate file report listing all duplicate files and using existing DupFileManager plugin options selected.";
+  const CreateReportNoTagButtonToolTip =
+    "Create a new duplicate file report listing all duplicate files and using existing DupFileManager plugin options selected. Do NOT tag files.";
+  const ToolsMenuToolTip =
+    "Show DupFileManager advance menu, which list additional tools and utilities.";
+  const ShowReportButtonToolTip =
+    "Open link to the duplicate file (HTML) report created in local path.";
+  const ReportMenuButtonToolTip =
+    "Main report menu for DupFileManager. Create and show duplicate files on an HTML report.";
+  // Buttons
+  const DupFileManagerReportMenuButton = React.createElement(
+    Link,
+    { to: "/plugin/DupFileManager", title: ReportMenuButtonToolTip },
+    React.createElement(Button, null, "DupFileManager Report Menu")
+  );
+  const ToolsMenuOptionButton = React.createElement(
+    Link,
+    { to: "/plugin/DupFileManager_ToolsAndUtilities", title: ToolsMenuToolTip },
+    React.createElement(Button, null, "DupFileManager Tools and Utilities")
+  );
+  function GetShowReportButton(LocalDuplicateReportPath, ButtonText) {
+    return React.createElement(
+      "a",
+      { href: LocalDuplicateReportPath, title: ShowReportButtonToolTip },
+      React.createElement(Button, null, ButtonText)
+    );
+  }
+  function GetAdvanceMenuButton() {
+    return React.createElement(
+      "a",
+      {
+        href: AdvanceMenuOptionUrl,
+        title: "Open link to the advance duplicate tagged menu.",
+      },
+      React.createElement(Button, null, "Show Advance Duplicate Tagged Menu")
+    );
+  }
+  function GetCreateReportNoTagButton(ButtonText) {
+    return React.createElement(
+      Link,
+      {
+        to: "/plugin/DupFileManager_CreateReportWithNoTagging",
+        title: CreateReportNoTagButtonToolTip,
+      },
+      React.createElement(Button, null, ButtonText)
+    );
+  }
+  function GetCreateReportButton(ButtonText) {
+    return React.createElement(
+      Link,
+      {
+        to: "/plugin/DupFileManager_CreateReport",
+        title: CreateReportButtonToolTip,
+      },
+      React.createElement(Button, null, ButtonText)
+    );
+  }
+
+  const { LoadingIndicator } = PluginApi.components;
+  const HomePage = () => {
+    var LocalDuplicateReportPath = GetLocalDuplicateReportPath();
+    console.log(LocalDupReportExist);
+    var MyHeader = React.createElement(
+      "h1",
+      null,
+      "DupFileManager Report Menu"
+    );
+    if (LocalDupReportExist)
+      return React.createElement(
+        "center",
+        null,
+        MyHeader,
+        GetShowReportButton(
+          LocalDuplicateReportPath,
+          "Show Duplicate-File Report"
+        ),
+        React.createElement("p", null),
+        GetAdvanceMenuButton(),
+        React.createElement("p", null),
+        GetCreateReportNoTagButton("Create New Report (NO Tagging)"),
+        React.createElement("p", null),
+        GetCreateReportButton("Create New Report with Tagging"),
+        React.createElement("p", null),
+        ToolsMenuOptionButton
+      );
+    return React.createElement(
+      "center",
+      null,
+      MyHeader,
+      GetCreateReportNoTagButton("Create Duplicate-File Report (NO Tagging)"),
+      React.createElement("p", null),
+      GetCreateReportButton("Create Duplicate-File Report with Tagging"),
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const CreateReport = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message:
+          "Running task to create report. This may take a while. Please standby.",
+      });
+    RunPluginDupFileManager("tag_duplicates_task");
+    return React.createElement(
+      "center",
+      null,
+      React.createElement(
+        "h1",
+        null,
+        "Report complete. Click [Show Report] to view report."
+      ),
+      GetShowReportButton(GetLocalDuplicateReportPath(), "Show Report"),
+      React.createElement("p", null),
+      GetAdvanceMenuButton(),
+      React.createElement("p", null),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const CreateReportWithNoTagging = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message: "Running task to create report. Please standby.",
+      });
+    RunPluginDupFileManager("createDuplicateReportWithoutTagging");
+    return React.createElement(
+      "center",
+      null,
+      React.createElement(
+        "h1",
+        null,
+        "Created HTML report without tagging. Click [Show Report] to view report."
+      ),
+      GetShowReportButton(GetLocalDuplicateReportPath(), "Show Report"),
+      React.createElement("p", null),
+      GetAdvanceMenuButton(),
+      React.createElement("p", null),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const ToolsAndUtilities = () => {
+    return React.createElement(
+      "center",
+      null,
+      React.createElement("h1", null, "DupFileManager Tools and Utilities"),
+      React.createElement("p", null),
+
+      React.createElement("h3", { class: "submenu" }, "Report Options"),
+      React.createElement("p", null),
+      GetCreateReportNoTagButton("Create Report (NO Tagging)"),
+      React.createElement("p", null),
+      GetCreateReportButton("Create Report (Tagging)"),
+      React.createElement("p", null),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      GetShowReportButton(
+        GetLocalDuplicateReportPath(),
+        "Show Duplicate-File Report"
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_deleteLocalDupReportHtmlFiles",
+          title: "Delete local HTML duplicate file report.",
+        },
+        React.createElement(
+          Button,
+          null,
+          "Delete Duplicate-File Report HTML Files"
+        )
+      ),
+      React.createElement("hr", { class: "dotted" }),
+
+      React.createElement(
+        "h3",
+        { class: "submenu" },
+        "Tagged Duplicates Options"
+      ),
+      React.createElement("p", null),
+      GetAdvanceMenuButton(),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_deleteTaggedDuplicatesTask",
+          title:
+            "Delete scenes previously given duplicate tag (_DuplicateMarkForDeletion).",
+        },
+        React.createElement(Button, null, "Delete Tagged Duplicates")
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_deleteBlackListTaggedDuplicatesTask",
+          title:
+            "Delete scenes only in blacklist which where previously given duplicate tag (_DuplicateMarkForDeletion).",
+        },
+        React.createElement(
+          Button,
+          null,
+          "Delete Tagged Duplicates in Blacklist Only"
+        )
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_deleteTaggedDuplicatesLwrResOrLwrDuration",
+          title:
+            "Delete scenes previously given duplicate tag (_DuplicateMarkForDeletion) and lower resultion or duration compare to primary (ToKeep) duplicate.",
+        },
+        React.createElement(
+          Button,
+          null,
+          "Delete Low Res/Dur Tagged Duplicates"
+        )
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_deleteBlackListTaggedDuplicatesLwrResOrLwrDuration",
+          title:
+            "Delete scenes only in blacklist which where previously given duplicate tag (_DuplicateMarkForDeletion) and lower resultion or duration compare to primary (ToKeep) duplicate.",
+        },
+        React.createElement(
+          Button,
+          null,
+          "Delete Low Res/Dur Tagged Duplicates in Blacklist Only"
+        )
+      ),
+      React.createElement("p", null),
+      React.createElement("hr", { class: "dotted" }),
+
+      React.createElement(
+        "h3",
+        { class: "submenu" },
+        "Tagged Management Options"
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_ClearAllDuplicateTags",
+          title:
+            "Remove duplicate tag from all scenes. This task may take some time to complete.",
+        },
+        React.createElement(Button, null, "Clear All Duplicate Tags")
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_deleteAllDupFileManagerTags",
+          title: "Delete all DupFileManager tags from stash.",
+        },
+        React.createElement(Button, null, "Delete All DupFileManager Tags")
+      ),
+      React.createElement("p", null),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_tagGrayList",
+          title:
+            "Set tag _GraylistMarkForDeletion to scenes having DuplicateMarkForDeletion tag and that are in the Graylist.",
+        },
+        React.createElement(Button, null, "Tag Graylist")
+      ),
+      React.createElement("hr", { class: "dotted" }),
+
+      React.createElement("h3", { class: "submenu" }, "Miscellaneous Options"),
+      React.createElement(
+        Link,
+        {
+          to: "/plugin/DupFileManager_generatePHASH_Matching",
+          title:
+            "Generate PHASH (Perceptual hashes) matching. Used for file comparisons.",
+        },
+        React.createElement(
+          Button,
+          null,
+          "Generate PHASH (Perceptual hashes) Matching"
+        )
+      ),
+      React.createElement("p", null),
+      React.createElement("p", null),
+      React.createElement("p", null),
+      React.createElement("p", null)
+    );
+  };
+  const ClearAllDuplicateTags = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message:
+          "Running clear duplicate tags in background. This may take a while. Please standby.",
+      });
+    RunPluginDupFileManager("clear_duplicate_tags_task");
+    return React.createElement(
+      "div",
+      null,
+      React.createElement(
+        "h1",
+        null,
+        "Removed duplicate tags from all scenes."
+      ),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const deleteLocalDupReportHtmlFiles = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message: "Running task to delete HTML files. Please standby.",
+      });
+    RunPluginDupFileManager("deleteLocalDupReportHtmlFiles");
+    return React.createElement(
+      "div",
+      null,
+      React.createElement(
+        "h2",
+        null,
+        "Deleted the HTML duplicate file report from local files."
+      ),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const deleteAllDupFileManagerTags = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message:
+          "Running task to delete all DupFileManager tags in background. This may take a while. Please standby.",
+      });
+    RunPluginDupFileManager("deleteAllDupFileManagerTags");
+    return React.createElement(
+      "div",
+      null,
+      React.createElement("h1", null, "Deleted all DupFileManager tags."),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const generatePHASH_Matching = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message:
+          "Running task generate PHASH (Perceptual hashes) matching in background. This may take a while. Please standby.",
+      });
+    RunPluginDupFileManager("generate_phash_task");
+    return React.createElement(
+      "div",
+      null,
+      React.createElement("h1", null, "PHASH (Perceptual hashes) complete."),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const tagGrayList = () => {
+    const componentsLoading = PluginApi.hooks.useLoadComponents([
+      PluginApi.loadableComponents.SceneCard,
+    ]);
+    if (componentsLoading)
+      return React.createElement(LoadingIndicator, {
+        message:
+          "Running task to tag _GraylistMarkForDeletion to scenes having DuplicateMarkForDeletion tag and that are in the Graylist. This may take a while. Please standby.",
+      });
+    RunPluginDupFileManager("graylist_tag_task");
+    return React.createElement(
+      "div",
+      null,
+      React.createElement("h1", null, "Gray list tagging complete."),
+      DupFileManagerReportMenuButton,
+      React.createElement("p", null),
+      ToolsMenuOptionButton
+    );
+  };
+  const deleteTaggedDuplicatesTask = () => {
+    let result = confirm(
+      "Are you sure you want to delete all scenes having _DuplicateMarkForDeletion tags? This will delete the files, and remove them from stash."
+    );
+    if (result) {
+      const componentsLoading = PluginApi.hooks.useLoadComponents([
+        PluginApi.loadableComponents.SceneCard,
+      ]);
+      if (componentsLoading)
+        return React.createElement(LoadingIndicator, {
+          message:
+            "Running task to delete all scenes with _DuplicateMarkForDeletion tag. This may take a while. Please standby.",
+        });
+      RunPluginDupFileManager("delete_tagged_duplicates_task");
+      return React.createElement(
+        "div",
+        null,
+        React.createElement("h1", null, "Scenes with dup tag deleted."),
+        DupFileManagerReportMenuButton,
+        React.createElement("p", null),
+        ToolsMenuOptionButton
+      );
+    }
+    return ToolsAndUtilities();
+  };
+  const deleteBlackListTaggedDuplicatesTask = () => {
+    let result = confirm(
+      "Are you sure you want to delete all scenes in blacklist having _DuplicateMarkForDeletion tags? This will delete the files, and remove tem from stash."
+    );
+    if (result) {
+      const componentsLoading = PluginApi.hooks.useLoadComponents([
+        PluginApi.loadableComponents.SceneCard,
+      ]);
+      if (componentsLoading)
+        return React.createElement(LoadingIndicator, {
+          message:
+            "Running task to delete all scenes in blacklist with _DuplicateMarkForDeletion tag. This may take a while. Please standby.",
+        });
+      RunPluginDupFileManager("deleteBlackListTaggedDuplicatesTask");
+      return React.createElement(
+        "div",
+        null,
+        React.createElement(
+          "h1",
+          null,
+          "Blacklist scenes with dup tag deleted."
+        ),
+        DupFileManagerReportMenuButton,
+        React.createElement("p", null),
+        ToolsMenuOptionButton
+      );
+    }
+    return ToolsAndUtilities();
+  };
+  const deleteTaggedDuplicatesLwrResOrLwrDuration = () => {
+    let result = confirm(
+      "Are you sure you want to delete scenes having _DuplicateMarkForDeletion tags and lower resultion or duration? This will delete the files, and remove them from stash."
+    );
+    if (result) {
+      const componentsLoading = PluginApi.hooks.useLoadComponents([
+        PluginApi.loadableComponents.SceneCard,
+      ]);
+      if (componentsLoading)
+        return React.createElement(LoadingIndicator, {
+          message:
+            "Running task to delete all scenes with _DuplicateMarkForDeletion tag and lower resultion or duration. This may take a while. Please standby.",
+        });
+      RunPluginDupFileManager("deleteTaggedDuplicatesLwrResOrLwrDuration");
+      return React.createElement(
+        "div",
+        null,
+        React.createElement(
+          "h1",
+          null,
+          "Scenes with dup tag and lower resultion or duration deleted."
+        ),
+        DupFileManagerReportMenuButton,
+        React.createElement("p", null),
+        ToolsMenuOptionButton
+      );
+    }
+    return ToolsAndUtilities();
+  };
+  const deleteBlackListTaggedDuplicatesLwrResOrLwrDuration = () => {
+    let result = confirm(
+      "Are you sure you want to delete scenes in blacklist having _DuplicateMarkForDeletion tags and lower resultion or duration? This will delete the files, and remove tem from stash."
+    );
+    if (result) {
+      const componentsLoading = PluginApi.hooks.useLoadComponents([
+        PluginApi.loadableComponents.SceneCard,
+      ]);
+      if (componentsLoading)
+        return React.createElement(LoadingIndicator, {
+          message:
+            "Running task to delete all scenes in blacklist with _DuplicateMarkForDeletion tag and lower resultion or duration. This may take a while. Please standby.",
+        });
+      RunPluginDupFileManager(
+        "deleteBlackListTaggedDuplicatesLwrResOrLwrDuration"
+      );
+      return React.createElement(
+        "div",
+        null,
+        React.createElement(
+          "h1",
+          null,
+          "Blacklist scenes with dup tag and lower resultion or duration deleted."
+        ),
+        DupFileManagerReportMenuButton,
+        React.createElement("p", null),
+        ToolsMenuOptionButton
+      );
+    }
+    return ToolsAndUtilities();
+  };
+  PluginApi.register.route("/plugin/DupFileManager", HomePage);
+  PluginApi.register.route("/plugin/DupFileManager_CreateReport", CreateReport);
+  PluginApi.register.route(
+    "/plugin/DupFileManager_CreateReportWithNoTagging",
+    CreateReportWithNoTagging
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_ToolsAndUtilities",
+    ToolsAndUtilities
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_ClearAllDuplicateTags",
+    ClearAllDuplicateTags
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_deleteLocalDupReportHtmlFiles",
+    deleteLocalDupReportHtmlFiles
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_deleteAllDupFileManagerTags",
+    deleteAllDupFileManagerTags
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_generatePHASH_Matching",
+    generatePHASH_Matching
+  );
+  PluginApi.register.route("/plugin/DupFileManager_tagGrayList", tagGrayList);
+  PluginApi.register.route(
+    "/plugin/DupFileManager_deleteTaggedDuplicatesTask",
+    deleteTaggedDuplicatesTask
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_deleteBlackListTaggedDuplicatesTask",
+    deleteBlackListTaggedDuplicatesTask
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_deleteTaggedDuplicatesLwrResOrLwrDuration",
+    deleteTaggedDuplicatesLwrResOrLwrDuration
+  );
+  PluginApi.register.route(
+    "/plugin/DupFileManager_deleteBlackListTaggedDuplicatesLwrResOrLwrDuration",
+    deleteBlackListTaggedDuplicatesLwrResOrLwrDuration
+  );
+  PluginApi.patch.before("SettingsToolsSection", function (props) {
+    const { Setting } = PluginApi.components;
+    return [
+      {
+        children: React.createElement(
+          React.Fragment,
+          null,
+          props.children,
+          React.createElement(Setting, {
+            heading: React.createElement(
+              Link,
+              { to: "/plugin/DupFileManager", title: ReportMenuButtonToolTip },
+              React.createElement(
+                Button,
+                null,
+                "Duplicate File Report (DupFileManager)"
+              )
+            ),
+          }),
+          React.createElement(Setting, {
+            heading: React.createElement(
+              Link,
+              {
+                to: "/plugin/DupFileManager_ToolsAndUtilities",
+                title: ToolsMenuToolTip,
+              },
+              React.createElement(
+                Button,
+                null,
+                "DupFileManager Tools and Utilities"
+              )
+            ),
+          })
+        ),
+      },
+    ];
+  });
+  PluginApi.patch.before("MainNavBar.UtilityItems", function (props) {
+    const { Icon } = PluginApi.components;
+    return [
+      {
+        children: React.createElement(
+          React.Fragment,
+          null,
+          props.children,
+          React.createElement(
+            NavLink,
+            {
+              className: "nav-utility",
+              exact: true,
+              to: "/plugin/DupFileManager",
+            },
+            React.createElement(
+              Button,
+              {
+                className: "minimal d-flex align-items-center h-100",
+                title: ReportMenuButtonToolTip,
+              },
+              React.createElement(Icon, { icon: faEthernet })
+            )
+          )
+        ),
+      },
+    ];
+  });
+})();
diff --git a/plugins/DupFileManager/DupFileManager.js.map b/plugins/DupFileManager/DupFileManager.js.map
new file mode 100644
index 00000000..5fdfda50
--- /dev/null
+++ b/plugins/DupFileManager/DupFileManager.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"DupFileManager.js","sourceRoot":"","sources":["../src/DupFileManager.tsx"],"names":[],"mappings":";AA0CA,CAAC;IACC,MAAM,SAAS,GAAI,MAAc,CAAC,SAAuB,CAAC;IAC1D,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;IAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC;IAE1B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC;IACjD,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,gBAAgB,CAAC;IAC5D,MAAM,EACJ,IAAI,EACJ,OAAO,GACR,GAAG,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC;IAEvC,MAAM,EACJ,QAAQ,EACT,GAAG,SAAS,CAAC,KAAK,CAAC;IAEpB,SAAS,CAAC,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IAEtJ,MAAM,cAAc,GAEf,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;QACrB,8EAA8E;QAC9E,yDAAyD;QACzD,MAAM,EACJ,YAAY,GACb,GAAG,SAAS,CAAC,UAAU,CAAC;QAEzB,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAClC,GAAG,EAAE;;YAAC,OAAA,CACJ,6BAAK,SAAS,EAAC,yBAAyB;gBACtC,oBAAC,IAAI,IAAC,EAAE,EAAE,eAAe,SAAS,CAAC,EAAE,EAAE;oBACrC,6BACE,SAAS,EAAC,iBAAiB,EAC3B,GAAG,EAAE,MAAA,SAAS,CAAC,IAAI,mCAAI,EAAE,EACzB,GAAG,EAAE,MAAA,SAAS,CAAC,UAAU,mCAAI,EAAE,GAC/B,CACG,CACH,CACP,CAAA;SAAA,EACD,CAAC,SAAS,CAAC,CACZ,CAAC;QAEF,OAAO,CACL,oBAAC,YAAY,IACX,SAAS,EAAC,uBAAuB,EACjC,SAAS,EAAC,KAAK,EACf,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,GAAG;YAEf,2BAAG,IAAI,EAAE,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,IAAG,SAAS,CAAC,IAAI,CAAK,CAC5D,CAChB,CAAC;IACJ,CAAC,CAAC;IAEF,SAAS,YAAY,CAAC,KAAU;QAC9B,MAAM,EACJ,OAAO,GACR,GAAG,SAAS,CAAC,UAAU,CAAC;QAEzB,SAAS,qBAAqB;YAC5B,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO;YAE/C,OAAO,CACL,6BAAK,SAAS,EAAC,wBAAwB,IACpC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAc,EAAE,EAAE,CAAC,CAC9C,oBAAC,cAAc,IAAC,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,GAAI,CAC5D,CAAC,CACE,CACP,CAAC;QACJ,CAAC;QAED,SAAS,eAAe;YACtB,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO;YAEzC,OAAO,CACL,6BAAK,SAAS,EAAC,kBAAkB,IAC9B,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAClC,oBAAC,OAAO,IAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,GAAI,CACnC,CAAC,CACE,CACP,CAAC;QACJ,CAAC;QAED,OAAO,CACL,6BAAK,SAAS,EAAC,qBAAqB;YAClC,8BAAM,SAAS,EAAC,kBAAkB,IAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAQ;YAC3D,qBAAqB,EAAE;YACvB,eAAe,EAAE,CACd,CACP,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,UAAU,KAAU,EAAE,CAAM,EAAE,QAAa;QACtF,OAAO,oBAAC,YAAY,OAAK,KAAK,GAAI,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAa,GAAG,EAAE;QAC9B,MAAM,iBAAiB,GAAG,SAAS,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC;QAEtG,MAAM,EACJ,SAAS,EACT,gBAAgB,GACjB,GAAG,SAAS,CAAC,UAAU,CAAC;QAEzB,mDAAmD;QACnD,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,kBAAkB,CAAC;YACtC,SAAS,EAAE;gBACT,MAAM,EAAE;oBACN,QAAQ,EAAE,CAAC;oBACX,IAAI,EAAE,QAAQ;iBACf;aACF;SACF,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAEzC,IAAI,iBAAiB;YAAE,OAAO,CAC5B,oBAAC,gBAAgB,OAAG,CACrB,CAAC;QAEF,OAAO,CACL;YACE,wDAA+B;YAC9B,CAAC,CAAC,KAAK,IAAI,oBAAC,SAAS,IAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAI,CACvD,CACP,CAAC;IACJ,CAAC,CAAC;IAEF,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;IAEzD,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,EAAE,UAAU,KAAU;QACjE,MAAM,EACJ,OAAO,GACR,GAAG,SAAS,CAAC,UAAU,CAAC;QAEzB,OAAO;YACL;gBACE,QAAQ,EAAE,CACR;oBACG,KAAK,CAAC,QAAQ;oBACf,oBAAC,OAAO,IACN,OAAO,EACL,oBAAC,IAAI,IAAC,EAAE,EAAC,oBAAoB;4BAC3B,oBAAC,MAAM,oBAEE,CACJ,GAET,CACD,CACJ;aACF;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,yBAAyB,EAAE,UAAU,KAAU;QACpE,MAAM,EACJ,IAAI,GACL,GAAG,SAAS,CAAC,UAAU,CAAC;QAEzB,OAAO;YACL;gBACE,QAAQ,EAAE,CACR;oBACG,KAAK,CAAC,QAAQ;oBACf,oBAAC,OAAO,IACN,SAAS,EAAC,aAAa,EACvB,KAAK,QACL,EAAE,EAAC,oBAAoB;wBAEvB,oBAAC,MAAM,IACL,SAAS,EAAC,yCAAyC,EACnD,KAAK,EAAC,WAAW;4BAEjB,oBAAC,IAAI,IAAC,IAAI,EAAE,UAAU,GAAI,CACnB,CACD,CACT,CACJ;aACF;SACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,EAAE,CAAC"}
\ No newline at end of file
diff --git a/plugins/DupFileManager/DupFileManager.py b/plugins/DupFileManager/DupFileManager.py
index c9ef4a16..16625534 100644
--- a/plugins/DupFileManager/DupFileManager.py
+++ b/plugins/DupFileManager/DupFileManager.py
@@ -3,31 +3,60 @@
 # Get the latest developers version from following link: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
 # Note: To call this script outside of Stash, pass argument --url 
 #       Example:    python DupFileManager.py --url http://localhost:9999 -a
-import os, sys, time, pathlib, argparse, platform, shutil, logging
+try:
+    import ModulesValidate
+    ModulesValidate.modulesInstalled(["send2trash", "requests"], silent=True)
+except Exception as e:
+    import traceback, sys
+    tb = traceback.format_exc()
+    print(f"ModulesValidate Exception. Error: {e}\nTraceBack={tb}", file=sys.stderr)
+import os, sys, time, pathlib, argparse, platform, shutil, traceback, logging, requests
+from datetime import datetime
 from StashPluginHelper import StashPluginHelper
+from stashapi.stash_types import PhashDistance
 from DupFileManager_config import config # Import config from DupFileManager_config.py
+from DupFileManager_report_config import report_config
+
+# ToDo: make sure the following line of code works
+config |= report_config
 
 parser = argparse.ArgumentParser()
 parser.add_argument('--url', '-u', dest='stash_url', type=str, help='Add Stash URL')
 parser.add_argument('--trace', '-t', dest='trace', action='store_true', help='Enables debug trace mode.')
 parser.add_argument('--add_dup_tag', '-a', dest='dup_tag', action='store_true', help='Set a tag to duplicate files.')
+parser.add_argument('--clear_dup_tag', '-c', dest='clear_tag', action='store_true', help='Clear duplicates of duplicate tags.')
 parser.add_argument('--del_tag_dup', '-d', dest='del_tag', action='store_true', help='Only delete scenes having DuplicateMarkForDeletion tag.')
 parser.add_argument('--remove_dup', '-r', dest='remove', action='store_true', help='Remove (delete) duplicate files.')
 parse_args = parser.parse_args()
 
 settings = {
+    "matchDupDistance": 0,
     "mergeDupFilename": False,
-    "permanentlyDelete": False,
     "whitelistDelDupInSameFolder": False,
-    "whitelistDoTagLowResDup": False,
-    "zCleanAfterDel": False,
-    "zSwapHighRes": False,
-    "zSwapLongLength": False,
+    "zvWhitelist": "",
+    "zwGraylist": "",
+    "zxBlacklist": "",
+    "zyMaxDupToProcess": 0,
+    "zySwapHighRes": False,
+    "zySwapLongLength": False,
+    "zySwapBetterBitRate": False,
+    "zySwapCodec": False,
+    "zySwapBetterFrameRate": False,
+    "zzDebug": False,
+    "zzTracing": False,
+    
+    "zzObsoleteSettingsCheckVer2": False, # This is a hidden variable that is NOT displayed in the UI
+    
+    # Obsolete setting names
     "zWhitelist": "",
     "zxGraylist": "",
     "zyBlacklist": "",
-    "zyMaxDupToProcess": 0,
-    "zzdebugTracing": False,
+    "zyMatchDupDistance": 0,
+    "zSwapHighRes": False,
+    "zSwapLongLength": False,
+    "zSwapBetterBitRate": False,
+    "zSwapCodec": False,
+    "zSwapBetterFrameRate": False,
 }
 stash = StashPluginHelper(
         stash_url=parse_args.stash_url,
@@ -35,64 +64,172 @@
         settings=settings,
         config=config,
         maxbytes=10*1024*1024,
+        DebugTraceFieldName="zzTracing",
+        DebugFieldName="zzDebug",
         )
+stash.convertToAscii = True
+
+advanceMenuOptions = [  "applyCombo", "applyComboBlacklist", "pathToDelete", "pathToDeleteBlacklist", "sizeToDeleteLess", "sizeToDeleteGreater", "sizeToDeleteBlacklistLess", "sizeToDeleteBlacklistGreater", "durationToDeleteLess", "durationToDeleteGreater", "durationToDeleteBlacklistLess", "durationToDeleteBlacklistGreater", 
+                        "commonResToDeleteLess", "commonResToDeleteEq", "commonResToDeleteGreater", "commonResToDeleteBlacklistLess", "commonResToDeleteBlacklistEq", "commonResToDeleteBlacklistGreater", "resolutionToDeleteLess", "resolutionToDeleteEq", "resolutionToDeleteGreater", 
+                        "resolutionToDeleteBlacklistLess", "resolutionToDeleteBlacklistEq", "resolutionToDeleteBlacklistGreater", "ratingToDeleteLess", "ratingToDeleteEq", "ratingToDeleteGreater", "ratingToDeleteBlacklistLess", "ratingToDeleteBlacklistEq", "ratingToDeleteBlacklistGreater", 
+                        "tagToDelete", "tagToDeleteBlacklist", "titleToDelete", "titleToDeleteBlacklist", "pathStrToDelete", "pathStrToDeleteBlacklist"]
+
+doJsonReturnModeTypes = ["tag_duplicates_task", "removeDupTag", "addExcludeTag", "removeExcludeTag", "mergeTags", "getLocalDupReportPath", 
+                         "createDuplicateReportWithoutTagging", "deleteLocalDupReportHtmlFiles", "clear_duplicate_tags_task",
+                         "deleteAllDupFileManagerTags", "deleteBlackListTaggedDuplicatesTask", "deleteTaggedDuplicatesLwrResOrLwrDuration",
+                         "deleteBlackListTaggedDuplicatesLwrResOrLwrDuration", "create_duplicate_report_task"]
+doJsonReturnModeTypes += [advanceMenuOptions]
+doJsonReturn = False
+if len(sys.argv) < 2 and stash.PLUGIN_TASK_NAME in doJsonReturnModeTypes:
+    doJsonReturn = True
+    stash.log_to_norm = stash.LogTo.FILE
+elif stash.PLUGIN_TASK_NAME == "doEarlyExit":
+    time.sleep(3)
+    stash.Log("Doing early exit because of task name")
+    time.sleep(3)
+    exit(0)
+
+stash.Log("******************* Starting   *******************")
 if len(sys.argv) > 1:
     stash.Log(f"argv = {sys.argv}")
 else:
-    stash.Trace(f"No command line arguments. JSON_INPUT['args'] = {stash.JSON_INPUT['args']}")
-stash.Status(logLevel=logging.DEBUG)
+    stash.Debug(f"No command line arguments. JSON_INPUT['args'] = {stash.JSON_INPUT['args']}; PLUGIN_TASK_NAME = {stash.PLUGIN_TASK_NAME}; argv = {sys.argv}")
+stash.status(logLevel=logging.DEBUG)
 
-# stash.Trace(f"\nStarting (__file__={__file__}) (stash.CALLED_AS_STASH_PLUGIN={stash.CALLED_AS_STASH_PLUGIN}) (stash.DEBUG_TRACING={stash.DEBUG_TRACING}) (stash.PLUGIN_TASK_NAME={stash.PLUGIN_TASK_NAME})************************************************")
-# stash.encodeToUtf8 = True
+obsoleteSettingsToConvert = {"zWhitelist" : "zvWhitelist", "zxGraylist" : "zwGraylist", "zyBlacklist" : "zxBlacklist", "zyMatchDupDistance" : "matchDupDistance", "zSwapHighRes" : "zySwapHighRes", "zSwapLongLength" : "zySwapLongLength", "zSwapBetterBitRate" : "zySwapBetterBitRate", "zSwapCodec" : "zySwapCodec", "zSwapBetterFrameRate" : "zySwapBetterFrameRate"}
+stash.replaceObsoleteSettings(obsoleteSettingsToConvert, "zzObsoleteSettingsCheckVer2")
 
 
-LOG_STASH_N_PLUGIN = stash.LOG_TO_STASH if stash.CALLED_AS_STASH_PLUGIN else stash.LOG_TO_CONSOLE + stash.LOG_TO_FILE
+LOG_STASH_N_PLUGIN = stash.LogTo.STASH if stash.CALLED_AS_STASH_PLUGIN else stash.LogTo.CONSOLE + stash.LogTo.FILE
 listSeparator               = stash.Setting('listSeparator', ',', notEmpty=True)
 addPrimaryDupPathToDetails  = stash.Setting('addPrimaryDupPathToDetails') 
+clearAllDupfileManagerTags  = stash.Setting('clearAllDupfileManagerTags')
+doGeneratePhash             = stash.Setting('doGeneratePhash')
 mergeDupFilename            = stash.Setting('mergeDupFilename')
 moveToTrashCan              = False if stash.Setting('permanentlyDelete') else True
 alternateTrashCanPath       = stash.Setting('dup_path')
 whitelistDelDupInSameFolder = stash.Setting('whitelistDelDupInSameFolder')
-whitelistDoTagLowResDup     = stash.Setting('whitelistDoTagLowResDup')
+graylistTagging             = stash.Setting('graylistTagging')
 maxDupToProcess             = int(stash.Setting('zyMaxDupToProcess'))
-swapHighRes                 = stash.Setting('zSwapHighRes')
-swapLongLength              = stash.Setting('zSwapLongLength')
-significantTimeDiff         = stash.Setting('significantTimeDiff')
+significantTimeDiff         = float(stash.Setting('significantTimeDiff'))
 toRecycleBeforeSwap         = stash.Setting('toRecycleBeforeSwap')
-cleanAfterDel               = stash.Setting('zCleanAfterDel')
-duration_diff               = float(stash.Setting('duration_diff'))
-if duration_diff > 10:
-    duration_diff = 10
-elif duration_diff < 1:
-    duration_diff = 1
+cleanAfterDel               = stash.Setting('cleanAfterDel')
+
+swapHighRes                 = stash.Setting('zySwapHighRes')
+swapLongLength              = stash.Setting('zySwapLongLength')
+swapBetterBitRate           = stash.Setting('zySwapBetterBitRate')
+swapCodec                   = stash.Setting('zySwapCodec')
+swapBetterFrameRate         = stash.Setting('zySwapBetterFrameRate')
+favorLongerFileName         = stash.Setting('favorLongerFileName')
+favorLargerFileSize         = stash.Setting('favorLargerFileSize')
+favorBitRateChange          = stash.Setting('favorBitRateChange')
+favorHighBitRate            = stash.Setting('favorHighBitRate')
+favorFrameRateChange        = stash.Setting('favorFrameRateChange')
+favorHigherFrameRate        = stash.Setting('favorHigherFrameRate')
+favorCodecRanking           = stash.Setting('favorCodecRanking')
+codecRankingSetToUse        = stash.Setting('codecRankingSetToUse')
+if   codecRankingSetToUse == 4:
+    codecRanking            = stash.Setting('codecRankingSet4')
+elif codecRankingSetToUse == 3:
+    codecRanking            = stash.Setting('codecRankingSet3')
+elif codecRankingSetToUse == 2:
+    codecRanking            = stash.Setting('codecRankingSet2')
+else:
+    codecRanking            = stash.Setting('codecRankingSet1')
+skipIfTagged                = stash.Setting('skipIfTagged')
+killScanningPostProcess     = stash.Setting('killScanningPostProcess')
+tagLongDurationLowRes       = stash.Setting('tagLongDurationLowRes')
+bitRateIsImporantComp       = stash.Setting('bitRateIsImporantComp')
+codecIsImporantComp         = stash.Setting('codecIsImporantComp')
+
+excludeFromReportIfSignificantTimeDiff = False
+
+matchDupDistance            = int(stash.Setting('matchDupDistance'))
+matchPhaseDistance          = PhashDistance.EXACT
+matchPhaseDistanceText      = "Exact Match"
+if (stash.PLUGIN_TASK_NAME == "tag_duplicates_task" or stash.PLUGIN_TASK_NAME == "create_duplicate_report_task") and 'Target' in stash.JSON_INPUT['args']:
+    stash.enableProgressBar(False)
+    if stash.JSON_INPUT['args']['Target'].startswith("0"):
+        matchDupDistance = 0
+    elif stash.JSON_INPUT['args']['Target'].startswith("1"):
+        matchDupDistance = 1
+    elif stash.JSON_INPUT['args']['Target'].startswith("2"):
+        matchDupDistance = 2
+    elif stash.JSON_INPUT['args']['Target'].startswith("3"):
+        matchDupDistance = 3
+    
+    if stash.JSON_INPUT['args']['Target'].find(":") == 1:
+        significantTimeDiff = float(stash.JSON_INPUT['args']['Target'][2:])
+        excludeFromReportIfSignificantTimeDiff = True
+
+if matchDupDistance == 1:
+    matchPhaseDistance      = PhashDistance.HIGH
+    matchPhaseDistanceText  = "High Match"
+elif matchDupDistance == 2:
+    matchPhaseDistance      = PhashDistance.MEDIUM
+    matchPhaseDistanceText  = "Medium Match"
+elif matchDupDistance == 3:
+    matchPhaseDistance      = PhashDistance.LOW
+    matchPhaseDistanceText  = "Low Match"
 
 # significantTimeDiff can not be higher than 1 and shouldn't be lower than .5
 if significantTimeDiff > 1:
-    significantTimeDiff = 1
-if significantTimeDiff < .5:
-    significantTimeDiff = .5
+    significantTimeDiff = float(1.00)
+if significantTimeDiff < .25:
+    significantTimeDiff = float(0.25)
 
 
 duplicateMarkForDeletion = stash.Setting('DupFileTag')
 if duplicateMarkForDeletion == "":
     duplicateMarkForDeletion = 'DuplicateMarkForDeletion'
 
+base1_duplicateMarkForDeletion = duplicateMarkForDeletion
+
 duplicateWhitelistTag = stash.Setting('DupWhiteListTag')
 if duplicateWhitelistTag == "":
-    duplicateWhitelistTag = 'DuplicateWhitelistFile'
+    duplicateWhitelistTag = '_DuplicateWhitelistFile'
+
+excludeDupFileDeleteTag = stash.Setting('excludeDupFileDeleteTag')
+if excludeDupFileDeleteTag == "":
+    excludeDupFileDeleteTag = '_ExcludeDuplicateMarkForDeletion'
+
+graylistMarkForDeletion = stash.Setting('graylistMarkForDeletion')
+if graylistMarkForDeletion == "":
+    graylistMarkForDeletion = '_GraylistMarkForDeletion'
+
+longerDurationLowerResolution = stash.Setting('longerDurationLowerResolution')
+if longerDurationLowerResolution == "":
+    longerDurationLowerResolution = '_LongerDurationLowerResolution'
 
-excludeMergeTags = [duplicateMarkForDeletion, duplicateWhitelistTag]
-stash.init_mergeMetadata(excludeMergeTags)
+excludeMergeTags = [duplicateMarkForDeletion, duplicateWhitelistTag, excludeDupFileDeleteTag]
 
-graylist = stash.Setting('zxGraylist').split(listSeparator)
+if stash.Setting('underscoreDupFileTag') and not duplicateMarkForDeletion.startswith('_'):
+    duplicateMarkForDeletionWithOutUnderscore = duplicateMarkForDeletion
+    duplicateMarkForDeletion = "_" + duplicateMarkForDeletion
+    if stash.renameTag(duplicateMarkForDeletionWithOutUnderscore, duplicateMarkForDeletion):
+        stash.Log(f"Renamed tag {duplicateMarkForDeletionWithOutUnderscore} to {duplicateMarkForDeletion}")
+    stash.Trace(f"Added underscore to {duplicateMarkForDeletionWithOutUnderscore} = {duplicateMarkForDeletion}")
+    excludeMergeTags += [duplicateMarkForDeletion]
+else:
+    stash.Trace(f"duplicateMarkForDeletion = {duplicateMarkForDeletion}")
+
+base2_duplicateMarkForDeletion = duplicateMarkForDeletion
+
+if stash.Setting('appendMatchDupDistance'):
+    duplicateMarkForDeletion += f"_{matchDupDistance}"
+    excludeMergeTags += [duplicateMarkForDeletion]
+
+stash.initMergeMetadata(excludeMergeTags)
+
+graylist = stash.Setting('zwGraylist').split(listSeparator)
 graylist = [item.lower() for item in graylist]
 if graylist == [""] : graylist = []
 stash.Trace(f"graylist = {graylist}")   
-whitelist = stash.Setting('zWhitelist').split(listSeparator)
+whitelist = stash.Setting('zvWhitelist').split(listSeparator)
 whitelist = [item.lower() for item in whitelist]
 if whitelist == [""] : whitelist = []
 stash.Trace(f"whitelist = {whitelist}")   
-blacklist = stash.Setting('zyBlacklist').split(listSeparator)
+blacklist = stash.Setting('zxBlacklist').split(listSeparator)
 blacklist = [item.lower() for item in blacklist]
 if blacklist == [""] : blacklist = []
 stash.Trace(f"blacklist = {blacklist}")
@@ -169,51 +306,49 @@ def testReparsePointAndSymLink(merge=False, deleteDup=False):
         stash.Log(f"Not isSymLink '{myTestPath6}'")
     return
 
+detailPrefix = "BaseDup="
+detailPostfix = "<BaseDup>\n"
 
-def createTagId(tagName, tagName_descp, deleteIfExist = False):
-    tagId = stash.find_tags(q=tagName)
-    if len(tagId):
-        tagId = tagId[0]
-        if deleteIfExist:
-            stash.destroy_tag(int(tagId['id']))
-        else:
-            return tagId['id']
-    tagId = stash.create_tag({"name":tagName, "description":tagName_descp, "ignore_auto_tag": True})
-    stash.Log(f"Dup-tagId={tagId['id']}")
-    return tagId['id']
-
-def setTagId(tagId, tagName, sceneDetails, DupFileToKeep):
+def setTagId(tagName, sceneDetails, DupFileToKeep, TagReason="", ignoreAutoTag=False):
     details = ""
     ORG_DATA_DICT = {'id' : sceneDetails['id']}
     dataDict = ORG_DATA_DICT.copy()
     doAddTag = True
     if addPrimaryDupPathToDetails:
-        BaseDupStr = f"BaseDup={DupFileToKeep['files'][0]['path']}\n{stash.STASH_URL}/scenes/{DupFileToKeep['id']}\n"
+        BaseDupStr = f"{detailPrefix}{DupFileToKeep['files'][0]['path']}\n{stash.STASH_URL}/scenes/{DupFileToKeep['id']}\n{TagReason}(matchDupDistance={matchPhaseDistanceText})\n{detailPostfix}"
         if sceneDetails['details'] == "":
             details = BaseDupStr
-        elif not sceneDetails['details'].startswith(BaseDupStr):
+        elif not sceneDetails['details'].startswith(detailPrefix):
             details = f"{BaseDupStr};\n{sceneDetails['details']}"
     for tag in sceneDetails['tags']:
         if tag['name'] == tagName:
             doAddTag = False
             break
     if doAddTag:
-        dataDict.update({'tag_ids' : tagId})
+        stash.addTag(sceneDetails, tagName, ignoreAutoTag=ignoreAutoTag)
     if details != "":
         dataDict.update({'details' : details})
     if dataDict != ORG_DATA_DICT:
-        stash.update_scene(dataDict)
-        stash.Trace(f"[setTagId] Updated {sceneDetails['files'][0]['path']} with metadata {dataDict}", toAscii=True)
+        stash.updateScene(dataDict)
+        stash.Trace(f"[setTagId] Updated {sceneDetails['files'][0]['path']} with metadata {dataDict} and tag {tagName}", toAscii=True)
     else:
-        stash.Trace(f"[setTagId] Nothing to update {sceneDetails['files'][0]['path']}.", toAscii=True)
-
+        stash.Trace(f"[setTagId] Nothing to update {sceneDetails['files'][0]['path']} already has tag {tagName}.", toAscii=True)
+    return doAddTag
 
-def isInList(listToCk, pathToCk):
-    pathToCk = pathToCk.lower()
-    for item in listToCk:
-        if pathToCk.startswith(item):
-            return True
-    return False
+def setTagId_withRetry(tagName, sceneDetails, DupFileToKeep, TagReason="", ignoreAutoTag=False, retryCount = 12, sleepSecondsBetweenRetry = 5):
+    errMsg = None
+    for i in range(0, retryCount):
+        try:
+            if errMsg != None:
+                stash.Warn(errMsg)
+            return setTagId(tagName, sceneDetails, DupFileToKeep, TagReason, ignoreAutoTag)
+        except (requests.exceptions.ConnectionError, ConnectionResetError):
+            tb = traceback.format_exc()
+            errMsg = f"[setTagId] Exception calling setTagId. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+        except Exception as e:
+            tb = traceback.format_exc()
+            errMsg = f"[setTagId] Unknown exception calling setTagId. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+        time.sleep(sleepSecondsBetweenRetry)
 
 def hasSameDir(path1, path2):
     if pathlib.Path(path1).resolve().parent == pathlib.Path(path2).resolve().parent:
@@ -237,39 +372,284 @@ def sendToTrash(path):
         except Exception as e:
             stash.Error(f"Failed to delete file {path}. Error: {e}", toAscii=True)
     return False
-
-def significantLessTime(durrationToKeep, durrationOther):
-    timeDiff = durrationToKeep / durrationOther
+# If ckTimeDiff=False: Does durration2 have significant more time than durration1
+def significantTimeDiffCheck(durration1, durration2, ckTimeDiff = False): # If ckTimeDiff=True: is time different significant in either direction. 
+    if not isinstance(durration1, int) and 'files' in durration1:
+        durration1 = int(durration1['files'][0]['duration'])
+        durration2 = int(durration2['files'][0]['duration'])
+    timeDiff = getTimeDif(durration1, durration2)
+    if ckTimeDiff and timeDiff > 1:
+        timeDiff = getTimeDif(durration2, durration1)
     if timeDiff < significantTimeDiff:
         return True
     return False
 
+def getTimeDif(durration1, durration2): # Where durration1 is ecpected to be smaller than durration2 IE(45/60=.75)
+    return durration1 / durration2
+
+def isBetterVideo(scene1, scene2, swapCandidateCk = False): # is scene2 better than scene1
+    # Prioritize higher reslution over codec, bit rate, and frame rate
+    if int(scene1['files'][0]['width']) * int(scene1['files'][0]['height']) > int(scene2['files'][0]['width']) * int(scene2['files'][0]['height']):
+        return False
+    if (favorBitRateChange and swapCandidateCk == False) or (swapCandidateCk and swapBetterBitRate):
+        if (favorHighBitRate and int(scene2['files'][0]['bit_rate']) > int(scene1['files'][0]['bit_rate'])) or (not favorHighBitRate and int(scene2['files'][0]['bit_rate']) < int(scene1['files'][0]['bit_rate'])):
+            stash.Trace(f"[isBetterVideo]:[favorHighBitRate={favorHighBitRate}] Better bit rate. {scene1['files'][0]['path']}={scene1['files'][0]['bit_rate']} v.s. {scene2['files'][0]['path']}={scene2['files'][0]['bit_rate']}")
+            return True
+    if (favorCodecRanking and swapCandidateCk == False) or (swapCandidateCk and swapCodec):
+        scene1CodecRank = stash.indexStartsWithInList(codecRanking, scene1['files'][0]['video_codec'])
+        scene2CodecRank = stash.indexStartsWithInList(codecRanking, scene2['files'][0]['video_codec'])
+        if scene2CodecRank < scene1CodecRank:
+            stash.Trace(f"[isBetterVideo] Better codec. {scene1['files'][0]['path']}={scene1['files'][0]['video_codec']}:Rank={scene1CodecRank} v.s. {scene2['files'][0]['path']}={scene2['files'][0]['video_codec']}:Rank={scene2CodecRank}")
+            return True
+    if (favorFrameRateChange and swapCandidateCk == False) or (swapCandidateCk and swapBetterFrameRate):
+        if (favorHigherFrameRate and int(scene2['files'][0]['frame_rate']) > int(scene1['files'][0]['frame_rate'])) or (not favorHigherFrameRate and int(scene2['files'][0]['frame_rate']) < int(scene1['files'][0]['frame_rate'])):
+            stash.Trace(f"[isBetterVideo]:[favorHigherFrameRate={favorHigherFrameRate}] Better frame rate. {scene1['files'][0]['path']}={scene1['files'][0]['frame_rate']} v.s. {scene2['files'][0]['path']}={scene2['files'][0]['frame_rate']}")
+            return True
+    return False
+
+def significantMoreTimeCompareToBetterVideo(scene1, scene2): # is scene2 better than scene1
+    if isinstance(scene1, int):
+        scene1 = stash.find_scene(scene1)
+        scene2 = stash.find_scene(scene2)
+    if int(scene1['files'][0]['duration']) >= int(scene2['files'][0]['duration']):
+        return False
+    if int(scene1['files'][0]['width']) * int(scene1['files'][0]['height']) > int(scene2['files'][0]['width']) * int(scene2['files'][0]['height']):
+        if significantTimeDiffCheck(scene1, scene2):
+            if tagLongDurationLowRes:
+                didAddTag = setTagId_withRetry(longerDurationLowerResolution, scene2, scene1, ignoreAutoTag=True)
+                stash.Log(f"Tagged sene2 with tag {longerDurationLowerResolution}, because scene1 is better video, but it has significant less time ({getTimeDif(int(scene1['files'][0]['duration']), int(scene2['files'][0]['duration']))}%) compare to scene2; scene1={scene1['files'][0]['path']} (ID={scene1['id']})(duration={scene1['files'][0]['duration']}); scene2={scene2['files'][0]['path']} (ID={scene2['id']}) (duration={scene1['files'][0]['duration']}); didAddTag={didAddTag}")
+            else:
+                stash.Warn(f"Scene1 is better video, but it has significant less time ({getTimeDif(int(scene1['files'][0]['duration']), int(scene2['files'][0]['duration']))}%) compare to scene2; Scene1={scene1['files'][0]['path']} (ID={scene1['id']})(duration={scene1['files'][0]['duration']}); Scene2={scene2['files'][0]['path']} (ID={scene2['id']}) (duration={scene1['files'][0]['duration']})")            
+        return False
+    return True
+
+def allThingsEqual(scene1, scene2): # If all important things are equal, return true
+    if int(scene1['files'][0]['duration']) != int(scene2['files'][0]['duration']):
+        return False
+    if scene1['files'][0]['width'] != scene2['files'][0]['width']:
+        return False
+    if scene1['files'][0]['height'] != scene2['files'][0]['height']:
+        return False
+    if bitRateIsImporantComp and scene1['files'][0]['bit_rate'] != scene2['files'][0]['bit_rate']:
+        return False
+    if codecIsImporantComp and scene1['files'][0]['video_codec'] != scene2['files'][0]['video_codec']:
+        return False
+    return True
+
 def isSwapCandidate(DupFileToKeep, DupFile):
     # Don't move if both are in whitelist
-    if isInList(whitelist, DupFileToKeep['files'][0]['path']) and isInList(whitelist, DupFile['files'][0]['path']):
+    if stash.startsWithInList(whitelist, DupFileToKeep['files'][0]['path']) and stash.startsWithInList(whitelist, DupFile['files'][0]['path']):
         return False
-    if swapHighRes and (int(DupFileToKeep['files'][0]['width']) > int(DupFile['files'][0]['width']) or int(DupFileToKeep['files'][0]['height']) > int(DupFile['files'][0]['height'])):
-        if not significantLessTime(int(DupFileToKeep['files'][0]['duration']), int(DupFile['files'][0]['duration'])):
+    if swapHighRes and int(DupFileToKeep['files'][0]['width']) * int(DupFileToKeep['files'][0]['height']) > int(DupFile['files'][0]['width']) * int(DupFile['files'][0]['height']):
+        if not significantTimeDiffCheck(DupFileToKeep, DupFile):
             return True
         else:
             stash.Warn(f"File '{DupFileToKeep['files'][0]['path']}' has a higher resolution than '{DupFile['files'][0]['path']}', but the duration is significantly shorter.", toAscii=True)
     if swapLongLength and int(DupFileToKeep['files'][0]['duration']) > int(DupFile['files'][0]['duration']):
         if int(DupFileToKeep['files'][0]['width']) >= int(DupFile['files'][0]['width']) or int(DupFileToKeep['files'][0]['height']) >= int(DupFile['files'][0]['height']):
             return True
+    if isBetterVideo(DupFile, DupFileToKeep, swapCandidateCk=True):
+        if not significantTimeDiffCheck(DupFileToKeep, DupFile):
+            return True
+        else:
+            stash.Warn(f"File '{DupFileToKeep['files'][0]['path']}' has better codec/bit-rate than '{DupFile['files'][0]['path']}', but the duration is significantly shorter; DupFileToKeep-ID={DupFileToKeep['id']};DupFile-ID={DupFile['id']};BitRate {DupFileToKeep['files'][0]['bit_rate']} vs {DupFile['files'][0]['bit_rate']};Codec {DupFileToKeep['files'][0]['video_codec']} vs {DupFile['files'][0]['video_codec']};FrameRate {DupFileToKeep['files'][0]['frame_rate']} vs {DupFile['files'][0]['frame_rate']};", toAscii=True)
+    return False
+
+dupWhitelistTagId = None
+def addDupWhitelistTag():
+    global dupWhitelistTagId
+    stash.Trace(f"Adding tag duplicateWhitelistTag = {duplicateWhitelistTag}")    
+    descp = 'Tag added to duplicate scenes which are in the whitelist. This means there are two or more duplicates in the whitelist.'
+    dupWhitelistTagId = stash.createTagId(duplicateWhitelistTag, descp, ignoreAutoTag=True)
+    stash.Trace(f"dupWhitelistTagId={dupWhitelistTagId} name={duplicateWhitelistTag}")
+
+excludeDupFileDeleteTagId = None
+def addExcludeDupTag():
+    global excludeDupFileDeleteTagId
+    stash.Trace(f"Adding tag excludeDupFileDeleteTag = {excludeDupFileDeleteTag}")    
+    descp = 'Excludes duplicate scene from DupFileManager tagging and deletion process. A scene having this tag will not get deleted by DupFileManager'
+    excludeDupFileDeleteTagId = stash.createTagId(excludeDupFileDeleteTag, descp, ignoreAutoTag=True)
+    stash.Trace(f"dupWhitelistTagId={excludeDupFileDeleteTagId} name={excludeDupFileDeleteTag}")
+
+def isTaggedExcluded(Scene):
+    for tag in Scene['tags']:
+        if tag['name'] == excludeDupFileDeleteTag:
+            return True
+    return False
+
+def isWorseKeepCandidate(DupFileToKeep, Scene):
+    if not stash.startsWithInList(whitelist, Scene['files'][0]['path']) and stash.startsWithInList(whitelist, DupFileToKeep['files'][0]['path']):
+        return True
+    if not stash.startsWithInList(graylist, Scene['files'][0]['path']) and stash.startsWithInList(graylist, DupFileToKeep['files'][0]['path']):
+        return True
+    if not stash.startsWithInList(blacklist, DupFileToKeep['files'][0]['path']) and stash.startsWithInList(blacklist, Scene['files'][0]['path']):
+        return True
+    
+    if stash.startsWithInList(graylist, Scene['files'][0]['path']) and stash.startsWithInList(graylist, DupFileToKeep['files'][0]['path']) and stash.indexStartsWithInList(graylist, DupFileToKeep['files'][0]['path']) < stash.indexStartsWithInList(graylist, Scene['files'][0]['path']):
+        return True
+    if stash.startsWithInList(blacklist, DupFileToKeep['files'][0]['path']) and stash.startsWithInList(blacklist, Scene['files'][0]['path']) and stash.indexStartsWithInList(blacklist, DupFileToKeep['files'][0]['path']) < stash.indexStartsWithInList(blacklist, Scene['files'][0]['path']):
+        return True      
     return False
 
-def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
+def killScanningJobs():
+    try:
+        if killScanningPostProcess:
+            stash.stopJobs(1, "Scanning...")
+    except Exception as e:
+        tb = traceback.format_exc()
+        stash.Error(f"Exception while trying to kill scan jobs; Error: {e}\nTraceBack={tb}")
+
+def getPath(Scene, getParent = False):
+    path = stash.asc2(Scene['files'][0]['path'])
+    path = path.replace("'", "")
+    path = path.replace("\\\\", "\\")
+    if getParent:
+        return pathlib.Path(path).resolve().parent
+    return path
+
+def getHtmlReportTableRow(qtyResults, tagDuplicates):
+    htmlReportPrefix = stash.Setting('htmlReportPrefix')
+    htmlReportPrefix = htmlReportPrefix.replace('http://127.0.0.1:9999/graphql', stash.url)
+    htmlReportPrefix = htmlReportPrefix.replace('http://localhost:9999/graphql', stash.url)
+    if tagDuplicates == False:
+        htmlReportPrefix = htmlReportPrefix.replace('<td><button id="AdvanceMenu"', '<td hidden><button id="AdvanceMenu"')
+    htmlReportPrefix = htmlReportPrefix.replace('(QtyPlaceHolder)', f'{qtyResults}')
+    htmlReportPrefix = htmlReportPrefix.replace('(MatchTypePlaceHolder)', f'(Match Type = {matchPhaseDistanceText})')
+    htmlReportPrefix = htmlReportPrefix.replace('(DateCreatedPlaceHolder)', datetime.now().strftime("%d-%b-%Y, %H:%M:%S"))
+    return htmlReportPrefix
+
+htmlReportTableData         = stash.Setting('htmlReportTableData')
+htmlDetailDiffTextColor = stash.Setting('htmlDetailDiffTextColor')
+htmlSupperHighlight     = stash.Setting('htmlSupperHighlight')
+htmlLowerHighlight      = stash.Setting('htmlLowerHighlight')
+def getColor(Scene1, Scene2, ifScene1HigherChangeColor = False, roundUpNumber = False, qtyDiff=0):
+    if (Scene1 == Scene2) or (roundUpNumber and int(Scene1) == int(Scene2)):
+        return ""
+    if ifScene1HigherChangeColor and int(Scene1) > int(Scene2):
+        if (int(Scene1) - int(Scene2)) > qtyDiff:
+            return f' style="color:{htmlDetailDiffTextColor};background-color:{htmlSupperHighlight};"'
+        return f' style="color:{htmlDetailDiffTextColor};background-color:{htmlLowerHighlight};"'
+    return f' style="color:{htmlDetailDiffTextColor};"'
+
+def getRes(Scene):
+    return int(Scene['files'][0]['width']) * int(Scene['files'][0]['height'])
+
+reasonDict = {}
+
+def logReason(DupFileToKeep, Scene, reason):
+    global reasonDict
+    reasonDict[Scene['id']] = reason
+    reasonDict[DupFileToKeep['id']] = reason
+    stash.Debug(f"Replacing {DupFileToKeep['files'][0]['path']} with {Scene['files'][0]['path']} for candidate to keep. Reason={reason}")
+
+
+def getSceneID(scene):
+    return htmlReportTableData.replace("<td", f"<td class=\"ID_{scene['id']}\" ")
+
+def fileNameClassID(scene):
+    return f" class=\"FN_ID_{scene['id']}\" "
+
+htmlReportNameFolder        = f"{stash.PLUGINS_PATH}{os.sep}DupFileManager{os.sep}report"
+htmlReportName              = f"{htmlReportNameFolder}{os.sep}{stash.Setting('htmlReportName')}"
+htmlReportTableRow          = stash.Setting('htmlReportTableRow')
+htmlIncludeImagePreview     = stash.Setting('htmlIncludeImagePreview')
+htmlImagePreviewPopupSize   = stash.Setting('htmlImagePreviewPopupSize')
+htmlReportVideoPreview      = stash.Setting('htmlReportVideoPreview')
+htmlHighlightTimeDiff       = stash.Setting('htmlHighlightTimeDiff')
+htmlPreviewOrStream         = "stream" if stash.Setting('streamOverPreview') else "preview"
+
+def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel = "?", tagDuplicates = False):
+    dupFileExist = True if os.path.isfile(DupFile['files'][0]['path']) else False
+    toKeepFileExist = True if os.path.isfile(DupFileToKeep['files'][0]['path']) else False
+    fileHtmlReport.write(f"{htmlReportTableRow}")
+    videoPreview = f"<video {htmlReportVideoPreview} poster=\"{DupFile['paths']['screenshot']}\"><source src=\"{DupFile['paths'][htmlPreviewOrStream]}\" type=\"video/mp4\"></video>"
+    if htmlIncludeImagePreview:
+        imagePreview = f"<ul><li><img src=\"{DupFile['paths']['sprite']}\" alt=\"\" width=\"140\"><span class=\"large\"><img src=\"{DupFile['paths']['sprite']}\" class=\"large-image\" alt=\"\" width=\"{htmlImagePreviewPopupSize}\"></span></li></ul>"
+        fileHtmlReport.write(f"{getSceneID(DupFile)}<table><tr><td>{videoPreview}</td><td>{imagePreview}</td></tr></table></td>")
+    else:
+        fileHtmlReport.write(f"{getSceneID(DupFile)}{videoPreview}</td>")
+    fileHtmlReport.write(f"{getSceneID(DupFile)}<a href=\"{stash.STASH_URL}/scenes/{DupFile['id']}\" target=\"_blank\" rel=\"noopener noreferrer\" {fileNameClassID(DupFile)}>{getPath(DupFile)}</a>")
+    fileHtmlReport.write(f"<p><table><tr class=\"scene-details\"><th>Res</th><th>Durration</th><th>BitRate</th><th>Codec</th><th>FrameRate</th><th>size</th><th>ID</th><th>index</th></tr>")
+    fileHtmlReport.write(f"<tr class=\"scene-details\"><td {getColor(getRes(DupFile), getRes(DupFileToKeep), True)}>{DupFile['files'][0]['width']}x{DupFile['files'][0]['height']}</td><td {getColor(DupFile['files'][0]['duration'], DupFileToKeep['files'][0]['duration'], True, True, htmlHighlightTimeDiff)}>{DupFile['files'][0]['duration']}</td><td {getColor(DupFile['files'][0]['bit_rate'], DupFileToKeep['files'][0]['bit_rate'])}>{DupFile['files'][0]['bit_rate']}</td><td {getColor(DupFile['files'][0]['video_codec'], DupFileToKeep['files'][0]['video_codec'])}>{DupFile['files'][0]['video_codec']}</td><td {getColor(DupFile['files'][0]['frame_rate'], DupFileToKeep['files'][0]['frame_rate'])}>{DupFile['files'][0]['frame_rate']}</td><td {getColor(DupFile['files'][0]['size'], DupFileToKeep['files'][0]['size'])}>{DupFile['files'][0]['size']}</td><td>{DupFile['id']}</td><td>{QtyTagForDel}</td></tr>")
+    
+    if DupFile['id'] in reasonDict:
+        fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: {reasonDict[DupFile['id']]}</td></tr>")
+    # elif DupFileToKeep['id'] in reasonDict:
+        # fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: {reasonDict[DupFileToKeep['id']]}</td></tr>")
+    elif int(DupFileToKeep['files'][0]['width']) * int(DupFileToKeep['files'][0]['height']) > int(DupFile['files'][0]['width']) * int(DupFile['files'][0]['height']):
+        fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: Resolution {DupFile['files'][0]['width']}x{DupFile['files'][0]['height']} < {DupFileToKeep['files'][0]['width']}x{DupFileToKeep['files'][0]['height']}</td></tr>")
+    elif significantMoreTimeCompareToBetterVideo(DupFile, DupFileToKeep):
+        if significantTimeDiffCheck(DupFile, DupFileToKeep):
+            theReason = f"Significant-Duration: <b style='color:red;background-color:neon green;'>{DupFile['files'][0]['duration']} < {DupFileToKeep['files'][0]['duration']}</b>"
+        else:
+            theReason = f"Duration: {DupFile['files'][0]['duration']} < {DupFileToKeep['files'][0]['duration']}"
+        fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: {theReason}</td></tr>")
+    elif isBetterVideo(DupFile, DupFileToKeep):
+        fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: Better Video</td></tr>")
+    elif stash.startsWithInList(DupFileToKeep, DupFile['files'][0]['path']) and not stash.startsWithInList(whitelist, DupFile['files'][0]['path']):
+        fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: not whitelist vs whitelist</td></tr>")
+    elif isTaggedExcluded(DupFileToKeep) and not isTaggedExcluded(DupFile):
+        fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: not ExcludeTag vs ExcludeTag</td></tr>")
+    
+    fileHtmlReport.write("</table>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Delete file and remove scene from stash\" value=\"deleteScene\" id=\"{DupFile['id']}\">[Delete]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove scene from stash only. Do NOT delete file.\" value=\"removeScene\" id=\"{DupFile['id']}\">[Remove]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Copy duplicate to file-to-keep.\" value=\"copyScene\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Copy]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Replace file-to-keep with this duplicate, and copy metadata from this duplicate to file-to-keep.\" value=\"moveScene\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Move]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Replace file-to-keep file name with this duplicate file name.\" value=\"renameFile\" id=\"{DupFileToKeep['id']}:{stash.asc2(pathlib.Path(DupFile['files'][0]['path']).stem)}\">[CpyName]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFile['id']}\">[Flag]</button>")
+    # ToDo: Add following buttons:
+    #       rename file
+    if dupFileExist and tagDuplicates:
+        fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove duplicate tag from scene.\" value=\"removeDupTag\" id=\"{DupFile['id']}\">[-Tag]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Add exclude tag to scene. This will exclude scene from deletion via deletion tag\" value=\"addExcludeTag\" id=\"{DupFile['id']}\">[+Exclude]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Merge duplicate scene tags with ToKeep scene tags\" value=\"mergeTags\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Merge Tags]</button>")
+    if dupFileExist:
+        fileHtmlReport.write(f"<a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFile, True)}\">[Folder]</a>")
+        fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFile)}\">[Play]</a>")
+    else:
+        fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
+    fileHtmlReport.write("</p></td>")
+    
+    videoPreview = f"<video {htmlReportVideoPreview} poster=\"{DupFileToKeep['paths']['screenshot']}\"><source src=\"{DupFileToKeep['paths'][htmlPreviewOrStream]}\" type=\"video/mp4\"></video>"
+    if htmlIncludeImagePreview:
+        imagePreview = f"<ul><li><img src=\"{DupFileToKeep['paths']['sprite']}\" alt=\"\" width=\"140\"><span class=\"large\"><img src=\"{DupFileToKeep['paths']['sprite']}\" class=\"large-image\" alt=\"\" width=\"{htmlImagePreviewPopupSize}\"></span></li></ul>"
+        fileHtmlReport.write(f"{getSceneID(DupFileToKeep)}<table><tr><td>{videoPreview}</td><td>{imagePreview}</td></tr></table></td>")
+    else:
+        fileHtmlReport.write(f"{getSceneID(DupFileToKeep)}{videoPreview}</td>")
+    fileHtmlReport.write(f"{getSceneID(DupFileToKeep)}<a href=\"{stash.STASH_URL}/scenes/{DupFileToKeep['id']}\" target=\"_blank\" rel=\"noopener noreferrer\" {fileNameClassID(DupFileToKeep)}>{getPath(DupFileToKeep)}</a>")
+    fileHtmlReport.write(f"<p><table><tr class=\"scene-details\"><th>Res</th><th>Durration</th><th>BitRate</th><th>Codec</th><th>FrameRate</th><th>size</th><th>ID</th></tr>")
+    fileHtmlReport.write(f"<tr class=\"scene-details\"><td>{DupFileToKeep['files'][0]['width']}x{DupFileToKeep['files'][0]['height']}</td><td>{DupFileToKeep['files'][0]['duration']}</td><td>{DupFileToKeep['files'][0]['bit_rate']}</td><td>{DupFileToKeep['files'][0]['video_codec']}</td><td>{DupFileToKeep['files'][0]['frame_rate']}</td><td>{DupFileToKeep['files'][0]['size']}</td><td>{DupFileToKeep['id']}</td></tr></table>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Delete [DupFileToKeep] and remove scene from stash\" value=\"deleteScene\" id=\"{DupFileToKeep['id']}\">[Delete]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove scene from stash only. Do NOT delete file.\" value=\"removeScene\" id=\"{DupFileToKeep['id']}\">[Remove]</button>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Rename file-to-keep.\" value=\"newName\" id=\"{DupFileToKeep['id']}:{stash.asc2(pathlib.Path(DupFileToKeep['files'][0]['path']).stem)}\">[Rename]</button>")
+    if isTaggedExcluded(DupFileToKeep):
+        fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove exclude scene from deletion tag\" value=\"removeExcludeTag\" id=\"{DupFileToKeep['id']}\">[-Exclude]</button>")
+    fileHtmlReport.write(f"<a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFileToKeep, True)}\">[Folder]</a>")
+    if toKeepFileExist:
+        fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFileToKeep)}\">[Play]</a>")
+    else:
+        fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
+    fileHtmlReport.write(f"<button class=\"link-button\" title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFileToKeep['id']}\">[Flag]</button>")
+    # ToDo: Add following buttons:
+    #       rename file
+    fileHtmlReport.write(f"</p></td>")
+    
+    fileHtmlReport.write("</tr>\n")
+
+def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlacklistOnly=False, deleteLowerResAndDuration=False):
+    global reasonDict
     duplicateMarkForDeletion_descp = 'Tag added to duplicate scenes so-as to tag them for deletion.'
     stash.Trace(f"duplicateMarkForDeletion = {duplicateMarkForDeletion}")    
-    dupTagId = createTagId(duplicateMarkForDeletion, duplicateMarkForDeletion_descp)
+    dupTagId = stash.createTagId(duplicateMarkForDeletion, duplicateMarkForDeletion_descp, ignoreAutoTag=True)
     stash.Trace(f"dupTagId={dupTagId} name={duplicateMarkForDeletion}")
+    createHtmlReport            = stash.Setting('createHtmlReport')
+    htmlReportNameHomePage      = htmlReportName
+    htmlReportPaginate          = stash.Setting('htmlReportPaginate')
+    
     
-    dupWhitelistTagId = None
-    if whitelistDoTagLowResDup:
-        stash.Trace(f"duplicateWhitelistTag = {duplicateWhitelistTag}")    
-        duplicateWhitelistTag_descp = 'Tag added to duplicate scenes which are in the whitelist. This means there are two or more duplicates in the whitelist.'
-        dupWhitelistTagId = createTagId(duplicateWhitelistTag, duplicateWhitelistTag_descp)
-        stash.Trace(f"dupWhitelistTagId={dupWhitelistTagId} name={duplicateWhitelistTag}")
+    addDupWhitelistTag()
+    addExcludeDupTag()
     
     QtyDupSet = 0
     QtyDup = 0
@@ -277,187 +657,897 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
     QtyAlmostDup = 0
     QtyRealTimeDiff = 0
     QtyTagForDel = 0
+    QtyTagForDelPaginate = 0
+    PaginateId = 0
+    QtyNewlyTag = 0
     QtySkipForDel = 0
+    QtyExcludeForDel = 0
     QtySwap = 0
     QtyMerge = 0
     QtyDeleted = 0
     stash.Log("#########################################################################")
     stash.Trace("#########################################################################")
-    stash.Log(f"Waiting for find_duplicate_scenes_diff to return results; duration_diff={duration_diff}; significantTimeDiff={significantTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
-    DupFileSets = stash.find_duplicate_scenes_diff(duration_diff=duration_diff)
+    stash.Log(f"Waiting for find_duplicate_scenes_diff to return results; matchDupDistance={matchPhaseDistanceText}; significantTimeDiff={significantTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
+    stash.startSpinningProcessBar()
+    htmlFileData = " paths {screenshot sprite " + htmlPreviewOrStream + "} " if createHtmlReport else ""
+    mergeFieldData = " code director title rating100 date studio {id} movies {movie {id} } galleries {id} performers {id} urls " if merge else ""
+    DupFileSets = stash.find_duplicate_scenes(matchPhaseDistance, fragment='id tags {id name} files {path width height duration size video_codec bit_rate frame_rate} details ' + mergeFieldData + htmlFileData)
+    stash.stopSpinningProcessBar()
     qtyResults = len(DupFileSets)
+    stash.setProgressBarIter(qtyResults)
     stash.Trace("#########################################################################")
+    stash.Log(f"Found {qtyResults} duplicate sets...")
+    fileHtmlReport = None
+    if createHtmlReport:
+        if not os.path.isdir(htmlReportNameFolder):
+            os.mkdir(htmlReportNameFolder)
+            if not os.path.isdir(htmlReportNameFolder):
+                stash.Error(f"Failed to create report directory {htmlReportNameFolder}.")
+                return
+        deleteLocalDupReportHtmlFiles(False)
+        fileHtmlReport = open(htmlReportName, "w")
+        fileHtmlReport.write(f"{getHtmlReportTableRow(qtyResults, tagDuplicates)}\n")
+        fileHtmlReport.write(f"{stash.Setting('htmlReportTable')}\n")
+        htmlReportTableHeader   = stash.Setting('htmlReportTableHeader')
+        fileHtmlReport.write(f"{htmlReportTableRow}{htmlReportTableHeader}Scene</th>{htmlReportTableHeader}Duplicate to Delete</th>{htmlReportTableHeader}Scene-ToKeep</th>{htmlReportTableHeader}Duplicate to Keep</th></tr>\n")
+    
     for DupFileSet in DupFileSets:
-        stash.Trace(f"DupFileSet={DupFileSet}")
+        # stash.Trace(f"DupFileSet={DupFileSet}", toAscii=True)
         QtyDupSet+=1
-        stash.Progress(QtyDupSet, qtyResults)
+        stash.progressBar(QtyDupSet, qtyResults)
         SepLine = "---------------------------"
-        DupFileToKeep = ""
+        DupFileToKeep = None
         DupToCopyFrom = ""
         DupFileDetailList = []
         for DupFile in DupFileSet:
             QtyDup+=1
-            stash.log.sl.progress(f"Scene ID = {DupFile['id']}")
-            time.sleep(2)
-            Scene = stash.find_scene(DupFile['id'])
-            sceneData = f"Scene = {Scene}"
-            stash.Trace(sceneData, toAscii=True)
+            Scene = DupFile
+            if skipIfTagged and createHtmlReport == False and duplicateMarkForDeletion in Scene['tags']:
+                stash.Trace(f"Skipping scene '{Scene['files'][0]['path']}' because already tagged with {duplicateMarkForDeletion}")
+                continue
+            stash.TraceOnce(f"Scene = {Scene}", toAscii=True)
             DupFileDetailList = DupFileDetailList + [Scene]
-            if DupFileToKeep != "":
-                if int(DupFileToKeep['files'][0]['duration']) == int(Scene['files'][0]['duration']): # Do not count fractions of a second as a difference
-                    QtyExactDup+=1
+            if os.path.isfile(Scene['files'][0]['path']):
+                if DupFileToKeep != None:
+                    if int(DupFileToKeep['files'][0]['duration']) == int(Scene['files'][0]['duration']): # Do not count fractions of a second as a difference
+                        QtyExactDup+=1
+                    else:
+                        QtyAlmostDup+=1
+                        SepLine = "***************************"
+                        if significantTimeDiffCheck(DupFileToKeep, Scene):
+                            QtyRealTimeDiff += 1
+                    
+                    if int(DupFileToKeep['files'][0]['width']) * int(DupFileToKeep['files'][0]['height']) < int(Scene['files'][0]['width']) * int(Scene['files'][0]['height']):
+                        logReason(DupFileToKeep, Scene, f"resolution: {DupFileToKeep['files'][0]['width']}x{DupFileToKeep['files'][0]['height']} < {Scene['files'][0]['width']}x{Scene['files'][0]['height']}")
+                        DupFileToKeep = Scene
+                    elif significantMoreTimeCompareToBetterVideo(DupFileToKeep, Scene):
+                        if significantTimeDiffCheck(DupFileToKeep, Scene):
+                            theReason = f"significant-duration: <b style='color:red;background-color:bright green;'>{DupFileToKeep['files'][0]['duration']} < {Scene['files'][0]['duration']}</b>"
+                        else:
+                            theReason = f"duration: {DupFileToKeep['files'][0]['duration']} < {Scene['files'][0]['duration']}"
+                        reasonKeyword = "significant-duration" if significantTimeDiffCheck(DupFileToKeep, Scene) else "duration"
+                        logReason(DupFileToKeep, Scene, theReason)
+                        DupFileToKeep = Scene
+                    elif isBetterVideo(DupFileToKeep, Scene):
+                        logReason(DupFileToKeep, Scene, f"codec,bit_rate, or frame_rate: {DupFileToKeep['files'][0]['video_codec']}, {DupFileToKeep['files'][0]['bit_rate']}, {DupFileToKeep['files'][0]['frame_rate']} : {Scene['files'][0]['video_codec']}, {Scene['files'][0]['bit_rate']}, {Scene['files'][0]['frame_rate']}")
+                        DupFileToKeep = Scene
+                    elif stash.startsWithInList(whitelist, Scene['files'][0]['path']) and not stash.startsWithInList(whitelist, DupFileToKeep['files'][0]['path']):
+                        logReason(DupFileToKeep, Scene, f"not whitelist vs whitelist")
+                        DupFileToKeep = Scene
+                    elif isTaggedExcluded(Scene) and not isTaggedExcluded(DupFileToKeep):
+                        logReason(DupFileToKeep, Scene, f"not ExcludeTag vs ExcludeTag")
+                        DupFileToKeep = Scene
+                    elif allThingsEqual(DupFileToKeep, Scene):
+                        # Only do below checks if all imporant things are equal.
+                        if stash.startsWithInList(blacklist, DupFileToKeep['files'][0]['path']) and not stash.startsWithInList(blacklist, Scene['files'][0]['path']):
+                            logReason(DupFileToKeep, Scene, f"blacklist vs not blacklist")
+                            DupFileToKeep = Scene
+                        elif stash.startsWithInList(blacklist, DupFileToKeep['files'][0]['path']) and stash.startsWithInList(blacklist, Scene['files'][0]['path']) and stash.indexStartsWithInList(blacklist, DupFileToKeep['files'][0]['path']) > stash.indexStartsWithInList(blacklist, Scene['files'][0]['path']):
+                            logReason(DupFileToKeep, Scene, f"blacklist-index {stash.indexStartsWithInList(blacklist, DupFileToKeep['files'][0]['path'])} > {stash.indexStartsWithInList(blacklist, Scene['files'][0]['path'])}")
+                            DupFileToKeep = Scene
+                        elif stash.startsWithInList(graylist, Scene['files'][0]['path']) and not stash.startsWithInList(graylist, DupFileToKeep['files'][0]['path']):
+                            logReason(DupFileToKeep, Scene, f"not graylist vs graylist")
+                            DupFileToKeep = Scene
+                        elif stash.startsWithInList(graylist, Scene['files'][0]['path']) and stash.startsWithInList(graylist, DupFileToKeep['files'][0]['path']) and stash.indexStartsWithInList(graylist, DupFileToKeep['files'][0]['path']) > stash.indexStartsWithInList(graylist, Scene['files'][0]['path']):
+                            logReason(DupFileToKeep, Scene, f"graylist-index {stash.indexStartsWithInList(graylist, DupFileToKeep['files'][0]['path'])} > {stash.indexStartsWithInList(graylist, Scene['files'][0]['path'])}")
+                            DupFileToKeep = Scene
+                        elif favorLongerFileName and len(DupFileToKeep['files'][0]['path']) < len(Scene['files'][0]['path']) and not isWorseKeepCandidate(DupFileToKeep, Scene):
+                            logReason(DupFileToKeep, Scene, f"path-len {len(DupFileToKeep['files'][0]['path'])} < {len(Scene['files'][0]['path'])}")
+                            DupFileToKeep = Scene
+                        elif favorLargerFileSize and int(DupFileToKeep['files'][0]['size']) < int(Scene['files'][0]['size']) and not isWorseKeepCandidate(DupFileToKeep, Scene):
+                            logReason(DupFileToKeep, Scene, f"size {DupFileToKeep['files'][0]['size']} < {Scene['files'][0]['size']}")
+                            DupFileToKeep = Scene
+                        elif not favorLongerFileName and len(DupFileToKeep['files'][0]['path']) > len(Scene['files'][0]['path']) and not isWorseKeepCandidate(DupFileToKeep, Scene):
+                            logReason(DupFileToKeep, Scene, f"path-len {len(DupFileToKeep['files'][0]['path'])} > {len(Scene['files'][0]['path'])}")
+                            DupFileToKeep = Scene
+                        elif not favorLargerFileSize and int(DupFileToKeep['files'][0]['size']) > int(Scene['files'][0]['size']) and not isWorseKeepCandidate(DupFileToKeep, Scene):
+                            logReason(DupFileToKeep, Scene, f"size {DupFileToKeep['files'][0]['size']} > {Scene['files'][0]['size']}")
+                            DupFileToKeep = Scene
                 else:
-                    QtyAlmostDup+=1
-                    SepLine = "***************************"
-                    if significantLessTime(int(DupFileToKeep['files'][0]['duration']), int(Scene['files'][0]['duration'])):
-                        QtyRealTimeDiff += 1
-                if int(DupFileToKeep['files'][0]['width']) < int(Scene['files'][0]['width']) or int(DupFileToKeep['files'][0]['height']) < int(Scene['files'][0]['height']):
-                    DupFileToKeep = Scene
-                elif int(DupFileToKeep['files'][0]['duration']) < int(Scene['files'][0]['duration']):
-                    DupFileToKeep = Scene
-                elif isInList(whitelist, Scene['files'][0]['path']) and not isInList(whitelist, DupFileToKeep['files'][0]['path']):
-                    DupFileToKeep = Scene
-                elif isInList(blacklist, DupFileToKeep['files'][0]['path']) and not isInList(blacklist, Scene['files'][0]['path']):
-                    DupFileToKeep = Scene
-                elif isInList(graylist, Scene['files'][0]['path']) and not isInList(graylist, DupFileToKeep['files'][0]['path']):
-                    DupFileToKeep = Scene
-                elif len(DupFileToKeep['files'][0]['path']) < len(Scene['files'][0]['path']):
-                    DupFileToKeep = Scene
-                elif int(DupFileToKeep['files'][0]['size']) < int(Scene['files'][0]['size']):
                     DupFileToKeep = Scene
+                # stash.Trace(f"DupFileToKeep = {DupFileToKeep}")
+                stash.Debug(f"KeepID={DupFileToKeep['id']}, ID={DupFile['id']} duration=({Scene['files'][0]['duration']}), Size=({Scene['files'][0]['size']}), Res=({Scene['files'][0]['width']} x {Scene['files'][0]['height']}) Name={Scene['files'][0]['path']}, KeepPath={DupFileToKeep['files'][0]['path']}", toAscii=True)
             else:
-                DupFileToKeep = Scene
-            # stash.Trace(f"DupFileToKeep = {DupFileToKeep}")
-            stash.Trace(f"KeepID={DupFileToKeep['id']}, ID={DupFile['id']} duration=({Scene['files'][0]['duration']}), Size=({Scene['files'][0]['size']}), Res=({Scene['files'][0]['width']} x {Scene['files'][0]['height']}) Name={Scene['files'][0]['path']}, KeepPath={DupFileToKeep['files'][0]['path']}", toAscii=True)
+                stash.Error(f"Scene does NOT exist; path={Scene['files'][0]['path']}; ID={Scene['id']}")
         
         for DupFile in DupFileDetailList:
-            if DupFile['id'] != DupFileToKeep['id']:
+            if DupFileToKeep != None and DupFile['id'] != DupFileToKeep['id']:
                 if merge:
-                    result = stash.merge_metadata(DupFile, DupFileToKeep)
+                    result = stash.mergeMetadata(DupFile, DupFileToKeep)
                     if result != "Nothing To Merge":
                         QtyMerge += 1
-                
-                if isInList(whitelist, DupFile['files'][0]['path']) and (not whitelistDelDupInSameFolder or not hasSameDir(DupFile['files'][0]['path'], DupFileToKeep['files'][0]['path'])):
+                didAddTag = False
+                if stash.startsWithInList(whitelist, DupFile['files'][0]['path']) and (not whitelistDelDupInSameFolder or not hasSameDir(DupFile['files'][0]['path'], DupFileToKeep['files'][0]['path'])):
+                    QtySkipForDel+=1
                     if isSwapCandidate(DupFileToKeep, DupFile):
                         if merge:
-                            stash.merge_metadata(DupFileToKeep, DupFile)
+                            stash.mergeMetadata(DupFileToKeep, DupFile)
                         if toRecycleBeforeSwap:
                             sendToTrash(DupFile['files'][0]['path'])
-                        shutil.move(DupFileToKeep['files'][0]['path'], DupFile['files'][0]['path'])
-                        stash.Log(f"Moved better file '{DupFileToKeep['files'][0]['path']}' to '{DupFile['files'][0]['path']}'", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
+                        stash.Log(f"Moving better file '{DupFileToKeep['files'][0]['path']}' to '{DupFile['files'][0]['path']}'; SrcID={DupFileToKeep['id']};DescID={DupFile['id']};QtyDup={QtyDup};Set={QtyDupSet} of {qtyResults};QtySwap={QtySwap};QtySkipForDel={QtySkipForDel}", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
+                        try:
+                            shutil.move(DupFileToKeep['files'][0]['path'], DupFile['files'][0]['path'])
+                            QtySwap+=1
+                        except Exception as e:
+                            tb = traceback.format_exc()
+                            stash.Error(f"Exception while moving file '{DupFileToKeep['files'][0]['path']}' to '{DupFile['files'][0]['path']}; SrcID={DupFileToKeep['id']};DescID={DupFile['id']}'; Error: {e}\nTraceBack={tb}")
                         DupFileToKeep = DupFile
-                        QtySwap+=1
                     else:
-                        stash.Log(f"NOT processing duplicate, because it's in whitelist. '{DupFile['files'][0]['path']}'", toAscii=True)
                         if dupWhitelistTagId and tagDuplicates:
-                            setTagId(dupWhitelistTagId, duplicateWhitelistTag, DupFile, DupFileToKeep)
-                    QtySkipForDel+=1
+                            didAddTag = setTagId_withRetry(duplicateWhitelistTag, DupFile, DupFileToKeep, ignoreAutoTag=True)
+                        stash.Log(f"NOT processing duplicate, because it's in whitelist. '{DupFile['files'][0]['path']}';AddTagW={didAddTag};QtyDup={QtyDup};Set={QtyDupSet} of {qtyResults};QtySkipForDel={QtySkipForDel}", toAscii=True)
                 else:
-                    if deleteDup:
-                        DupFileName = DupFile['files'][0]['path']
-                        DupFileNameOnly = pathlib.Path(DupFileName).stem
-                        stash.Warn(f"Deleting duplicate '{DupFileName}'", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
-                        if alternateTrashCanPath != "":
-                            destPath = f"{alternateTrashCanPath }{os.sep}{DupFileNameOnly}"
-                            if os.path.isfile(destPath):
-                                destPath = f"{alternateTrashCanPath }{os.sep}_{time.time()}_{DupFileNameOnly}"
-                            shutil.move(DupFileName, destPath)
-                        elif moveToTrashCan:
-                            sendToTrash(DupFileName)
-                        stash.destroy_scene(DupFile['id'], delete_file=True)
-                        QtyDeleted += 1
-                    elif tagDuplicates:
-                        if QtyTagForDel == 0:
-                            stash.Log(f"Tagging duplicate {DupFile['files'][0]['path']} for deletion with tag {duplicateMarkForDeletion}.", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
-                        else:
-                            stash.Log(f"Tagging duplicate {DupFile['files'][0]['path']} for deletion.", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
-                        setTagId(dupTagId, duplicateMarkForDeletion, DupFile, DupFileToKeep)
-                    QtyTagForDel+=1
+                    if isTaggedExcluded(DupFile):
+                        QtyExcludeForDel+=1
+                        stash.Log(f"Excluding file {DupFile['files'][0]['path']} because tagged for exclusion via tag {excludeDupFileDeleteTag};QtyDup={QtyDup};Set={QtyDupSet} of {qtyResults}")
+                    else:
+                        # ToDo: Add merge logic here
+                        if deleteDup:
+                            DupFileName = DupFile['files'][0]['path']
+                            if not deleteBlacklistOnly or stash.startsWithInList(blacklist, DupFile['files'][0]['path']):
+                                if not deleteLowerResAndDuration or (isBetterVideo(DupFile, DupFileToKeep) and not significantMoreTimeCompareToBetterVideo(DupFileToKeep, DupFile)) or (significantMoreTimeCompareToBetterVideo(DupFile, DupFileToKeep) and not isBetterVideo(DupFileToKeep, DupFile)):
+                                    QtyDeleted += 1
+                                    DupFileNameOnly = pathlib.Path(DupFileName).stem
+                                    stash.Warn(f"Deleting duplicate '{DupFileName}';QtyDup={QtyDup};Set={QtyDupSet} of {qtyResults};QtyDeleted={QtyDeleted}", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
+                                    if alternateTrashCanPath != "":
+                                        destPath = f"{alternateTrashCanPath }{os.sep}{DupFileNameOnly}"
+                                        if os.path.isfile(destPath):
+                                            destPath = f"{alternateTrashCanPath }{os.sep}_{time.time()}_{DupFileNameOnly}"
+                                        shutil.move(DupFileName, destPath)
+                                    elif moveToTrashCan:
+                                        sendToTrash(DupFileName)
+                                    stash.destroyScene(DupFile['id'], delete_file=True)
+                        elif tagDuplicates or fileHtmlReport != None:
+                            if excludeFromReportIfSignificantTimeDiff and significantTimeDiffCheck(DupFile, DupFileToKeep, True):
+                                stash.Log(f"Skipping duplicate {DupFile['files'][0]['path']} (ID={DupFile['id']}), because of time difference greater than {significantTimeDiff} for file {DupFileToKeep['files'][0]['path']}.")
+                                continue
+                            QtyTagForDel+=1
+                            QtyTagForDelPaginate+=1
+                            didAddTag = False
+                            if tagDuplicates:
+                                didAddTag = setTagId_withRetry(duplicateMarkForDeletion, DupFile, DupFileToKeep, ignoreAutoTag=True)
+                            if fileHtmlReport != None:
+                                # ToDo: Add icons using github path
+                                #    add copy button with copy icon
+                                #    add move button with r-sqr icon
+                                #    repace delete button with trashcan icon
+                                #    add rename file code and button
+                                #    add delete only from stash db code and button using DB delete icon
+                                stash.Debug(f"Adding scene {DupFile['id']} to HTML report.")
+                                writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel, tagDuplicates)
+                                if QtyTagForDelPaginate >= htmlReportPaginate:
+                                    QtyTagForDelPaginate = 0
+                                    fileHtmlReport.write("</table>\n")
+                                    homeHtmReportLink = f"<a class=\"link-items\" title=\"Home Page\" href=\"file://{htmlReportNameHomePage}\">[Home]</a>"
+                                    prevHtmReportLink = ""
+                                    if PaginateId > 0:
+                                        if PaginateId > 1:
+                                            prevHtmReport = htmlReportNameHomePage.replace(".html", f"_{PaginateId-1}.html")
+                                        else:
+                                            prevHtmReport = htmlReportNameHomePage
+                                        prevHtmReportLink = f"<a class=\"link-items\" title=\"Previous Page\" href=\"file://{prevHtmReport}\">[Prev]</a>"
+                                    nextHtmReport = htmlReportNameHomePage.replace(".html", f"_{PaginateId+1}.html")
+                                    nextHtmReportLink = f"<a class=\"link-items\" title=\"Next Page\" href=\"file://{nextHtmReport}\">[Next]</a>"
+                                    fileHtmlReport.write(f"<center><table><tr><td>{homeHtmReportLink}</td><td>{prevHtmReportLink}</td><td>{nextHtmReportLink}</td></tr></table></center>")
+                                    fileHtmlReport.write(f"{stash.Setting('htmlReportPostfix')}")
+                                    fileHtmlReport.close()
+                                    PaginateId+=1
+                                    fileHtmlReport = open(nextHtmReport, "w")
+                                    fileHtmlReport.write(f"{getHtmlReportTableRow(qtyResults, tagDuplicates)}\n")
+                                    if PaginateId > 1:
+                                        prevHtmReport = htmlReportNameHomePage.replace(".html", f"_{PaginateId-1}.html")
+                                    else:
+                                        prevHtmReport = htmlReportNameHomePage
+                                    prevHtmReportLink = f"<a class=\"link-items\" title=\"Previous Page\" href=\"file://{prevHtmReport}\">[Prev]</a>"
+                                    if len(DupFileSets) > (QtyTagForDel + htmlReportPaginate):
+                                        nextHtmReport = htmlReportNameHomePage.replace(".html", f"_{PaginateId+1}.html")
+                                        nextHtmReportLink = f"<a class=\"link-items\" title=\"Next Page\" href=\"file://{nextHtmReport}\">[Next]</a>"
+                                        fileHtmlReport.write(f"<center><table><tr><td>{homeHtmReportLink}</td><td>{prevHtmReportLink}</td><td>{nextHtmReportLink}</td></tr></table></center>")
+                                    else:
+                                        stash.Debug(f"DupFileSets Qty = {len(DupFileSets)}; DupFileDetailList Qty = {len(DupFileDetailList)}; QtyTagForDel = {QtyTagForDel}; htmlReportPaginate = {htmlReportPaginate}; QtyTagForDel + htmlReportPaginate = {QtyTagForDel+htmlReportPaginate}")
+                                        fileHtmlReport.write(f"<center><table><tr><td>{homeHtmReportLink}</td><td>{prevHtmReportLink}</td></tr></table></center>")
+                                    fileHtmlReport.write(f"{stash.Setting('htmlReportTable')}\n")
+                                    fileHtmlReport.write(f"{htmlReportTableRow}{htmlReportTableHeader}Scene</th>{htmlReportTableHeader}Duplicate to Delete</th>{htmlReportTableHeader}Scene-ToKeep</th>{htmlReportTableHeader}Duplicate to Keep</th></tr>\n")
+                            
+                            if tagDuplicates and graylistTagging and stash.startsWithInList(graylist, DupFile['files'][0]['path']):
+                                stash.addTag(DupFile, graylistMarkForDeletion, ignoreAutoTag=True)
+                            if didAddTag:
+                                QtyNewlyTag+=1
+                            if QtyTagForDel == 1:
+                                stash.Log(f"Tagging duplicate {DupFile['files'][0]['path']} for deletion with tag {duplicateMarkForDeletion}", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
+                            else:
+                                didAddTag = 1 if didAddTag else 0
+                                stash.Log(f"Tagging duplicate {DupFile['files'][0]['path']} for deletion;AddTag={didAddTag};Qty={QtyDup};Set={QtyDupSet} of {qtyResults};NewlyTag={QtyNewlyTag};isTag={QtyTagForDel}", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
         stash.Trace(SepLine)
-        if maxDupToProcess > 0 and QtyDup > maxDupToProcess:
+        if maxDupToProcess > 0 and ((QtyTagForDel > maxDupToProcess) or (QtyTagForDel == 0 and QtyDup > maxDupToProcess)):
             break
     
-    stash.Log(f"QtyDupSet={QtyDupSet}, QtyDup={QtyDup}, QtyDeleted={QtyDeleted}, QtySwap={QtySwap}, QtyTagForDel={QtyTagForDel}, QtySkipForDel={QtySkipForDel}, QtyExactDup={QtyExactDup}, QtyAlmostDup={QtyAlmostDup}, QtyMerge={QtyMerge}, QtyRealTimeDiff={QtyRealTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
-    if cleanAfterDel:
+    if fileHtmlReport != None:
+        fileHtmlReport.write("</table>\n")
+        if PaginateId > 0:
+            homeHtmReportLink = f"<a class=\"link-items\" title=\"Home Page\" href=\"file://{htmlReportNameHomePage}\">[Home]</a>"
+            if PaginateId > 1:
+                prevHtmReport = htmlReportNameHomePage.replace(".html", f"_{PaginateId-1}.html")
+            else:
+                prevHtmReport = htmlReportNameHomePage
+            prevHtmReportLink = f"<a class=\"link-items\" title=\"Previous Page\" href=\"file://{prevHtmReport}\">[Prev]</a>"
+            fileHtmlReport.write(f"<center><table><tr><td>{homeHtmReportLink}</td><td>{prevHtmReportLink}</td></tr></table></center>")
+        fileHtmlReport.write(f"<h2>Total Tagged for Deletion {QtyTagForDel}</h2>\n")
+        fileHtmlReport.write(f"{stash.Setting('htmlReportPostfix')}")
+        fileHtmlReport.close()
+        stash.Log(f"************************************************************", printTo = stash.LogTo.STASH)
+        stash.Log(f"************************************************************", printTo = stash.LogTo.STASH)
+        stash.Log(f"View Stash duplicate report using Stash->Settings->Tools->[Duplicate File Report]", printTo = stash.LogTo.STASH)
+        stash.Log(f"************************************************************", printTo = stash.LogTo.STASH)
+        stash.Log(f"************************************************************", printTo = stash.LogTo.STASH)
+
+        
+    stash.Debug("#####################################################")
+    stash.Log(f"QtyDupSet={QtyDupSet}, QtyDup={QtyDup}, QtyDeleted={QtyDeleted}, QtySwap={QtySwap}, QtyTagForDel={QtyTagForDel}, QtySkipForDel={QtySkipForDel}, QtyExcludeForDel={QtyExcludeForDel}, QtyExactDup={QtyExactDup}, QtyAlmostDup={QtyAlmostDup}, QtyMerge={QtyMerge}, QtyRealTimeDiff={QtyRealTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
+    killScanningJobs()
+    if cleanAfterDel and deleteDup:
         stash.Log("Adding clean jobs to the Task Queue", printTo=LOG_STASH_N_PLUGIN)
-        stash.metadata_clean(paths=stash.STASH_PATHS)
+        stash.metadata_clean()
         stash.metadata_clean_generated()
         stash.optimise_database()
+    if doGeneratePhash:
+        stash.metadata_generate({"phashes": True})
+    sys.stdout.write("Report complete")
 
-def deleteTagggedDuplicates():
-    tagId = stash.find_tags(q=duplicateMarkForDeletion)
-    if len(tagId) > 0 and 'id' in tagId[0]:
-        tagId = tagId[0]['id']
-    else:
+def findCurrentTagId(tagNames):
+    # tagNames = [i for n, i in enumerate(tagNames) if i not in tagNames[:n]]
+    for tagName in tagNames:
+        tagId = stash.find_tags(q=tagName)
+        if len(tagId) > 0 and 'id' in tagId[0]:
+            stash.Debug(f"Using tag name {tagName} with Tag ID {tagId[0]['id']}")
+            return tagId[0]['id']
+    return "-1"
+
+def toJson(data):
+    import json
+    # data = data.replace("'", '"')
+    data = data.replace("\\", "\\\\")
+    data = data.replace("\\\\\\\\", "\\\\")
+    return json.loads(data)
+
+def getAnAdvanceMenuOptionSelected(taskName, target, isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater):
+    stash.Log(f"Processing taskName = {taskName}, target = {target}")
+    if "Blacklist" in taskName:
+        isBlackList = True
+    if "Less" in taskName:
+        compareToLess = True
+    if "Greater" in taskName:
+        compareToGreater = True
+    
+    if "pathToDelete" in taskName:
+        pathToDelete = target.lower()
+    elif "sizeToDelete" in taskName:
+        sizeToDelete = int(target)
+    elif "durationToDelete" in taskName:
+        durationToDelete = int(target)
+    elif "commonResToDelete" in taskName:
+        resolutionToDelete = int(target)
+    elif "resolutionToDelete" in taskName:
+        resolutionToDelete = int(target)
+    elif "ratingToDelete" in taskName:
+        ratingToDelete = int(target) * 20
+    elif "tagToDelete" in taskName:
+        tagToDelete = target.lower()
+    elif "titleToDelete" in taskName:
+        titleToDelete = target.lower()
+    elif "pathStrToDelete" in taskName:
+        pathStrToDelete = target.lower()
+    elif "fileNotExistToDelete" in taskName:
+        fileNotExistToDelete = True
+    return isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
+
+def getAdvanceMenuOptionSelected(advanceMenuOptionSelected):
+    isBlackList = False
+    pathToDelete = ""
+    sizeToDelete = -1
+    durationToDelete = -1
+    resolutionToDelete = -1
+    ratingToDelete = -1
+    tagToDelete = ""
+    titleToDelete = ""
+    pathStrToDelete = ""
+    fileNotExistToDelete = False
+    compareToLess = False
+    compareToGreater = False
+    if advanceMenuOptionSelected:
+        stash.enableProgressBar(False)
+        if 'Target' in stash.JSON_INPUT['args']:
+            if "applyCombo" in stash.PLUGIN_TASK_NAME:
+                jsonObject = toJson(stash.JSON_INPUT['args']['Target'])
+                for taskName in jsonObject:
+                    isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAnAdvanceMenuOptionSelected(taskName, jsonObject[taskName], isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, compareToLess, compareToGreater)
+            else:
+                return getAnAdvanceMenuOptionSelected(stash.PLUGIN_TASK_NAME, stash.JSON_INPUT['args']['Target'], isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, compareToLess, compareToGreater)
+    return isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
+
+# //////////////////////////////////////////////////////////////////////////////
+# //////////////////////////////////////////////////////////////////////////////
+def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=False, tagId=-1, advanceMenuOptionSelected=False):
+    if tagId == -1:
+        tagId = findCurrentTagId([duplicateMarkForDeletion, base1_duplicateMarkForDeletion, base2_duplicateMarkForDeletion, 'DuplicateMarkForDeletion', '_DuplicateMarkForDeletion'])
+    if int(tagId) < 0:
         stash.Warn(f"Could not find tag ID for tag '{duplicateMarkForDeletion}'.")
         return
+
+    excludedTags = [duplicateMarkForDeletion]
+    if clearAllDupfileManagerTags:
+        excludedTags = [duplicateMarkForDeletion, duplicateWhitelistTag, excludeDupFileDeleteTag, graylistMarkForDeletion, longerDurationLowerResolution]
+    
+    isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAdvanceMenuOptionSelected(advanceMenuOptionSelected)
+    if advanceMenuOptionSelected and deleteScenes and pathToDelete == "" and tagToDelete == "" and titleToDelete == "" and pathStrToDelete == "" and sizeToDelete == -1 and durationToDelete == -1 and resolutionToDelete == -1 and ratingToDelete == -1 and fileNotExistToDelete == False:
+        stash.Error("Running advance menu option with no options enabled.")
+        return
+    
     QtyDup = 0
     QtyDeleted = 0
+    QtyClearedTags = 0
+    QtySetGraylistTag = 0
     QtyFailedQuery = 0
-    stash.Trace("#########################################################################")
-    sceneIDs = stash.find_scenes(f={"tags": {"value":tagId, "modifier":"INCLUDES"}}, fragment='id')
-    qtyResults = len(sceneIDs)
-    stash.Trace(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion}): sceneIDs = {sceneIDs}")
-    for sceneID in sceneIDs:
-        # stash.Trace(f"Getting scene data for scene ID {sceneID['id']}.")
+    stash.Debug("#########################################################################")
+    stash.startSpinningProcessBar()
+    scenes = stash.find_scenes(f={"tags": {"value":tagId, "modifier":"INCLUDES"}}, fragment='id tags {id name} files {path width height duration size video_codec bit_rate frame_rate} details title rating100')
+    stash.stopSpinningProcessBar()
+    qtyResults = len(scenes)
+    stash.Log(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion})")
+    stash.setProgressBarIter(qtyResults)
+    for scene in scenes:
         QtyDup += 1
-        prgs = QtyDup / qtyResults
-        stash.Progress(QtyDup, qtyResults)
-        scene = stash.find_scene(sceneID['id'])
-        if scene == None or len(scene) == 0:
-            stash.Warn(f"Could not get scene data for scene ID {sceneID['id']}.")
-            QtyFailedQuery += 1
-            continue
-        # stash.Log(f"scene={scene}")
-        DupFileName = scene['files'][0]['path']
-        DupFileNameOnly = pathlib.Path(DupFileName).stem
-        stash.Warn(f"Deleting duplicate '{DupFileName}'", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
-        if alternateTrashCanPath != "":
-            destPath = f"{alternateTrashCanPath }{os.sep}{DupFileNameOnly}"
-            if os.path.isfile(destPath):
-                destPath = f"{alternateTrashCanPath }{os.sep}_{time.time()}_{DupFileNameOnly}"
-            shutil.move(DupFileName, destPath)
-        elif moveToTrashCan:
-            sendToTrash(DupFileName)
-        result = stash.destroy_scene(scene['id'], delete_file=True)
-        stash.Trace(f"destroy_scene result={result} for file {DupFileName}", toAscii=True)
-        QtyDeleted += 1
-    stash.Log(f"QtyDup={QtyDup}, QtyDeleted={QtyDeleted}, QtyFailedQuery={QtyFailedQuery}", printTo=LOG_STASH_N_PLUGIN)
-    return
+        stash.progressBar(QtyDup, qtyResults)
+        # scene = stash.find_scene(sceneID['id'])
+        # if scene == None or len(scene) == 0:
+            # stash.Warn(f"Could not get scene data for scene ID {scene['id']}.")
+            # QtyFailedQuery += 1
+            # continue
+        # stash.Trace(f"scene={scene}")
+        if clearTag:
+            QtyClearedTags += 1 
+            # ToDo: Add logic to exclude graylistMarkForDeletion
+            tags = [int(item['id']) for item in scene["tags"] if item['name'] not in excludedTags]
+            # if clearAllDupfileManagerTags:
+                # tags = []
+                # for tag in scene["tags"]:
+                    # if tag['name'] in excludedTags:
+                        # continue
+                    # tags += [int(tag['id'])]
+            stash.TraceOnce(f"tagId={tagId}, len={len(tags)}, tags = {tags}")
+            dataDict = {'id' : scene['id']}
+            if addPrimaryDupPathToDetails:
+                sceneDetails = scene['details']
+                if sceneDetails.find(detailPrefix) == 0 and sceneDetails.find(detailPostfix) > 1:
+                    Pos1 = sceneDetails.find(detailPrefix)
+                    Pos2 = sceneDetails.find(detailPostfix)
+                    sceneDetails = sceneDetails[0:Pos1] + sceneDetails[Pos2 + len(detailPostfix):]                
+                dataDict.update({'details' : sceneDetails})
+            dataDict.update({'tag_ids' : tags})
+            stash.Log(f"Updating scene with {dataDict};QtyClearedTags={QtyClearedTags};Count={QtyDup} of {qtyResults}")
+            stash.updateScene(dataDict)
+            # stash.removeTag(scene, duplicateMarkForDeletion)
+        elif setGrayListTag:
+            if stash.startsWithInList(graylist, scene['files'][0]['path']):
+                QtySetGraylistTag+=1
+                if stash.addTag(scene, graylistMarkForDeletion, ignoreAutoTag=True):
+                    stash.Log(f"Added tag {graylistMarkForDeletion} to scene {scene['files'][0]['path']};QtySetGraylistTag={QtySetGraylistTag};Count={QtyDup} of {qtyResults}")
+                else:
+                    stash.Trace(f"Scene already had tag {graylistMarkForDeletion}; {scene['files'][0]['path']}")
+        elif deleteScenes:
+            DupFileName = scene['files'][0]['path']
+            DupFileNameOnly = pathlib.Path(DupFileName).stem
+            if advanceMenuOptionSelected:
+                if isBlackList:
+                    if not stash.startsWithInList(blacklist, scene['files'][0]['path']):
+                        continue
+                if pathToDelete != "":
+                    if not DupFileName.lower().startswith(pathToDelete):
+                        stash.Debug(f"Skipping file {DupFileName} because it does not start with {pathToDelete}.")
+                        continue
+                if pathStrToDelete != "":
+                    if not pathStrToDelete in DupFileName.lower():
+                        stash.Debug(f"Skipping file {DupFileName} because it does not contain value {pathStrToDelete}.")
+                        continue
+                if sizeToDelete != -1:
+                    compareTo = int(scene['files'][0]['size'])
+                    if compareToLess:
+                        if not (compareTo < sizeToDelete):
+                            continue
+                    elif compareToGreater:
+                        if not (compareTo > sizeToDelete):
+                            continue
+                    else:
+                        if not compareTo == sizeToDelete:
+                            continue
+                if durationToDelete != -1:
+                    compareTo = int(scene['files'][0]['duration'])
+                    if compareToLess:
+                        if not (compareTo < durationToDelete):
+                            continue
+                    elif compareToGreater:
+                        if not (compareTo > durationToDelete):
+                            continue
+                    else:
+                        if not compareTo == durationToDelete:
+                            continue
+                if resolutionToDelete != -1:
+                    compareTo = int(scene['files'][0]['width']) * int(scene['files'][0]['height'])
+                    if compareToLess:
+                        if not (compareTo < resolutionToDelete):
+                            continue
+                    elif compareToGreater:
+                        if not (compareTo > resolutionToDelete):
+                            continue
+                    else:
+                        if not compareTo == resolutionToDelete:
+                            continue
+                if ratingToDelete != -1:
+                    if scene['rating100'] == "None":
+                        compareTo = 0
+                    else:
+                        compareTo = int(scene['rating100'])
+                    if compareToLess:
+                        if not (compareTo < resolutionToDelete):
+                            continue
+                    elif compareToGreater:
+                        if not (compareTo > resolutionToDelete):
+                            continue
+                    else:
+                        if not compareTo == resolutionToDelete:
+                            continue
+                if titleToDelete != "":
+                    if not titleToDelete in scene['title'].lower():
+                        stash.Debug(f"Skipping file {DupFileName} because it does not contain value {titleToDelete} in title ({scene['title']}).")
+                        continue
+                if tagToDelete != "":
+                    doProcessThis = False
+                    for tag in scene['tags']:
+                        if tag['name'].lower() == tagToDelete:
+                            doProcessThis = True
+                            break
+                    if doProcessThis == False:
+                        continue
+                if fileNotExistToDelete:
+                    if os.path.isfile(scene['files'][0]['path']):
+                        continue
+            stash.Warn(f"Deleting duplicate '{DupFileName}'", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
+            if alternateTrashCanPath != "":
+                destPath = f"{alternateTrashCanPath }{os.sep}{DupFileNameOnly}"
+                if os.path.isfile(destPath):
+                    destPath = f"{alternateTrashCanPath }{os.sep}_{time.time()}_{DupFileNameOnly}"
+                shutil.move(DupFileName, destPath)
+            elif moveToTrashCan:
+                sendToTrash(DupFileName)
+            result = stash.destroyScene(scene['id'], delete_file=True)
+            QtyDeleted += 1
+            stash.Debug(f"destroyScene result={result} for file {DupFileName};QtyDeleted={QtyDeleted};Count={QtyDup} of {qtyResults}", toAscii=True)
+        else:
+            stash.Error("manageTagggedDuplicates called with invlaid input arguments. Doing early exit.")
+            return
+    stash.Debug("#####################################################")
+    stash.Log(f"QtyDup={QtyDup}, QtyClearedTags={QtyClearedTags}, QtySetGraylistTag={QtySetGraylistTag}, QtyDeleted={QtyDeleted}, QtyFailedQuery={QtyFailedQuery}", printTo=LOG_STASH_N_PLUGIN)
+    killScanningJobs()
+    if deleteScenes and not advanceMenuOptionSelected:
+        if cleanAfterDel:
+            stash.Log("Adding clean jobs to the Task Queue", printTo=LOG_STASH_N_PLUGIN)
+            stash.metadata_clean()
+            stash.metadata_clean_generated()
+            stash.optimise_database()
 
-def testSetDupTagOnScene(sceneId):
-    scene = stash.find_scene(sceneId)
-    stash.Log(f"scene={scene}")
-    stash.Log(f"scene tags={scene['tags']}")
-    tag_ids = [dupTagId]
-    for tag in scene['tags']:
-        tag_ids = tag_ids + [tag['id']]
-    stash.Log(f"tag_ids={tag_ids}")
-    stash.update_scene({'id' : scene['id'], 'tag_ids' : tag_ids})
-
-if stash.PLUGIN_TASK_NAME == "tag_duplicates_task":
-    mangeDupFiles(tagDuplicates=True, merge=mergeDupFilename)
-    stash.Trace(f"{stash.PLUGIN_TASK_NAME} EXIT")
-elif stash.PLUGIN_TASK_NAME == "delete_tagged_duplicates_task":
-    deleteTagggedDuplicates()
-    stash.Trace(f"{stash.PLUGIN_TASK_NAME} EXIT")
-elif stash.PLUGIN_TASK_NAME == "delete_duplicates_task":
-    mangeDupFiles(deleteDup=True, merge=mergeDupFilename)
-    stash.Trace(f"{stash.PLUGIN_TASK_NAME} EXIT")
-elif parse_args.dup_tag:
-    mangeDupFiles(tagDuplicates=True, merge=mergeDupFilename)
-    stash.Trace(f"Tag duplicate EXIT")
-elif parse_args.del_tag:
-    deleteTagggedDuplicates()
-    stash.Trace(f"Delete Tagged duplicates EXIT")
-elif parse_args.remove:
-    mangeDupFiles(deleteDup=True, merge=mergeDupFilename)
-    stash.Trace(f"Delete duplicate EXIT")
-else:
-    stash.Log(f"Nothing to do!!! (PLUGIN_ARGS_MODE={stash.PLUGIN_TASK_NAME})")
 
+def removeDupTag():
+    if 'Target' not in stash.JSON_INPUT['args']:
+        stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
+        return
+    scene = stash.JSON_INPUT['args']['Target']
+    stash.Log(f"Processing scene ID# {scene}")
+    stash.removeTag(scene, duplicateMarkForDeletion)
+    stash.Log(f"Done removing tag from scene {scene}.")
+    jsonReturn = "{'removeDupTag' : 'complete', 'id': '" + f"{scene}" + "'}"
+    stash.Log(f"Sending json value {jsonReturn}")
+    sys.stdout.write(jsonReturn)
+
+def addExcludeTag():
+    if 'Target' not in stash.JSON_INPUT['args']:
+        stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
+        return
+    scene = stash.JSON_INPUT['args']['Target']
+    stash.Log(f"Processing scene ID# {scene}")
+    stash.addTag(scene, excludeDupFileDeleteTag)
+    stash.Log(f"Done adding exclude tag to scene {scene}.")
+    sys.stdout.write("{" + f"addExcludeTag : 'complete', id: '{scene}'" + "}")
+
+def removeExcludeTag():
+    if 'Target' not in stash.JSON_INPUT['args']:
+        stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
+        return
+    scene = stash.JSON_INPUT['args']['Target']
+    stash.Log(f"Processing scene ID# {scene}")
+    stash.removeTag(scene, excludeDupFileDeleteTag)
+    stash.Log(f"Done removing exclude tag from scene {scene}.")
+    sys.stdout.write("{" + f"removeExcludeTag : 'complete', id: '{scene}'" + "}")
+
+def getParseData(getSceneDetails1=True, getSceneDetails2=True):
+    if 'Target' not in stash.JSON_INPUT['args']:
+        stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
+        return None, None
+    targetsSrc = stash.JSON_INPUT['args']['Target']
+    targets = targetsSrc.split(":")
+    if len(targets) < 2:
+        stash.Error(f"Could not get both targets from string {targetsSrc}")
+        return None, None
+    stash.Log(f"Parsed targets {targets[0]} and {targets[1]}")
+    target1 = targets[0]
+    target2 = targets[1]
+    if getSceneDetails1:
+        target1 = stash.find_scene(int(target1))
+    if getSceneDetails2:
+        target2 = stash.find_scene(int(target2))
+    elif len(targets) > 2:
+        target2 = target2 + targets[2]
+    return target1, target2
+    
+
+def mergeTags():
+    scene1, scene2 = getParseData()
+    if scene1 == None or scene2 == None:
+        sys.stdout.write("{" + f"mergeTags : 'failed', id1: '{scene1}', id2: '{scene2}'" + "}")
+        return
+    stash.mergeMetadata(scene1, scene2)
+    stash.Log(f"Done merging scenes for scene {scene1['id']} and scene {scene2['id']}")
+    sys.stdout.write("{" + f"mergeTags : 'complete', id1: '{scene1['id']}', id2: '{scene2['id']}'" + "}")
+
+def getLocalDupReportPath():
+    htmlReportExist = "true" if os.path.isfile(htmlReportName) else "false"
+    localPath = htmlReportName.replace("\\", "\\\\")
+    jsonReturn = "{'LocalDupReportExist' : " + f"{htmlReportExist}" + ", 'Path': '" + f"{localPath}" + "'}"
+    stash.Log(f"Sending json value {jsonReturn}")
+    sys.stdout.write(jsonReturn)
+
+def deleteLocalDupReportHtmlFiles(doJsonOutput = True):
+    htmlReportExist = "true" if os.path.isfile(htmlReportName) else "false"
+    if os.path.isfile(htmlReportName):
+        stash.Log(f"Deleting file {htmlReportName}")
+        os.remove(htmlReportName)
+        for x in range(2, 9999):
+            fileName = htmlReportName.replace(".html", f"_{x-1}.html")
+            stash.Debug(f"Checking if file '{fileName}' exist.")
+            if not os.path.isfile(fileName):
+                break
+            stash.Log(f"Deleting file {fileName}")
+            os.remove(fileName)
+    else:
+        stash.Log(f"Report file does not exist: {htmlReportName}")
+    if doJsonOutput:
+        jsonReturn = "{'LocalDupReportExist' : " + f"{htmlReportExist}" + ", 'Path': '" + f"{htmlReportName}" + "', 'qty': '" + f"{x}" + "'}"
+        stash.Log(f"Sending json value {jsonReturn}")
+        sys.stdout.write(jsonReturn)
+
+def removeTagFromAllScenes(tagName, deleteTags):
+    # ToDo: Replace code with SQL code if DB version 68
+    tagId = stash.find_tags(q=tagName)
+    if len(tagId) > 0 and 'id' in tagId[0]:
+        if deleteTags:
+            stash.Debug(f"Deleting tag name {tagName} with Tag ID {tagId[0]['id']} from stash.")
+            stash.destroy_tag(int(tagId[0]['id']))
+        else:
+            stash.Debug(f"Removing tag name {tagName} with Tag ID {tagId[0]['id']} from all scenes.")
+            manageTagggedDuplicates(clearTag=True, tagId=int(tagId[0]['id']))
+        return True
+    return False
+
+def removeAllDupTagsFromAllScenes(deleteTags=False):
+    tagsToClear = [duplicateMarkForDeletion, base1_duplicateMarkForDeletion, base2_duplicateMarkForDeletion, graylistMarkForDeletion, longerDurationLowerResolution, duplicateWhitelistTag]
+    for x in range(0, 3):
+        tagsToClear += [base1_duplicateMarkForDeletion + f"_{x}"] 
+    for x in range(0, 3):
+        tagsToClear += [base2_duplicateMarkForDeletion + f"_{x}"] 
+    tagsToClear = list(set(tagsToClear)) # Remove duplicates
+    validTags = []
+    for tagToClear in tagsToClear:
+        if removeTagFromAllScenes(tagToClear, deleteTags):
+            validTags +=[tagToClear]
+    if doJsonReturn:
+        jsonReturn = "{'removeAllDupTagFromAllScenes' : " + f"{duplicateMarkForDeletion}" + ", 'OtherTags': '" + f"{validTags}" + "'}"
+        stash.Log(f"Sending json value {jsonReturn}")
+        sys.stdout.write(jsonReturn)
+    else:
+        stash.Log(f"Clear tags {tagsToClear}")
+
+def updateScenesInReport(fileName, scene):
+    stash.Log(f"Updating table rows with scene {scene} in file {fileName}")
+    scene1 = -1
+    scene2 = -1
+    strToFind = "class=\"ID_"
+    lines = None
+    with open(fileName, 'r') as file:
+        lines = file.readlines()
+    stash.Log(f"line count = {len(lines)}")
+    with open(fileName, 'w') as file:
+        for line in lines:
+            # stash.Debug(f"line = {line}")
+            if f"class=\"ID_{scene}\"" in line:
+                idx = 0
+                while line.find(strToFind, idx) > -1:
+                    idx = line.find(strToFind, idx) + len(strToFind)
+                    id = line[idx:]
+                    stash.Debug(f"id = {id}, idx = {idx}")
+                    id = id[:id.find('"')]
+                    stash.Debug(f"id = {id}")
+                    if scene1 == -1:
+                        scene1 = int(id)
+                    elif scene1 != int(id) and scene2 == -1:
+                        scene2 = int(id)
+                    elif scene1 != -1 and scene2 != -1:
+                        break
+                if scene1 != -1 and scene2 != -1:
+                    sceneDetail1 = stash.find_scene(scene1)
+                    sceneDetail2 = stash.find_scene(scene2)
+                    if sceneDetail1 == None or sceneDetail2 == None:
+                        stash.Error("Could not get scene details for both scene1 ({scene1}) and scene2  ({scene2}); sceneDetail1={sceneDetail1}; sceneDetail2={sceneDetail2};")
+                    else:
+                        writeRowToHtmlReport(file, sceneDetail1, sceneDetail2)
+                else:
+                    stash.Error(f"Could not get both scene ID associated with scene {scene}; scene1 = {scene1}; scene2 = {scene2}")
+                    file.write(line)
+            else:
+                file.write(line)
 
+def updateScenesInReports(scene, ReportName = htmlReportName):
+    if os.path.isfile(ReportName):
+        updateScenesInReport(ReportName, scene)
+        for x in range(2, 9999):
+            fileName = ReportName.replace(".html", f"_{x-1}.html")
+            stash.Debug(f"Checking if file '{fileName}' exist.")
+            if not os.path.isfile(fileName):
+                break
+            updateScenesInReport(fileName, scene)
+    else:
+        stash.Log(f"Report file does not exist: {ReportName}")
 
+def addPropertyToSceneClass(fileName, scene, property):
+    stash.Log(f"Inserting property {property} for scene {scene} in file {fileName}")
+    doStyleEndTagCheck = True
+    lines = None
+    with open(fileName, 'r') as file:
+        lines = file.readlines()
+    stash.Log(f"line count = {len(lines)}")
+    with open(fileName, 'w') as file:
+        for line in lines:
+            # stash.Debug(f"line = {line}")
+            if doStyleEndTagCheck:
+                if property == "" and line.startswith(f".ID_{scene}" + "{"):
+                    continue
+                if line.startswith("</style>"):
+                    if property != "":
+                        styleSetting = f".ID_{scene}{property}\n"
+                        stash.Log(f"styleSetting = {styleSetting}")
+                        file.write(styleSetting)
+                    doStyleEndTagCheck = False
+            file.write(line)
 
+def addPropertyToSceneClassToAllFiles(scene, property, ReportName = htmlReportName):
+    if os.path.isfile(ReportName):
+        addPropertyToSceneClass(ReportName, scene, property)
+        for x in range(2, 9999):
+            fileName = ReportName.replace(".html", f"_{x-1}.html")
+            stash.Debug(f"Checking if file '{fileName}' exist.")
+            if not os.path.isfile(fileName):
+                break
+            addPropertyToSceneClass(fileName, scene, property)
+    else:
+        stash.Log(f"Report file does not exist: {ReportName}")
+
+def deleteScene(disableInReport=True, deleteFile=True):
+    if 'Target' not in stash.JSON_INPUT['args']:
+        stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
+        return
+    scene = stash.JSON_INPUT['args']['Target']
+    stash.Log(f"Processing scene ID# {scene}")
+    result = None
+    result = stash.destroyScene(scene, delete_file=deleteFile)
+    if disableInReport:
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:gray;pointer-events:none;}")
+    stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene} with results = {result}")
+    sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id: '{scene}', result: '{result}'" + "}")
+
+def copyScene(moveScene=False):
+    scene1, scene2 = getParseData()
+    if scene1 == None or scene2 == None:
+        sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'failed', id1: '{scene1}', id2: '{scene2}'" + "}")
+        return
+    if moveScene:
+        stash.mergeMetadata(scene1, scene2)
+    result = shutil.copy(scene1['files'][0]['path'], scene2['files'][0]['path'])
+    if moveScene:
+        result = stash.destroyScene(scene1['id'], delete_file=True)
+        stash.Log(f"destroyScene for scene {scene1['id']} results = {result}")
+    stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene1['id']} and {scene2['id']}")
+    sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id1: '{scene1['id']}', id2: '{scene2['id']}', result: '{result}'" + "}")
+
+def renameFile():
+    scene, newName = getParseData(getSceneDetails2=False)
+    if scene == None or newName == None:
+        sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'failed', scene: '{scene}', newName: '{newName}'" + "}")
+        return
+    newName = newName.strip("'")
+    ext = pathlib.Path(scene['files'][0]['path']).suffix
+    newNameFull = f"{pathlib.Path(scene['files'][0]['path']).resolve().parent}{os.sep}{newName}{ext}"
+    newNameFull = newNameFull.strip("'")
+    newNameFull = newNameFull.replace("\\\\", "\\")
+    oldNameFull = scene['files'][0]['path']
+    oldNameFull = oldNameFull.strip("'")
+    oldNameFull = oldNameFull.replace("\\\\", "\\")
+    stash.Log(f"renaming file '{stash.asc2(oldNameFull)}' to '{stash.asc2(newNameFull)}'")
+    result = os.rename(oldNameFull, newNameFull)
+    stash.renameFileNameInDB(scene['files'][0]['id'], pathlib.Path(oldNameFull).stem, f"{newName}{ext}", UpdateUsingIdOnly = True)
+    updateScenesInReports(scene['id'])
+    stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene['id']} ;renamed to {newName}; result={result}")
+    sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', scene: '{scene['id']}', newName: '{newName}', result: '{result}'" + "}")
+    
+def flagScene():
+    scene, flagType = getParseData(False, False)
+    if scene == None or flagType == None:
+        sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'failed', scene: '{scene}', flagType: '{flagType}'" + "}")
+        return
+    if flagType == "disable-scene":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:gray;pointer-events:none;}")
+    elif flagType == "strike-through":
+        addPropertyToSceneClassToAllFiles(scene, "{text-decoration: line-through;}")
+    elif flagType == "yellow highlight":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:yellow;}")
+    elif flagType == "green highlight":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:#00FF00;}")
+    elif flagType == "orange highlight":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:orange;}")
+    elif flagType == "cyan highlight":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:cyan;}")
+    elif flagType == "pink highlight":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:pink;}")
+    elif flagType == "red highlight":
+        addPropertyToSceneClassToAllFiles(scene, "{background-color:red;}")
+    elif flagType == "remove all flags":
+        addPropertyToSceneClassToAllFiles(scene, "")
+    else:
+        stash.Log(f"Invalid flagType ({flagType})")
+        sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'failed', scene: '{scene}', flagType: '{flagType}'" + "}")
+        return
+    sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', scene: '{scene}', flagType: '{flagType}'" + "}")
+
+# ToDo: Add to UI menu
+#    Remove all Dup tagged files (Just remove from stash, and leave file)
+#    Clear GraylistMarkForDel tag
+#    Delete GraylistMarkForDel tag
+#    Remove from stash all files no longer part of stash library 
+#    Remove from stash all files in the Exclusion list (Not supporting regexps)
+# ToDo: Add to advance menu
+#    Remove only graylist dup
+#    Exclude graylist from delete
+#    Include graylist in delete
+
+try:
+    if stash.PLUGIN_TASK_NAME == "tag_duplicates_task":
+        mangeDupFiles(tagDuplicates=True, merge=mergeDupFilename)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "create_duplicate_report_task":
+        mangeDupFiles(tagDuplicates=False, merge=mergeDupFilename)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "delete_tagged_duplicates_task":
+        manageTagggedDuplicates(deleteScenes=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "delete_duplicates_task":
+        mangeDupFiles(deleteDup=True, merge=mergeDupFilename)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "clear_duplicate_tags_task":
+        removeAllDupTagsFromAllScenes()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "graylist_tag_task":
+        manageTagggedDuplicates(setGrayListTag=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "generate_phash_task":
+        stash.metadata_generate({"phashes": True})
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "deleteScene":
+        deleteScene()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "removeScene":
+        deleteScene(deleteFile=False)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "renameFile":
+        renameFile()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "flagScene":
+        flagScene()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "copyScene":
+        copyScene()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "moveScene":
+        copyScene(moveScene=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "removeDupTag":
+        removeDupTag()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "addExcludeTag":
+        addExcludeTag()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "removeExcludeTag":
+        removeExcludeTag()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "mergeTags":
+        mergeTags()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "getLocalDupReportPath":
+        getLocalDupReportPath()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "deleteLocalDupReportHtmlFiles":
+        deleteLocalDupReportHtmlFiles()
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "createDuplicateReportWithoutTagging":
+        mangeDupFiles(tagDuplicates=False, merge=mergeDupFilename)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "deleteAllDupFileManagerTags":
+        removeAllDupTagsFromAllScenes(deleteTags=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "deleteBlackListTaggedDuplicatesTask":
+        mangeDupFiles(deleteDup=True, merge=mergeDupFilename, deleteBlacklistOnly=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "deleteTaggedDuplicatesLwrResOrLwrDuration":
+        mangeDupFiles(deleteDup=True, merge=mergeDupFilename, deleteLowerResAndDuration=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif stash.PLUGIN_TASK_NAME == "deleteBlackListTaggedDuplicatesLwrResOrLwrDuration":
+        mangeDupFiles(deleteDup=True, merge=mergeDupFilename, deleteBlacklistOnly=True, deleteLowerResAndDuration=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    elif parse_args.dup_tag:
+        stash.PLUGIN_TASK_NAME = "dup_tag"
+        mangeDupFiles(tagDuplicates=True, merge=mergeDupFilename)
+        stash.Debug(f"Tag duplicate EXIT")
+    elif parse_args.del_tag:
+        stash.PLUGIN_TASK_NAME = "del_tag"
+        manageTagggedDuplicates(deleteScenes=True)
+        stash.Debug(f"Delete Tagged duplicates EXIT")
+    elif parse_args.clear_tag:
+        stash.PLUGIN_TASK_NAME = "clear_tag"
+        removeAllDupTagsFromAllScenes()
+        stash.Debug(f"Clear duplicate tags EXIT")
+    elif parse_args.remove:
+        stash.PLUGIN_TASK_NAME = "remove"
+        mangeDupFiles(deleteDup=True, merge=mergeDupFilename)
+        stash.Debug(f"Delete duplicate EXIT")
+    elif len(sys.argv) < 2 and stash.PLUGIN_TASK_NAME in advanceMenuOptions:
+        manageTagggedDuplicates(deleteScenes=True, advanceMenuOptionSelected=True)
+        stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
+    else:
+        stash.Log(f"Nothing to do!!! (PLUGIN_ARGS_MODE={stash.PLUGIN_TASK_NAME})")
+except Exception as e:
+    tb = traceback.format_exc()
+    
+    stash.Error(f"Exception while running DupFileManager Task({stash.PLUGIN_TASK_NAME}); Error: {e}\nTraceBack={tb}")
+    killScanningJobs()
+    stash.convertToAscii = False
+    stash.Error(f"Error: {e}\nTraceBack={tb}")
+    if doJsonReturn:
+        sys.stdout.write("{" + f"Exception : '{e}; See log file for TraceBack' " + "}")
 
-stash.Trace("\n*********************************\nEXITING   ***********************\n*********************************")
+stash.Log("\n*********************************\nEXITING   ***********************\n*********************************")
diff --git a/plugins/DupFileManager/DupFileManager.yml b/plugins/DupFileManager/DupFileManager.yml
index c75f561f..3d2f6ff1 100644
--- a/plugins/DupFileManager/DupFileManager.yml
+++ b/plugins/DupFileManager/DupFileManager.yml
@@ -1,55 +1,70 @@
 name: DupFileManager
 description: Manages duplicate files.
-version: 0.1.2
+version: 0.1.9
 url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
+ui:
+  javascript:
+    - DupFileManager.js
+  css:
+    - DupFileManager.css
+    - DupFileManager.css.map
+    - DupFileManager.js.map
 settings:
+  matchDupDistance:
+    displayName: Match Duplicate Distance
+    description: (Default=0) Where 0 = Exact Match, 1 = High Match, 2 = Medium Match, and 3 = Low Match.
+    type: NUMBER
   mergeDupFilename:
     displayName: Merge Duplicate Tags
     description: Before deletion, merge metadata from duplicate. E.g. Tag names, performers, studios, title, galleries, rating, details, etc...
     type: BOOLEAN
-  permanentlyDelete:
-    displayName: Permanent Delete
-    description: Enable to permanently delete files, instead of moving files to trash can.
-    type: BOOLEAN
   whitelistDelDupInSameFolder:
     displayName: Whitelist Delete In Same Folder
     description: Allow whitelist deletion of duplicates within the same whitelist folder.
     type: BOOLEAN
-  whitelistDoTagLowResDup:
-    displayName: Whitelist Duplicate Tagging
-    description: Enable to tag whitelist duplicates of lower resolution or duration or same folder.
-    type: BOOLEAN
-  zCleanAfterDel:
-    displayName: Run Clean After Delete
-    description: After running a 'Delete Duplicates' task, run Clean, Clean-Generated, and Optimize-Database.
-    type: BOOLEAN
-  zSwapHighRes:
-    displayName: Swap High Resolution
-    description: If enabled, swap higher resolution duplicate files to preferred path.
-    type: BOOLEAN
-  zSwapLongLength:
-    displayName: Swap Longer Duration
-    description: If enabled, swap longer duration media files to preferred path. Longer is determine by significantLongerTime field.
-    type: BOOLEAN
-  zWhitelist:
+  zvWhitelist:
     displayName: White List
     description: A comma seperated list of paths NOT to be deleted. E.g. C:\Favorite\,E:\MustKeep\
     type: STRING
-  zxGraylist:
+  zwGraylist:
     displayName: Gray List
-    description: List of preferential paths to determine which duplicate should be the primary. E.g. C:\2nd_Favorite\,H:\ShouldKeep\
+    description: Preferential paths to determine which duplicate should be kept. E.g. C:\2nd_Fav,C:\3rd_Fav,C:\4th_Fav,H:\ShouldKeep
     type: STRING
-  zyBlacklist:
+  zxBlacklist:
     displayName: Black List
-    description: List of LEAST preferential paths to determine primary candidates for deletion. E.g. C:\Downloads\,F:\DeleteMeFirst\
+    description: Least preferential paths; Determine primary deletion candidates. E.g. C:\Downloads,C:\DelMe-3rd,C:\DelMe-2nd,C:\DeleteMeFirst
     type: STRING
   zyMaxDupToProcess:
     displayName: Max Dup Process
-    description: Maximum number of duplicates to process. If 0, infinity
+    description: (Default=0) Maximum number of duplicates to process. If 0, infinity.
     type: NUMBER
-  zzdebugTracing:
-    displayName: Debug Tracing
-    description: (Default=false) [***For Advanced Users***] Enable debug tracing. When enabled, additional tracing logging is added to Stash\plugins\DupFileManager\DupFileManager.log
+  zySwapBetterBitRate:
+    displayName: Swap Better Bit Rate
+    description: Swap better bit rate for duplicate files. Use with DupFileManager_config.py file option favorHighBitRate
+    type: BOOLEAN
+  zySwapBetterFrameRate:
+    displayName: Swap Better Frame Rate
+    description: Swap better frame rate for duplicates. Use with DupFileManager_config.py file option favorHigherFrameRate
+    type: BOOLEAN
+  zySwapCodec:
+    displayName: Swap Better Codec
+    description: If enabled, swap better codec duplicate files to preferred path.
+    type: BOOLEAN
+  zySwapHighRes:
+    displayName: Swap Higher Resolution
+    description: If enabled, swap higher resolution duplicate files to preferred path.
+    type: BOOLEAN
+  zySwapLongLength:
+    displayName: Swap Longer Duration
+    description: If enabled, swap longer duration media files to preferred path. Longer is determine by significantLongerTime field.
+    type: BOOLEAN
+  zzDebug:
+    displayName: Debug
+    description: Enable debug so-as to add additional debug logging in Stash\plugins\DupFileManager\DupFileManager.log
+    type: BOOLEAN
+  zzTracing:
+    displayName: Tracing
+    description: Enable tracing and debug so-as to add additional tracing and debug logging in Stash\plugins\DupFileManager\DupFileManager.log
     type: BOOLEAN
 exec:
   - python
@@ -60,7 +75,11 @@ tasks:
     description: Set tag DuplicateMarkForDeletion to the duplicates with lower resolution, duration, file name length, or black list path.
     defaultArgs:
       mode: tag_duplicates_task
-  - name: Delete Tagged Duplicates
+  - name: Clear Tags
+    description: Clear tag DuplicateMarkForDeletion. Remove the tag from all files.
+    defaultArgs:
+      mode: clear_duplicate_tags_task
+  - name: Delete Tagged Scenes
     description: Only delete scenes having DuplicateMarkForDeletion tag.
     defaultArgs:
       mode: delete_tagged_duplicates_task
diff --git a/plugins/DupFileManager/DupFileManager_config.py b/plugins/DupFileManager/DupFileManager_config.py
index ab5b8178..65ee067c 100644
--- a/plugins/DupFileManager/DupFileManager_config.py
+++ b/plugins/DupFileManager/DupFileManager_config.py
@@ -8,19 +8,85 @@
     "dup_path": "", #Example: "C:\\TempDeleteFolder"
     # The threshold as to what percentage is consider a significant shorter time.
     "significantTimeDiff" : .90, # 90% threshold
-    # Valued passed to stash API function FindDuplicateScenes.
-    "duration_diff" : 10, # (default=10) A value from 1 to 10.
     # If enabled, moves destination file to recycle bin before swapping Hi-Res file.
     "toRecycleBeforeSwap" : True,
     # Character used to seperate items on the whitelist, blacklist, and graylist
     "listSeparator" : ",",
+    # Enable to permanently delete files, instead of moving files to trash can.
+    "permanentlyDelete" : False,
+    # After running a 'Delete Duplicates' task, run Clean, Clean-Generated, and Optimize-Database.
+    "cleanAfterDel" : True,
+    # Generate PHASH after tag or delete task.
+    "doGeneratePhash" : False,
+    # If enabled, skip processing tagged scenes. This option is ignored if createHtmlReport is True
+    "skipIfTagged" : False,
+    # If enabled, stop multiple scanning jobs after processing duplicates
+    "killScanningPostProcess" : True,
+    # If enabled, tag scenes which have longer duration, but lower resolution
+    "tagLongDurationLowRes" : True,
+    # If enabled, bit-rate is used in important comparisons for function allThingsEqual
+    "bitRateIsImporantComp" : True,
+    # If enabled, codec is used in important comparisons for function allThingsEqual
+    "codecIsImporantComp" : True,
+    
+    # Tag names **************************************************
     # Tag used to tag duplicates with lower resolution, duration, and file name length.
     "DupFileTag" : "DuplicateMarkForDeletion",
-    # Tag name used to tag duplicates in the whitelist. E.g. DuplicateWhitelistFile
-    "DupWhiteListTag" : "DuplicateWhitelistFile",
+    # Tag name used to tag duplicates in the whitelist. E.g. _DuplicateWhitelistFile
+    "DupWhiteListTag" : "_DuplicateWhitelistFile",
+    # Tag name used to exclude duplicate from deletion
+    "excludeDupFileDeleteTag" : "_ExcludeDuplicateMarkForDeletion",
+    # Tag name used to tag scenes with existing tag DuplicateMarkForDeletion, and that are in the graylist
+    "graylistMarkForDeletion" : "_GraylistMarkForDeletion",
+    # Tag name for scenes with significant longer duration but lower resolution
+    "longerDurationLowerResolution" : "_LongerDurationLowerResolution",
+    
+    # Other tag related options **************************************************
+    # If enabled, when adding tag DuplicateMarkForDeletion to graylist scene, also add tag _GraylistMarkForDeletion.
+    "graylistTagging" : True,
+    # If enabled, the Clear Tags task clears scenes of all tags (DuplicateMarkForDeletion, _DuplicateWhite..., _ExcludeDup..., _Graylist..., _LongerDur...)
+    "clearAllDupfileManagerTags" : True,
+    # If enabled, append dup tag name with match duplicate distance number. I.E. (DuplicateMarkForDeletion_0) or (DuplicateMarkForDeletion_1)
+    "appendMatchDupDistance" : True,
+    # If enabled, start dup tag name with an underscore. I.E. (_DuplicateMarkForDeletion). Places tag at the end of tag list.
+    "underscoreDupFileTag" : True,
+    
+    # Favor setings *********************************************
+    # If enabled, favor longer file name over shorter. If disabled, favor shorter file name.
+    "favorLongerFileName" : True,
+    # If enabled, favor larger file size over smaller. If disabled, favor smaller file size.
+    "favorLargerFileSize" : True,
+    # If enabled, favor videos with a different bit rate value. If favorHighBitRate is true, favor higher rate. If favorHighBitRate is false, favor lower rate
+    "favorBitRateChange" : True,
+    # If enabled, favor videos with higher bit rate. Used with either favorBitRateChange option or UI [Swap Bit Rate Change] option.
+    "favorHighBitRate" : True,
+    # If enabled, favor videos with a different frame rate value. If favorHigherFrameRate is true, favor higher rate. If favorHigherFrameRate is false, favor lower rate
+    "favorFrameRateChange" : True,
+    # If enabled, favor videos with higher frame rate. Used with either favorFrameRateChange option or UI [Swap Better Frame Rate] option.
+    "favorHigherFrameRate" : True,
+    # If enabled, favor videos with better codec according to codecRanking
+    "favorCodecRanking" : True,
+    # Codec Ranking in order of preference (default (codecRankingSet1) is order of ranking based on maximum potential efficiency)
+    "codecRankingSet1"      : ["h266", "vvc", "av1", "vvdec", "shvc", "h265", "hevc", "xvc", "vp9", "h264", "avc", "mvc", "msmpeg4v10", "vp8", "vcb", "msmpeg4v3", "h263", "h263i", "msmpeg4v2", "msmpeg4v1", "mpeg4", "mpeg-4", "mpeg4video", "theora", "vc3", "vc-3", "vp7", "vp6f", "vp6", "vc1", "vc-1", "mpeg2", "mpeg-2", "mpeg2video", "h262", "h222", "h261", "vp5", "vp4", "vp3", "wmv3", "mpeg1", "mpeg-1", "mpeg1video", "vp3", "wmv2", "wmv1", "wmv", "flv1", "png", "gif", "jpeg", "m-jpeg", "mjpeg"],
+    # codecRankingSet2 is in order of least potential efficiency
+    "codecRankingSet2"      : ["gif", "png", "flv1", "mpeg1video", "mpeg1", "wmv1", "wmv2", "wmv3", "mpeg2video", "mpeg2", "AVC", "vc1", "vc-1", "msmpeg4v1", "msmpeg4v2", "msmpeg4v3", "mpeg4", "vp6f", "vp8", "h263i", "h263", "h264", "h265", "av1", "vp9", "h266"],
+    # codecRankingSet3 is in order of quality
+    "codecRankingSet3"      : ["h266", "vp9", "av1", "h265", "h264", "h263", "h263i", "vp8", "vp6f", "mpeg4", "msmpeg4v3", "msmpeg4v2", "msmpeg4v1", "vc-1", "vc1", "AVC", "mpeg2", "mpeg2video", "wmv3", "wmv2", "wmv1", "mpeg1", "mpeg1video", "flv1", "png", "gif"],
+    # codecRankingSet4 is in order of compatibility 
+    "codecRankingSet4"      : ["h264", "vp8", "mpeg4", "msmpeg4v3", "msmpeg4v2", "msmpeg4v1", "h266", "vp9", "av1", "h265", "h263", "h263i", "vp6f", "vc-1", "vc1", "AVC", "mpeg2", "mpeg2video", "wmv3", "wmv2", "wmv1", "mpeg1", "mpeg1video", "flv1", "png", "gif"],
+    # Determines which codecRankingSet to use when ranking codec. Default is 1 for codecRankingSet1
+    "codecRankingSetToUse"  : 1,
     
     # The following fields are ONLY used when running DupFileManager in script mode
     "endpoint_Scheme" : "http", # Define endpoint to use when contacting the Stash server
     "endpoint_Host" : "0.0.0.0", # Define endpoint to use when contacting the Stash server
     "endpoint_Port" : 9999, # Define endpoint to use when contacting the Stash server
 }
+
+# Codec ranking research source:
+    # https://imagekit.io/blog/video-encoding/
+    # https://support.spinetix.com/wiki/Video_decoding
+    # https://en.wikipedia.org/wiki/Comparison_of_video_codecs
+    # https://en.wikipedia.org/wiki/List_of_open-source_codecs
+    # https://en.wikipedia.org/wiki/List_of_codecs
+    # https://en.wikipedia.org/wiki/Comparison_of_video_container_formats
diff --git a/plugins/DupFileManager/DupFileManager_report_config.py b/plugins/DupFileManager/DupFileManager_report_config.py
new file mode 100644
index 00000000..81151229
--- /dev/null
+++ b/plugins/DupFileManager/DupFileManager_report_config.py
@@ -0,0 +1,212 @@
+# Description: This is a Stash plugin which manages duplicate files.
+# By David Maisonave (aka Axter) Jul-2024 (https://www.axter.com/)
+# Get the latest developers version from following link:
+# https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
+
+# HTML Report Options **************************************************
+report_config = {    
+    # Paginate HTML report. Maximum number of results to display on one page, before adding (paginating) an additional page.
+    "htmlReportPaginate" : 100,
+    # Name of the HTML file to create
+    "htmlReportName" : "DuplicateTagScenes.html",
+    # If enabled, report displays an image preview similar to sceneDuplicateChecker
+    "htmlIncludeImagePreview" : False,
+    "htmlImagePreviewPopupSize" : 600,
+    # HTML report prefix, before table listing
+    "htmlReportPrefix" : """<!DOCTYPE html>
+<html>
+<head>
+<title>Stash Duplicate Report</title>
+<style>
+h2 {text-align: center;}
+table, th, td {border:1px solid black;}
+.inline {
+  display: inline;
+}
+.scene-details{text-align: center;font-size: small;}
+.reason-details{text-align: left;font-size: small;}
+.link-items{text-align: center;font-size: small;}
+.link-button {
+  background: none;
+  border: none;
+  color: blue;
+  text-decoration: underline;
+  cursor: pointer;
+  font-size: 1em;
+  font-family: serif;
+  text-align: center;
+  font-size: small;
+}
+.link-button:focus {
+  outline: none;
+}
+.link-button:active {
+  color:red;
+}
+ul {
+  display: flex;
+}
+
+li {
+  list-style-type: none;
+  padding: 10px;
+  position: relative;
+}
+.large {
+  position: absolute;
+  left: -9999px;
+}
+li:hover .large {
+  left: 20px;
+  top: -150px;
+}
+.large-image {
+  border-radius: 4px;
+   box-shadow: 1px 1px 3px 3px rgba(127, 127, 127, 0.15);;
+}
+</style>
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
+<script src="https://www.axter.com/js/jquery.prompt.js"></script>
+<link rel="stylesheet" href="https://www.axter.com/js/jquery.prompt.css"/>
+<script>
+function trim(str, ch) {
+    var start = 0, end = str.length;
+    while(start < end && str[start] === ch) ++start;
+    while(end > start && str[end - 1] === ch) --end;
+    return (start > 0 || end < str.length) ? str.substring(start, end) : str;
+}
+function RunPluginOperation(Mode, ActionID, button, asyncAjax){
+	var chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
+    $.ajax({method: "POST", url: "http://localhost:9999/graphql", contentType: "application/json", dataType: "text", cache: asyncAjax, async: asyncAjax,
+	data: JSON.stringify({
+			query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
+			variables: {"plugin_id": "DupFileManager", "args": { "Target" : ActionID, "mode":Mode}},
+		}), success: function(result){
+			console.log(result);
+            // if (Mode !== "flagScene") button.style.visibility = 'hidden';
+            if (Mode === "renameFile"){
+                var myArray = ActionID.split(":");
+                $('.FN_ID_' + myArray[0]).text(trim(myArray[1],"'"));
+            }
+			if (!chkBxRemoveValid.checked) alert("Action " + Mode + " for scene(s) ID# " + ActionID + " complete.");
+	}});
+}
+function selectMarker(Mode, ActionID, button){
+	$('<p>Select desire marker type <select><option>yellow highlight</option><option>green highlight</option><option>orange highlight</option><option>cyan highlight</option><option>pink highlight</option><option>red highlight</option><option>strike-through</option><option>disable-scene</option><option>remove all flags</option></select></p>').confirm(function(answer){
+		if(answer.response){
+            console.log("Selected " + $('select',this).val());
+            var flagType = $('select',this).val();
+            if (flagType == null){
+                console.log("Invalid flagType");
+                return;
+            }
+            if (flagType === "yellow highlight")
+                $('.ID_' + ActionID).css('background','yellow');
+            else if (flagType === "green highlight")
+                $('.ID_' + ActionID).css('background','#00FF00');
+            else if (flagType === "orange highlight")
+                $('.ID_' + ActionID).css('background','orange');
+            else if (flagType === "cyan highlight")
+                $('.ID_' + ActionID).css('background','cyan');
+            else if (flagType === "pink highlight")
+                $('.ID_' + ActionID).css('background','pink');
+            else if (flagType === "red highlight")
+                $('.ID_' + ActionID).css('background','red');
+            else if (flagType === "strike-through")
+                $('.ID_' + ActionID).css('text-decoration', 'line-through');
+            else if (flagType === "disable-scene")
+                $('.ID_' + ActionID).css({ 'background' : 'gray', 'pointer-events' : 'none' });
+            else if (flagType === "remove all flags")
+                $('.ID_' + ActionID).removeAttr('style'); //.css({ 'background' : '', 'text-decoration' : '', 'pointer-events' : '' });
+            else {
+                flagType = "none";
+                $('.ID_' + ActionID).css("target-property", "");
+                return;
+            }
+            ActionID = ActionID + ":" + flagType;
+            console.log("ActionID = " + ActionID);
+            RunPluginOperation(Mode, ActionID, button, false);
+        }
+        else console.log("Not valid response");
+	});
+}
+$(document).ready(function(){
+  $("button").click(function(){
+    var Mode = this.value;
+    var ActionID = this.id;
+	if (ActionID === "AdvanceMenu")
+    {
+		var newUrl = window.location.href;
+		newUrl = newUrl.replace(/report\/DuplicateTagScenes[_0-9]*.html/g, "advance_options.html?GQL=http://localhost:9999/graphql");
+		window.open(newUrl, "_blank");
+        return;
+    }
+	if (Mode === "deleteScene" || Mode === "removeScene"){
+		var chkBxDisableDeleteConfirm = document.getElementById("RemoveToKeepConfirm");
+        question = "Are you sure you want to delete this file and remove scene from stash?";
+        if (Mode === "removeScene") question = "Are you sure you want to remove scene from stash?";
+		if (!chkBxDisableDeleteConfirm.checked && !confirm(question))
+			return;
+        $('.ID_' + ActionID).css('background-color','gray');
+        $('.ID_' + ActionID).css('pointer-events','none');
+	}
+	else if (Mode === "newName" || Mode === "renameFile"){
+	    var myArray = ActionID.split(":");
+		var promptStr = "Enter new name for scene ID " + myArray[0] + ", or press escape to cancel.";
+		if (Mode === "renameFile") 
+			promptStr = "Press enter to rename scene ID " + myArray[0] + ", or press escape to cancel.";
+	    var newName=prompt(promptStr,trim(myArray[1], "'"));
+	    if (newName === null)
+	        return;
+	    ActionID = myArray[0] + ":" + newName;
+	    Mode = "renameFile";
+    }
+	else if (Mode === "flagScene"){
+	    selectMarker(Mode, ActionID, this);
+        return;
+    }
+    RunPluginOperation(Mode, ActionID, this, true);
+  });
+});
+</script>
+</head>
+<body>
+<center><table style="color:darkgreen;background-color:powderblue;">
+<tr><th>Report Info</th><th>Report Options</th></tr>
+<tr>
+<td><table><tr>
+<td>Found (QtyPlaceHolder) duplice sets</td>
+<td>Date Created: (DateCreatedPlaceHolder)</td>
+</tr></table></td>
+<td><table><tr>
+<td><input type="checkbox" id="RemoveValidatePrompt" name="RemoveValidatePrompt"><label for="RemoveValidatePrompt" title="Disable notice for task completion (Popup).">Disable Complete Confirmation</label><br></td>
+<td><input type="checkbox" id="RemoveToKeepConfirm" name="RemoveToKeepConfirm"><label for="RemoveToKeepConfirm" title="Disable confirmation prompts for delete scenes">Disable Delete Confirmation</label><br></td>
+<td><button id="AdvanceMenu" title="View advance menu for tagged duplicates." name="AdvanceMenu">Advance Tag Menu</button></td>
+</tr></table></td>
+</tr></table></center>
+<h2>Stash Duplicate Scenes Report (MatchTypePlaceHolder)</h2>\n""",
+    # HTML report postfiox, after table listing
+    "htmlReportPostfix" : "\n</body></html>",
+    # HTML report table
+    "htmlReportTable" : "<table style=\"width:100%\">",
+    # HTML report table row
+    "htmlReportTableRow" : "<tr>",
+    # HTML report table header
+    "htmlReportTableHeader" : "<th>",
+    # HTML report table data
+    "htmlReportTableData" : "<td>",
+     # HTML report video preview
+    "htmlReportVideoPreview" : "width='160' height='120' controls", # Alternative option "autoplay loop controls" or "autoplay controls"
+    # The number off seconds in time difference for supper highlight on htmlReport
+    "htmlHighlightTimeDiff" : 3,
+    # Supper highlight for details with higher resolution or duration
+    "htmlSupperHighlight" : "yellow",
+    # Lower highlight for details with slightly higher duration
+    "htmlLowerHighlight" : "nyanza",
+    # Text color for details with different resolution, duration, size, bitrate,codec, or framerate
+    "htmlDetailDiffTextColor" : "red",
+    # If enabled, create an HTML report when tagging duplicate files
+    "createHtmlReport" : True,
+    # If enabled, report displays stream instead of preview for video
+    "streamOverPreview" : False, # This option works in Chrome, but does not work very well on firefox.
+}
diff --git a/plugins/DupFileManager/ModulesValidate.py b/plugins/DupFileManager/ModulesValidate.py
new file mode 100644
index 00000000..4de2f3a4
--- /dev/null
+++ b/plugins/DupFileManager/ModulesValidate.py
@@ -0,0 +1,126 @@
+# ModulesValidate (By David Maisonave aka Axter)
+# Description:
+#               Checks if packages are installed, and optionally install packages if missing.
+#               The below example usage code should be plave at the very top of the scource code before any other imports.
+# Example Usage:
+#                import ModulesValidate
+#                ModulesValidate.modulesInstalled(["watchdog", "schedule", "requests"])
+# Testing:
+#           To test, uninstall packages via command line: pip uninstall -y watchdog schedule requests
+import sys, os, pathlib, platform, traceback
+# ToDo: Add logic to optionally pull package requirements from requirements.txt file.
+
+def modulesInstalled(moduleNames, install=True, silent=False):
+    retrnValue = True
+    for moduleName in moduleNames:
+        try: # Try Python 3.3 > way
+            import importlib
+            import importlib.util
+            if moduleName in sys.modules:
+                if not silent: print(f"{moduleName!r} already in sys.modules")
+            elif isModuleInstalled(moduleName):
+                if not silent: print(f"Module {moduleName!r} is available.")
+            else:
+                if install and (results:=installModule(moduleName)) > 0:
+                    if results == 1:
+                        print(f"Module {moduleName!r} has been installed")
+                    else:
+                        if not silent: print(f"Module {moduleName!r} is already installed")
+                    continue
+                else:
+                    if install:
+                        print(f"Can't find the {moduleName!r} module") 
+                    retrnValue = False
+        except Exception as e:
+            try:
+                i = importlib.import_module(moduleName)
+            except ImportError as e:
+                if install and (results:=installModule(moduleName)) > 0:
+                    if results == 1:
+                        print(f"Module {moduleName!r} has been installed")
+                    else:
+                        if not silent: print(f"Module {moduleName!r} is already installed")
+                    continue
+                else:
+                    if install:
+                        tb = traceback.format_exc()
+                        print(f"Can't find the {moduleName!r} module! Error: {e}\nTraceBack={tb}") 
+                    retrnValue = False
+    return retrnValue
+
+def isModuleInstalled(moduleName):
+    try:
+        __import__(moduleName)
+        return True
+    except Exception as e:
+        pass
+    return False
+
+def installModule(moduleName):
+    try:
+        if isLinux():
+            # Note: Linux may first need : sudo apt install python3-pip
+            #       if error starts with "Command 'pip' not found"
+            #       or includes "No module named pip"
+            results = os.popen(f"pip --disable-pip-version-check --version").read()
+            if results.find("Command 'pip' not found") != -1 or results.find("No module named pip") != -1:
+                results = os.popen(f"sudo apt install python3-pip").read()
+                results = os.popen(f"pip --disable-pip-version-check --version").read()
+                if results.find("Command 'pip' not found") != -1 or results.find("No module named pip") != -1:
+                    return -1
+        if isFreeBSD():
+            print("Warning: installModule may NOT work on freebsd")
+        pipArg = " --disable-pip-version-check"
+        if isDocker():
+            pipArg += " --break-system-packages"
+        results = os.popen(f"{sys.executable} -m pip install {moduleName}{pipArg}").read() # May need to be f"{sys.executable} -m pip install {moduleName}"
+        results = results.strip("\n")
+        if results.find("Requirement already satisfied:") > -1:
+            return 2
+        elif results.find("Successfully installed") > -1:
+            return 1
+        elif modulesInstalled(moduleNames=[moduleName], install=False):
+            return 1
+    except Exception as e:
+        pass
+    return 0
+
+def installPackage(package): # Should delete this.  It doesn't work consistently
+    try:
+        import pip
+        if hasattr(pip, 'main'):
+            pip.main(['install', package])
+        else:
+            pip._internal.main(['install', package])
+    except Exception as e:
+        return False
+    return True
+
+def isDocker():
+    cgroup = pathlib.Path('/proc/self/cgroup')
+    return pathlib.Path('/.dockerenv').is_file() or cgroup.is_file() and 'docker' in cgroup.read_text()
+
+def isWindows():
+    if any(platform.win32_ver()):
+        return True
+    return False
+
+def isLinux():
+    if platform.system().lower().startswith("linux"):
+        return True
+    return False
+
+def isFreeBSD():
+    if platform.system().lower().startswith("freebsd"):
+        return True
+    return False
+
+def isMacOS():
+    if sys.platform == "darwin":
+        return True
+    return False
+
+def isWindows():
+    if any(platform.win32_ver()):
+        return True
+    return False
diff --git a/plugins/DupFileManager/README.md b/plugins/DupFileManager/README.md
index 7d0cf052..0a90703c 100644
--- a/plugins/DupFileManager/README.md
+++ b/plugins/DupFileManager/README.md
@@ -1,11 +1,40 @@
-# DupFileManager: Ver 0.1.2 (By David Maisonave)
+# DupFileManager: Ver 0.1.9 (By David Maisonave)
 
-DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which manages duplicate file in the Stash system.
+DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which manages duplicate files in the Stash system.
+It has both **task** and **tools-UI** components.
 
 ### Features
 
+- Creates a duplicate file report which can be accessed from the settings->tools menu options.The report is created as an HTML file and stored in local path under plugins\DupFileManager\report\DuplicateTagScenes.html.
+  - See screenshot at the bottom of this page for example report.
+  - Items on the left side of the report are the primary duplicates designated for deletion. By default, these duplicates are given a special \_duplicate tag.
+  - Items on the right side of the report are designated as primary duplicates to keep. They usually have higher resolution, duration and/or preferred paths.
+  - The report has the following options:
+    - Delete: Delete file and remove from Stash library.
+    - Remove: Remove from Stash library.
+    - Rename: Rename file.
+    - Copy: Copy file from left (source) to right (to-keep).
+    - Move: Copy file and metadata left to right.
+    - Cpy-Name: Copy file name left to right.
+    - Add-Exclude: Add exclude tag to scene,so that scene is excluded from deletion.
+    - Remove-Tag: Remove duplicate tag from scene.
+    - Flag-Scene: Flag (mark) scene in report as reviewed (or as requiring further review). Optional flags (yellow, green, orange, cyan, pink, red, strike-through, & disable-scene)
+    - Merge: Copy Metadata (tags, performers,& studios) from left to right.
 - Can merge potential source in the duplicate file names for tag names, performers, and studios.
   - Normally when Stash searches the file name for tag names, performers, and studios, it only does so using the primary file.
+- Advance menu (for specially tagged duplicates)
+  ![Screenshot 2024-11-22 145139](https://github.com/user-attachments/assets/d76646f0-c5a8-4069-ad0f-a6e5e96e7ed0)
+  - Delete only specially tagged duplicates in blacklist path.
+  - Delete duplicates with specified file path.
+  - Delete duplicates with specific string in File name.
+  - Delete duplicates with specified file size range.
+  - Delete with specified duration range.
+  - Delete with resolution range.
+  - Delete duplicates having specified tags.
+  - Delete duplicates with specified rating.
+  - Delete duplicates with any of the above combinations.
+- Bottom extended portion of the Advanced Menu screen.
+  - ![Screenshot 2024-11-22 232005](https://github.com/user-attachments/assets/9a0d2e9d-783b-4ea2-8fa5-3805b40af4eb)
 - Delete duplicate file task with the following options:
   - Tasks (Settings->Task->[Plugin Tasks]->DupFileManager)
     - **Tag Duplicates** - Set tag DuplicateMarkForDeletion to the duplicates with lower resolution, duration, file name length, and/or black list path.
@@ -13,11 +42,11 @@ DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which mana
     - **Delete Duplicates** - Deletes duplicate files. Performs deletion without first tagging.
   - Plugin UI options (Settings->Plugins->Plugins->[DupFileManager])
     - Has a 3 tier path selection to determine which duplicates to keep, and which should be candidates for deletions.
-      - **Whitelist** - List of paths NOT to be deleted. 
+      - **Whitelist** - List of paths NOT to be deleted.
         - E.g. C:\Favorite\,E:\MustKeep\
-      - **Gray-List** - List of preferential paths to determine which duplicate should be the primary. 
+      - **Gray-List** - List of preferential paths to determine which duplicate should be the primary.
         - E.g. C:\2nd_Favorite\,H:\ShouldKeep\
-      - **Blacklist** - List of LEAST preferential paths to determine primary candidates for deletion. 
+      - **Blacklist** - List of LEAST preferential paths to determine primary candidates for deletion.
         - E.g. C:\Downloads\,F:\DeleteMeFirst\
     - **Permanent Delete** - Enable to permanently delete files, instead of moving files to trash can.
     - **Max Dup Process** - Use to limit the maximum files to process. Can be used to do a limited test run.
@@ -28,12 +57,15 @@ DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which mana
     - **dup_path** - Alternate path to move deleted files to. Example: "C:\TempDeleteFolder"
     - **toRecycleBeforeSwap** - When enabled, moves destination file to recycle bin before swapping files.
     - **addPrimaryDupPathToDetails** - If enabled, adds the primary duplicate path to the scene detail.
+- Tools UI Menu
+  ![Screenshot 2024-11-22 145512](https://github.com/user-attachments/assets/03e166eb-ddaa-4eb8-8160-4c9180ca1323)
+  - Can access either **Duplicate File Report (DupFileManager)** or **DupFileManager Tools and Utilities** menu options.
 
 ### Requirements
 
-`pip install --upgrade stashapp-tools`
-`pip install pyYAML`
-`pip install Send2Trash`
+- `pip install --upgrade stashapp-tools`
+- `pip install requests`
+- `pip install Send2Trash`
 
 ### Installation
 
@@ -48,3 +80,31 @@ That's it!!!
 
 - Options are accessible in the GUI via Settings->Plugins->Plugins->[DupFileManager].
 - More options available in DupFileManager_config.py.
+
+### Screenshots
+
+- Example DupFileManager duplicate report. (file names have been edited to PG).
+  - The report displays preview videos that are playable. Will play a few seconds sample of the video. This requires scan setting **[Generate animated image previews]** to be enabled when scanning all files.
+  - ![Screenshot 2024-11-22 225359](https://github.com/user-attachments/assets/dc705b24-e2d7-4663-92fd-1516aa7aacf5)
+  - If there's a scene on the left side that has a higher resolution or duration, it gets a yellow highlight on the report.
+  - There's an optional setting that allows both preview videos and preview images to be displayed on the report. See settings **htmlIncludeImagePreview** in the **DupFileManager_report_config.py** file.
+  - There are many more options available for how the report is created. These options are targeted for more advanced users. The options are all available in the **DupFileManager_report_config.py** file, and the settings have commented descriptions preceeding them. See the **DupFileManager_report_config.py** file in the DupFileManager plugin folder for more details.
+- Tools UI Menu
+  ![Screenshot 2024-11-22 145512](https://github.com/user-attachments/assets/03e166eb-ddaa-4eb8-8160-4c9180ca1323)
+  - Can access either **Duplicate File Report (DupFileManager)** or **DupFileManager Tools and Utilities** menu options.
+- DupFileManager Report Menu
+  - ![Screenshot 2024-11-22 151630](https://github.com/user-attachments/assets/834ee60f-1a4a-4a3e-bbf7-23aeca2bda1f)
+- DupFileManager Tools and Utilities
+  - ![Screenshot 2024-11-22 152023](https://github.com/user-attachments/assets/4daaea9e-f603-4619-b536-e6609135bab1)
+- Full bottom extended portion of the Advanced Menu screen.
+  - ![Screenshot 2024-11-22 232208](https://github.com/user-attachments/assets/bf1f3021-3a8c-4875-9737-60ee3d7fe675)
+
+### Future Planned Features
+
+- Currently, the report and advanced menu do not work with Stash settings requiring a password. Additional logic will be added to have them use the API Key. Planned for 1.0.0 Version.
+- Add an advanced menu that will work with non-tagged reports. It will iterated through the existing report file(s) to aplly deletions, instead of searching Stash DB for tagged files. Planned for 1.1.0 Version.
+- Greylist deletion option will be added to the advanced menu. Planned for 1.0.5 Version.
+- Add advanced menu directly to the Settings->Tools menu. Planned for 1.5.0 Version.
+- Add report directly to the Settings->Tools menu. Planned for 1.5.0 Version.
+- Remove all flags from all scenes option. Planned for 1.0.5 Version.
+- Transfer option settings **[Disable Complete Confirmation]** and **[Disable Delete Confirmation]** when paginating. Planned for 1.0.5 Version.
diff --git a/plugins/DupFileManager/StashPluginHelper.py b/plugins/DupFileManager/StashPluginHelper.py
index 6f0d3d15..a9be414e 100644
--- a/plugins/DupFileManager/StashPluginHelper.py
+++ b/plugins/DupFileManager/StashPluginHelper.py
@@ -1,12 +1,3 @@
-from stashapi.stashapp import StashInterface
-from logging.handlers import RotatingFileHandler
-import re, inspect, sys, os, pathlib, logging, json
-import concurrent.futures
-from stashapi.stash_types import PhashDistance
-import __main__
-
-_ARGUMENT_UNSPECIFIED_ = "_ARGUMENT_UNSPECIFIED_"
-
 # StashPluginHelper (By David Maisonave aka Axter)
     # See end of this file for example usage
     # Log Features:
@@ -24,6 +15,14 @@
         # Gets DEBUG_TRACING value from command line argument and/or from UI and/or from config file
         # Sets RUNNING_IN_COMMAND_LINE_MODE to True if detects multiple arguments
         # Sets CALLED_AS_STASH_PLUGIN to True if it's able to read from STDIN_READ
+from stashapi.stashapp import StashInterface
+from logging.handlers import RotatingFileHandler
+import re, inspect, sys, os, pathlib, logging, json, platform, subprocess, traceback, time
+import concurrent.futures
+from stashapi.stash_types import PhashDistance
+from enum import Enum, IntEnum
+import __main__
+
 class StashPluginHelper(StashInterface):
     # Primary Members for external reference
     PLUGIN_TASK_NAME = None
@@ -45,15 +44,44 @@ class StashPluginHelper(StashInterface):
     API_KEY = None
     excludeMergeTags = None
     
+    # class EnumInt(IntEnum):
+        # def __repr__(self) -> str:
+            # return f"{self.__class__.__name__}.{self.name}"
+        # def __str__(self) -> str:
+            # return str(self.value)
+        # def serialize(self):
+            # return self.value
+    
+    class EnumValue(Enum):
+        def __repr__(self) -> str:
+            return f"{self.__class__.__name__}.{self.name}"
+        def __str__(self) -> str:
+            return str(self.value)
+        def __add__(self, other):
+            return self.value + other.value
+        def serialize(self):
+            return self.value
+
     # printTo argument
-    LOG_TO_FILE = 1
-    LOG_TO_CONSOLE = 2  # Note: Only see output when running in command line mode. In plugin mode, this output is lost.
-    LOG_TO_STDERR = 4   # Note: In plugin mode, output to StdErr ALWAYS gets sent to stash logging as an error.
-    LOG_TO_STASH = 8
-    LOG_TO_WARN = 16
-    LOG_TO_ERROR = 32
-    LOG_TO_CRITICAL = 64
-    LOG_TO_ALL = LOG_TO_FILE + LOG_TO_CONSOLE + LOG_TO_STDERR + LOG_TO_STASH
+    class LogTo(IntEnum):
+        FILE = 1
+        CONSOLE = 2  # Note: Only see output when running in command line mode. In plugin mode, this output is lost.
+        STDERR = 4   # Note: In plugin mode, output to StdErr ALWAYS gets sent to stash logging as an error.
+        STASH = 8
+        WARN = 16
+        ERROR = 32
+        CRITICAL = 64
+        ALL = FILE + CONSOLE + STDERR + STASH
+    
+    class DbgLevel(IntEnum):
+        TRACE = 1
+        DBG = 2
+        INF = 3
+        WRN = 4
+        ERR = 5
+        CRITICAL = 6
+    
+    DBG_LEVEL = DbgLevel.INF
     
     # Misc class variables
     MAIN_SCRIPT_NAME = None
@@ -61,6 +89,25 @@ class StashPluginHelper(StashInterface):
     LOG_FILE_DIR = None
     LOG_FILE_NAME = None
     STDIN_READ = None
+    stopProcessBarSpin = True
+    updateProgressbarOnIter = 0
+    currentProgressbarIteration = 0
+    
+    class OS_Type(IntEnum):
+        WINDOWS = 1
+        LINUX = 2
+        MAC_OS = 3
+        FREEBSD = 4
+        UNKNOWN_OS = 5
+    
+    OS_TYPE = OS_Type.UNKNOWN_OS
+    
+    IS_DOCKER = False
+    IS_WINDOWS = False
+    IS_LINUX = False
+    IS_FREEBSD = False
+    IS_MAC_OS = False
+    
     pluginLog = None
     logLinePreviousHits = []
     thredPool = None
@@ -68,45 +115,76 @@ class StashPluginHelper(StashInterface):
     _mergeMetadata = None
     encodeToUtf8 = False
     convertToAscii = False # If set True, it takes precedence over encodeToUtf8
+    progressBarIsEnabled = True
     
     # Prefix message value
-    LEV_TRACE = "TRACE: "
-    LEV_DBG = "DBG: "
-    LEV_INF = "INF: "
-    LEV_WRN = "WRN: "
-    LEV_ERR = "ERR: "
-    LEV_CRITICAL = "CRITICAL: "
-    
-    # Default format
-    LOG_FORMAT = "[%(asctime)s] %(message)s"
+    class Level(EnumValue):
+        TRACE = "TRACE: "
+        DBG = "DBG: "
+        INF = "INF: "
+        WRN = "WRN: "
+        ERR = "ERR: "
+        CRITICAL = "CRITICAL: "
     
+    class Constant(EnumValue):
+        # Default format
+        LOG_FORMAT = "[%(asctime)s] %(message)s"
+        ARGUMENT_UNSPECIFIED = "_ARGUMENT_UNSPECIFIED_"
+        NOT_IN_LIST = 2147483646
+        
     # Externally modifiable variables
-    log_to_err_set = LOG_TO_FILE + LOG_TO_STDERR # This can be changed by the calling source in order to customize what targets get error messages
-    log_to_norm = LOG_TO_FILE + LOG_TO_CONSOLE # Can be change so-as to set target output for normal logging
+    log_to_err_set = LogTo.FILE + LogTo.STDERR # This can be changed by the calling source in order to customize what targets get error messages
+    log_to_norm = LogTo.FILE + LogTo.CONSOLE # Can be change so-as to set target output for normal logging
     # Warn message goes to both plugin log file and stash when sent to Stash log file.
-    log_to_wrn_set = LOG_TO_STASH # This can be changed by the calling source in order to customize what targets get warning messages
+    log_to_wrn_set = LogTo.STASH # This can be changed by the calling source in order to customize what targets get warning messages
 
     def __init__(self, 
-                    debugTracing = None,            # Set debugTracing to True so as to output debug and trace logging
-                    logFormat = LOG_FORMAT,         # Plugin log line format
-                    dateFmt = "%y%m%d %H:%M:%S",    # Date format when logging to plugin log file
-                    maxbytes = 8*1024*1024,         # Max size of plugin log file
-                    backupcount = 2,                # Backup counts when log file size reaches max size
-                    logToWrnSet = 0,                # Customize the target output set which will get warning logging
-                    logToErrSet = 0,                # Customize the target output set which will get error logging
-                    logToNormSet = 0,               # Customize the target output set which will get normal logging
-                    logFilePath = "",               # Plugin log file. If empty, the log file name will be set based on current python file name and path
-                    mainScriptName = "",            # The main plugin script file name (full path)
-                    pluginID = "",
-                    settings = None,                # Default settings for UI fields
-                    config = None,                  # From pluginName_config.py or pluginName_setting.py
-                    fragmentServer = None,
-                    stash_url = None,               # Stash URL (endpoint URL) Example: http://localhost:9999
-                    apiKey = None,                  # API Key only needed when username and password set while running script via command line
+                    debugTracing = None,                    # Set debugTracing to True so as to output debug and trace logging
+                    logFormat = Constant.LOG_FORMAT.value,  # Plugin log line format
+                    dateFmt = "%y%m%d %H:%M:%S",            # Date format when logging to plugin log file
+                    maxbytes = 8*1024*1024,                 # Max size of plugin log file
+                    backupcount = 2,                        # Backup counts when log file size reaches max size
+                    logToWrnSet = 0,                        # Customize the target output set which will get warning logging
+                    logToErrSet = 0,                        # Customize the target output set which will get error logging
+                    logToNormSet = 0,                       # Customize the target output set which will get normal logging
+                    logFilePath = "",                       # Plugin log file. If empty, the log file name will be set based on current python file name and path
+                    mainScriptName = "",                    # The main plugin script file name (full path)
+                    pluginID = "",      
+                    settings = None,                        # Default settings for UI fields
+                    config = None,                          # From pluginName_config.py or pluginName_setting.py
+                    fragmentServer = None,      
+                    stash_url = None,                       # Stash URL (endpoint URL) Example: http://localhost:9999
+                    apiKey = None,                          # API Key only needed when username and password set while running script via command line
                     DebugTraceFieldName = "zzdebugTracing",
+                    DebugFieldName = "zzDebug",
                     DryRunFieldName = "zzdryRun",
-                    setStashLoggerAsPluginLogger = False):              
+                    setStashLoggerAsPluginLogger = False,
+                    DBG_LEVEL = DbgLevel.INF):              
+        if DBG_LEVEL in list(self.DbgLevel):
+            self.DBG_LEVEL = DBG_LEVEL
+        if debugTracing:
+            self.DEBUG_TRACING = debugTracing
+            if self.DBG_LEVEL > self.DbgLevel.DBG:
+                self.DBG_LEVEL = self.DbgLevel.TRACE
+        elif self.DBG_LEVEL < self.DbgLevel.INF:
+            self.DEBUG_TRACING = True
         self.thredPool = concurrent.futures.ThreadPoolExecutor(max_workers=2)
+        if self.isWindows():
+            self.IS_WINDOWS = True
+            self.OS_TYPE = self.OS_Type.WINDOWS
+        elif self.isLinux():
+            self.IS_LINUX = True
+            self.OS_TYPE = self.OS_Type.LINUX
+            if self.isDocker():
+                self.IS_DOCKER = True
+        elif self.isFreeBSD():
+            self.IS_FREEBSD = True
+            self.OS_TYPE = self.OS_Type.FREEBSD
+            if self.isDocker():
+                self.IS_DOCKER = True
+        elif self.isMacOS():
+            self.IS_MAC_OS = True
+            self.OS_TYPE = self.OS_Type.MAC_OS
         if logToWrnSet: self.log_to_wrn_set = logToWrnSet
         if logToErrSet: self.log_to_err_set = logToErrSet
         if logToNormSet: self.log_to_norm = logToNormSet
@@ -129,7 +207,6 @@ def __init__(self,
         else:
             self.FRAGMENT_SERVER = {'Scheme': 'http', 'Host': '0.0.0.0', 'Port': '9999', 'SessionCookie': {'Name': 'session', 'Value': '', 'Path': '', 'Domain': '', 'Expires': '0001-01-01T00:00:00Z', 'RawExpires': '', 'MaxAge': 0, 'Secure': False, 'HttpOnly': False, 'SameSite': 0, 'Raw': '', 'Unparsed': None}, 'Dir': os.path.dirname(pathlib.Path(self.MAIN_SCRIPT_NAME).resolve().parent), 'PluginDir': pathlib.Path(self.MAIN_SCRIPT_NAME).resolve().parent}
         
-        if debugTracing: self.DEBUG_TRACING = debugTracing        
         if config:
             self.pluginConfig = config        
             if self.Setting('apiKey', "") != "":
@@ -191,8 +268,14 @@ def __init__(self,
                 self.API_KEY = self.STASH_CONFIGURATION['apiKey']
         
         self.DRY_RUN = self.Setting(DryRunFieldName, self.DRY_RUN)
-        self.DEBUG_TRACING = self.Setting(DebugTraceFieldName, self.DEBUG_TRACING)
-        if self.DEBUG_TRACING: self.LOG_LEVEL = logging.DEBUG
+        if self.Setting(DebugTraceFieldName, self.DEBUG_TRACING):
+            self.DEBUG_TRACING = True
+            self.LOG_LEVEL = logging.TRACE
+            self.DBG_LEVEL = self.DbgLevel.TRACE
+        elif self.Setting(DebugFieldName, self.DEBUG_TRACING):
+            self.DEBUG_TRACING = True
+            self.LOG_LEVEL = logging.DEBUG
+            self.DBG_LEVEL = self.DbgLevel.DBG
         
         logging.basicConfig(level=self.LOG_LEVEL, format=logFormat, datefmt=dateFmt, handlers=[RFH])
         self.pluginLog = logging.getLogger(pathlib.Path(self.MAIN_SCRIPT_NAME).stem)
@@ -202,74 +285,104 @@ def __init__(self,
     def __del__(self):
         self.thredPool.shutdown(wait=False)
     
-    def Setting(self, name, default=_ARGUMENT_UNSPECIFIED_, raiseEx=True, notEmpty=False):
+    def Setting(self, name, default=Constant.ARGUMENT_UNSPECIFIED.value, raiseEx=True, notEmpty=False):
         if self.pluginSettings != None and name in self.pluginSettings:
             if notEmpty == False or self.pluginSettings[name] != "":
                 return self.pluginSettings[name]
         if self.pluginConfig != None and name in self.pluginConfig:
             if notEmpty == False or self.pluginConfig[name] != "":
                 return self.pluginConfig[name]
-        if default == _ARGUMENT_UNSPECIFIED_ and raiseEx:
+        if default == self.Constant.ARGUMENT_UNSPECIFIED.value and raiseEx:
             raise Exception(f"Missing {name} from both UI settings and config file settings.") 
         return default
     
-    def Log(self, logMsg, printTo = 0, logLevel = logging.INFO, lineNo = -1, levelStr = "", logAlways = False, toAscii = None):
-        if toAscii or (toAscii == None and (self.encodeToUtf8 or self.convertToAscii)):
-            logMsg = self.asc2(logMsg)
-        else:
-            logMsg = logMsg
-        if printTo == 0: 
-            printTo = self.log_to_norm
-        elif printTo == self.LOG_TO_ERROR and logLevel == logging.INFO:
-            logLevel = logging.ERROR
-            printTo = self.log_to_err_set
-        elif printTo == self.LOG_TO_CRITICAL and logLevel == logging.INFO:
-            logLevel = logging.CRITICAL
-            printTo = self.log_to_err_set
-        elif printTo == self.LOG_TO_WARN and logLevel == logging.INFO:
-            logLevel = logging.WARN
-            printTo = self.log_to_wrn_set
+    def Log(self, logMsg, printTo = 0, logLevel = logging.INFO, lineNo = -1, levelStr = "", logAlways = False, toAscii = None, printLogException = False):
+        try:
+            if toAscii or (toAscii == None and (self.encodeToUtf8 or self.convertToAscii)):
+                logMsg = self.asc2(logMsg)
+            else:
+                logMsg = logMsg
+            if printTo == 0: 
+                printTo = self.log_to_norm
+            elif printTo == self.LogTo.ERROR and logLevel == logging.INFO:
+                logLevel = logging.ERROR
+                printTo = self.log_to_err_set
+            elif printTo == self.LogTo.CRITICAL and logLevel == logging.INFO:
+                logLevel = logging.CRITICAL
+                printTo = self.log_to_err_set
+            elif printTo == self.LogTo.WARN and logLevel == logging.INFO:
+                logLevel = logging.WARN
+                printTo = self.log_to_wrn_set
+            if lineNo == -1:
+                lineNo = inspect.currentframe().f_back.f_lineno
+            LN_Str = f"[LN:{lineNo}]"
+            # print(f"{LN_Str}, {logAlways}, {self.LOG_LEVEL}, {logging.DEBUG}, {levelStr}, {logMsg}")
+            if logLevel == logging.TRACE and (logAlways == False or self.LOG_LEVEL == logging.TRACE):
+                if levelStr == "": levelStr = self.Level.DBG
+                if printTo & self.LogTo.FILE: self.pluginLog.trace(f"{LN_Str} {levelStr}{logMsg}")
+                if printTo & self.LogTo.STASH: self.log.trace(f"{LN_Str} {levelStr}{logMsg}")
+            elif logLevel == logging.DEBUG and (logAlways == False or self.LOG_LEVEL == logging.DEBUG or self.LOG_LEVEL == logging.TRACE):
+                if levelStr == "": levelStr = self.Level.DBG
+                if printTo & self.LogTo.FILE: self.pluginLog.debug(f"{LN_Str} {levelStr}{logMsg}")
+                if printTo & self.LogTo.STASH: self.log.debug(f"{LN_Str} {levelStr}{logMsg}")
+            elif logLevel == logging.INFO or logLevel == logging.DEBUG:
+                if levelStr == "": levelStr = self.Level.INF if logLevel == logging.INFO else self.Level.DBG
+                if printTo & self.LogTo.FILE: self.pluginLog.info(f"{LN_Str} {levelStr}{logMsg}")
+                if printTo & self.LogTo.STASH: self.log.info(f"{LN_Str} {levelStr}{logMsg}")
+            elif logLevel == logging.WARN:
+                if levelStr == "": levelStr = self.Level.WRN
+                if printTo & self.LogTo.FILE: self.pluginLog.warning(f"{LN_Str} {levelStr}{logMsg}")
+                if printTo & self.LogTo.STASH: self.log.warning(f"{LN_Str} {levelStr}{logMsg}")
+            elif logLevel == logging.ERROR:
+                if levelStr == "": levelStr = self.Level.ERR
+                if printTo & self.LogTo.FILE: self.pluginLog.error(f"{LN_Str} {levelStr}{logMsg}")
+                if printTo & self.LogTo.STASH: self.log.error(f"{LN_Str} {levelStr}{logMsg}")
+            elif logLevel == logging.CRITICAL:
+                if levelStr == "": levelStr = self.Level.CRITICAL
+                if printTo & self.LogTo.FILE: self.pluginLog.critical(f"{LN_Str} {levelStr}{logMsg}")
+                if printTo & self.LogTo.STASH: self.log.error(f"{LN_Str} {levelStr}{logMsg}")
+            if (printTo & self.LogTo.CONSOLE) and (logLevel != logging.DEBUG or self.DEBUG_TRACING or logAlways):
+                print(f"{LN_Str} {levelStr}{logMsg}")
+            if (printTo & self.LogTo.STDERR) and (logLevel != logging.DEBUG or self.DEBUG_TRACING or logAlways):
+                print(f"StdErr: {LN_Str} {levelStr}{logMsg}", file=sys.stderr)
+        except Exception as e:
+            if printLogException:
+                tb = traceback.format_exc()
+                print(f"Exception calling [Log]; Error: {e}\nTraceBack={tb}")
+            pass
+    
+    def Trace(self, logMsg = "", printTo = 0, logAlways = False, lineNo = -1, toAscii = None):
+        if printTo == 0: printTo = self.LogTo.FILE
         if lineNo == -1:
             lineNo = inspect.currentframe().f_back.f_lineno
-        LN_Str = f"[LN:{lineNo}]"
-        # print(f"{LN_Str}, {logAlways}, {self.LOG_LEVEL}, {logging.DEBUG}, {levelStr}, {logMsg}")
-        if logLevel == logging.DEBUG and (logAlways == False or self.LOG_LEVEL == logging.DEBUG):
-            if levelStr == "": levelStr = self.LEV_DBG
-            if printTo & self.LOG_TO_FILE: self.pluginLog.debug(f"{LN_Str} {levelStr}{logMsg}")
-            if printTo & self.LOG_TO_STASH: self.log.debug(f"{LN_Str} {levelStr}{logMsg}")
-        elif logLevel == logging.INFO or logLevel == logging.DEBUG:
-            if levelStr == "": levelStr = self.LEV_INF if logLevel == logging.INFO else self.LEV_DBG
-            if printTo & self.LOG_TO_FILE: self.pluginLog.info(f"{LN_Str} {levelStr}{logMsg}")
-            if printTo & self.LOG_TO_STASH: self.log.info(f"{LN_Str} {levelStr}{logMsg}")
-        elif logLevel == logging.WARN:
-            if levelStr == "": levelStr = self.LEV_WRN
-            if printTo & self.LOG_TO_FILE: self.pluginLog.warning(f"{LN_Str} {levelStr}{logMsg}")
-            if printTo & self.LOG_TO_STASH: self.log.warning(f"{LN_Str} {levelStr}{logMsg}")
-        elif logLevel == logging.ERROR:
-            if levelStr == "": levelStr = self.LEV_ERR
-            if printTo & self.LOG_TO_FILE: self.pluginLog.error(f"{LN_Str} {levelStr}{logMsg}")
-            if printTo & self.LOG_TO_STASH: self.log.error(f"{LN_Str} {levelStr}{logMsg}")
-        elif logLevel == logging.CRITICAL:
-            if levelStr == "": levelStr = self.LEV_CRITICAL
-            if printTo & self.LOG_TO_FILE: self.pluginLog.critical(f"{LN_Str} {levelStr}{logMsg}")
-            if printTo & self.LOG_TO_STASH: self.log.error(f"{LN_Str} {levelStr}{logMsg}")
-        if (printTo & self.LOG_TO_CONSOLE) and (logLevel != logging.DEBUG or self.DEBUG_TRACING or logAlways):
-            print(f"{LN_Str} {levelStr}{logMsg}")
-        if (printTo & self.LOG_TO_STDERR) and (logLevel != logging.DEBUG or self.DEBUG_TRACING or logAlways):
-            print(f"StdErr: {LN_Str} {levelStr}{logMsg}", file=sys.stderr)
+        logLev = logging.INFO if logAlways else logging.TRACE
+        if self.DBG_LEVEL == self.DbgLevel.TRACE or logAlways:
+            if logMsg == "":
+                logMsg = f"Line number {lineNo}..."
+            self.Log(logMsg, printTo, logLev, lineNo, self.Level.TRACE, logAlways, toAscii=toAscii)
     
-    def Trace(self, logMsg = "", printTo = 0, logAlways = False, lineNo = -1, toAscii = None):
-        if printTo == 0: printTo = self.LOG_TO_FILE
+    # Log once per session. Only logs the first time called from a particular line number in the code.
+    def TraceOnce(self, logMsg = "", printTo = 0, logAlways = False, toAscii = None):
+        lineNo = inspect.currentframe().f_back.f_lineno
+        if self.DBG_LEVEL == self.DbgLevel.TRACE or logAlways:
+            FuncAndLineNo = f"{inspect.currentframe().f_back.f_code.co_name}:{lineNo}"
+            if FuncAndLineNo in self.logLinePreviousHits:
+                return
+            self.logLinePreviousHits.append(FuncAndLineNo)
+            self.Trace(logMsg, printTo, logAlways, lineNo, toAscii=toAscii)
+    
+    def Debug(self, logMsg = "", printTo = 0, logAlways = False, lineNo = -1, toAscii = None):
+        if printTo == 0: printTo = self.LogTo.FILE
         if lineNo == -1:
             lineNo = inspect.currentframe().f_back.f_lineno
         logLev = logging.INFO if logAlways else logging.DEBUG
         if self.DEBUG_TRACING or logAlways:
             if logMsg == "":
                 logMsg = f"Line number {lineNo}..."
-            self.Log(logMsg, printTo, logLev, lineNo, self.LEV_TRACE, logAlways, toAscii=toAscii)
+            self.Log(logMsg, printTo, logLev, lineNo, self.Level.DBG, logAlways, toAscii=toAscii)
     
     # Log once per session. Only logs the first time called from a particular line number in the code.
-    def TraceOnce(self, logMsg = "", printTo = 0, logAlways = False, toAscii = None):
+    def DebugOnce(self, logMsg = "", printTo = 0, logAlways = False, toAscii = None):
         lineNo = inspect.currentframe().f_back.f_lineno
         if self.DEBUG_TRACING or logAlways:
             FuncAndLineNo = f"{inspect.currentframe().f_back.f_code.co_name}:{lineNo}"
@@ -279,8 +392,8 @@ def TraceOnce(self, logMsg = "", printTo = 0, logAlways = False, toAscii = None)
             self.Trace(logMsg, printTo, logAlways, lineNo, toAscii=toAscii)
 
     # Log INFO on first call, then do Trace on remaining calls.
-    def LogOnce(self, logMsg = "", printTo = 0, logAlways = False, traceOnRemainingCalls = True, toAscii = None):
-        if printTo == 0: printTo = self.LOG_TO_FILE
+    def LogOnce(self, logMsg = "", printTo = 0, logAlways = False, traceOnRemainingCalls = True, toAscii = None, printLogException = False):
+        if printTo == 0: printTo = self.LogTo.FILE
         lineNo = inspect.currentframe().f_back.f_lineno
         FuncAndLineNo = f"{inspect.currentframe().f_back.f_code.co_name}:{lineNo}"
         if FuncAndLineNo in self.logLinePreviousHits:
@@ -288,49 +401,97 @@ def LogOnce(self, logMsg = "", printTo = 0, logAlways = False, traceOnRemainingC
                 self.Trace(logMsg, printTo, logAlways, lineNo, toAscii=toAscii) 
         else:
             self.logLinePreviousHits.append(FuncAndLineNo)
-            self.Log(logMsg, printTo, logging.INFO, lineNo, toAscii=toAscii)   
+            self.Log(logMsg, printTo, logging.INFO, lineNo, toAscii=toAscii, printLogException=printLogException)   
     
-    def Warn(self, logMsg, printTo = 0, toAscii = None):
+    def Warn(self, logMsg, printTo = 0, toAscii = None, printLogException = False):
         if printTo == 0: printTo = self.log_to_wrn_set
         lineNo = inspect.currentframe().f_back.f_lineno
-        self.Log(logMsg, printTo, logging.WARN, lineNo, toAscii=toAscii)
+        self.Log(logMsg, printTo, logging.WARN, lineNo, toAscii=toAscii, printLogException=printLogException)
     
-    def Error(self, logMsg, printTo = 0, toAscii = None):
+    def Error(self, logMsg, printTo = 0, toAscii = None, printLogException = False):
         if printTo == 0: printTo = self.log_to_err_set
         lineNo = inspect.currentframe().f_back.f_lineno
-        self.Log(logMsg, printTo, logging.ERROR, lineNo, toAscii=toAscii)
+        self.Log(logMsg, printTo, logging.ERROR, lineNo, toAscii=toAscii, printLogException=printLogException)
     
-    def Status(self, printTo = 0, logLevel = logging.INFO, lineNo = -1):
+    # Above logging functions all use UpperCamelCase naming convention to avoid conflict with parent class logging function names.
+    # The below non-loggging functions use (lower) camelCase naming convention.
+    def status(self, printTo = 0, logLevel = logging.INFO, lineNo = -1):
         if printTo == 0: printTo = self.log_to_norm
         if lineNo == -1:
             lineNo = inspect.currentframe().f_back.f_lineno
         self.Log(f"StashPluginHelper Status: (CALLED_AS_STASH_PLUGIN={self.CALLED_AS_STASH_PLUGIN}), (RUNNING_IN_COMMAND_LINE_MODE={self.RUNNING_IN_COMMAND_LINE_MODE}), (DEBUG_TRACING={self.DEBUG_TRACING}), (DRY_RUN={self.DRY_RUN}), (PLUGIN_ID={self.PLUGIN_ID}), (PLUGIN_TASK_NAME={self.PLUGIN_TASK_NAME}), (STASH_URL={self.STASH_URL}), (MAIN_SCRIPT_NAME={self.MAIN_SCRIPT_NAME})",
             printTo, logLevel, lineNo)
     
-    def ExecuteProcess(self, args, ExecDetach=False):
-        import platform, subprocess
-        is_windows = any(platform.win32_ver())
+    # Replaces obsolete UI settings variable with new name. Only use this with strings and numbers.
+    # Example usage:
+    #       obsoleteSettingsToConvert = {"OldVariableName" : "NewVariableName", "AnotherOldVarName" : "NewName2"}
+    #       stash.replaceObsoleteSettings(obsoleteSettingsToConvert, "ObsoleteSettingsCheckVer2")
+    def replaceObsoleteSettings(self, settingSet:dict, SettingToCheckFirst="", init_defaults=False):
+        if SettingToCheckFirst == "" or self.Setting(SettingToCheckFirst) == False:
+            for key in settingSet:
+                obsoleteVar = self.Setting(key)
+                if isinstance(obsoleteVar, bool):
+                    if obsoleteVar:
+                        if self.Setting(settingSet[key]) == False:
+                            self.Log(f"Detected obsolete (bool) settings ({key}). Moving obsolete settings to new setting name {settingSet[key]}.")
+                            results = self.configure_plugin(self.PLUGIN_ID, {settingSet[key]:self.Setting(key), key : False}, init_defaults)
+                            self.Debug(f"configure_plugin = {results}")
+                        else:
+                            self.Log(f"Detected obsolete (bool) settings ({key}), and deleting it's content because new setting name ({settingSet[key]}) is already populated.")
+                            results = self.configure_plugin(self.PLUGIN_ID, {key : False}, init_defaults)
+                            self.Debug(f"configure_plugin = {results}")                
+                elif isinstance(obsoleteVar, int): # Both int and bool type returns true here
+                    if obsoleteVar > 0:
+                        if self.Setting(settingSet[key]) > 0:
+                            self.Log(f"Detected obsolete (int) settings ({key}), and deleting it's content because new setting name ({settingSet[key]}) is already populated.")
+                            results = self.configure_plugin(self.PLUGIN_ID, {key : 0}, init_defaults)
+                            self.Debug(f"configure_plugin = {results}")
+                        else:
+                            self.Log(f"Detected obsolete (int) settings ({key}). Moving obsolete settings to new setting name {settingSet[key]}.")
+                            results = self.configure_plugin(self.PLUGIN_ID, {settingSet[key]:self.Setting(key), key : 0}, init_defaults)
+                            self.Debug(f"configure_plugin = {results}")
+                elif obsoleteVar != "":
+                    if self.Setting(settingSet[key]) == "":
+                        self.Log(f"Detected obsolete (str) settings ({key}). Moving obsolete settings to new setting name {settingSet[key]}.")
+                        results = self.configure_plugin(self.PLUGIN_ID, {settingSet[key]:self.Setting(key), key : ""}, init_defaults)
+                        self.Debug(f"configure_plugin = {results}")
+                    else:
+                        self.Log(f"Detected obsolete (str) settings ({key}), and deleting it's content because new setting name ({settingSet[key]}) is already populated.")
+                        results = self.configure_plugin(self.PLUGIN_ID, {key : ""}, init_defaults)
+                        self.Debug(f"configure_plugin = {results}")
+            if SettingToCheckFirst != "":
+                results = self.configure_plugin(self.PLUGIN_ID, {SettingToCheckFirst : True}, init_defaults)
+                self.Debug(f"configure_plugin = {results}")
+                        
+    
+    def executeProcess(self, args, ExecDetach=False):
         pid = None
-        self.Trace(f"is_windows={is_windows} args={args}")
-        if is_windows:
+        self.Trace(f"self.IS_WINDOWS={self.IS_WINDOWS} args={args}")
+        if self.IS_WINDOWS:
             if ExecDetach:
-                self.Trace("Executing process using Windows DETACHED_PROCESS")
+                self.Trace(f"Executing process using Windows DETACHED_PROCESS; args=({args})")
                 DETACHED_PROCESS = 0x00000008
                 pid = subprocess.Popen(args,creationflags=DETACHED_PROCESS, shell=True).pid
             else:
                 pid = subprocess.Popen(args, shell=True).pid
         else:
-            self.Trace("Executing process using normal Popen")
-            pid = subprocess.Popen(args).pid
+            if ExecDetach:
+                # For linux detached, use nohup. I.E. subprocess.Popen(["nohup", "python", "test.py"])
+                if self.IS_LINUX:
+                    args = ["nohup"] + args
+                self.Trace(f"Executing detached process using Popen({args})")
+            else:
+                self.Trace(f"Executing process using normal Popen({args})")
+            pid = subprocess.Popen(args).pid # On detach, may need the following for MAC OS subprocess.Popen(args, shell=True, start_new_session=True)
         self.Trace(f"pid={pid}")
         return pid
     
-    def ExecutePythonScript(self, args, ExecDetach=True):
+    def executePythonScript(self, args, ExecDetach=True):
         PythonExe = f"{sys.executable}"
         argsWithPython = [f"{PythonExe}"] + args
-        return self.ExecuteProcess(argsWithPython,ExecDetach=ExecDetach)
+        return self.executeProcess(argsWithPython,ExecDetach=ExecDetach)
     
-    def Submit(self, *args, **kwargs):
+    def submit(self, *args, **kwargs):
         return self.thredPool.submit(*args, **kwargs)
     
     def asc2(self, data, convertToAscii=None):
@@ -340,24 +501,282 @@ def asc2(self, data, convertToAscii=None):
         # data = str(data).encode('ascii','ignore') # This works better for logging than ascii function
         # return str(data)[2:-1] # strip out b'str'
     
-    def init_mergeMetadata(self, excludeMergeTags=None):
+    def initMergeMetadata(self, excludeMergeTags=None):
         self.excludeMergeTags = excludeMergeTags
         self._mergeMetadata = mergeMetadata(self, self.excludeMergeTags)
     
-    # Must call init_mergeMetadata, before calling merge_metadata
-    def merge_metadata(self, SrcData, DestData): # Input arguments can be scene ID or scene metadata
-        if type(SrcData) is int:
-            SrcData = self.find_scene(SrcData)
-            DestData = self.find_scene(DestData)
-        return self._mergeMetadata.merge(SrcData, DestData)
+    def mergeMetadata(self, SrcData, DestData, retryCount = 12, sleepSecondsBetweenRetry = 5, excludeMergeTags=None): # Input arguments can be scene ID or scene metadata
+        import requests
+        if self._mergeMetadata == None:
+            self.initMergeMetadata(excludeMergeTags)
+        errMsg = None
+        for i in range(0, retryCount):
+            try:
+                if errMsg != None:
+                    self.Warn(errMsg)
+                if type(SrcData) is int:
+                    SrcData = self.find_scene(SrcData)
+                    DestData = self.find_scene(DestData)
+                return self._mergeMetadata.merge(SrcData, DestData)
+            except (requests.exceptions.ConnectionError, ConnectionResetError):
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [mergeMetadata]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            except Exception as e:
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [mergeMetadata]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            time.sleep(sleepSecondsBetweenRetry)
+    
+    def getUpdateProgressBarIter(self, qtyResults):
+        if qtyResults > 40000:
+            return 100
+        if qtyResults > 20000:
+            return 80
+        if qtyResults > 10000:
+          return 40
+        if qtyResults > 5000:
+          return 20
+        if qtyResults > 2000:
+          return 10
+        if qtyResults > 1000:
+          return 5
+        if qtyResults > 500:
+          return 3
+        if qtyResults > 200:
+          return 2
+        return 1
+    
+    def enableProgressBar(self, enable=True):
+        self.progressBarIsEnabled = enable
+    
+    # Use setProgressBarIter to reduce traffic to the server by only updating the progressBar every X(updateProgressbarOnIter) iteration.
+    def setProgressBarIter(self, qtyResults):
+        if self.progressBarIsEnabled:
+            self.updateProgressbarOnIter = self.getUpdateProgressBarIter(qtyResults)
+            self.currentProgressbarIteration = 0
+    
+    def progressBar(self, currentIndex, maxCount):
+        if self.progressBarIsEnabled:
+            if self.updateProgressbarOnIter > 0:
+                self.currentProgressbarIteration+=1
+                if self.currentProgressbarIteration > self.updateProgressbarOnIter:
+                    self.currentProgressbarIteration = 0
+                else:
+                    return
+            progress = (currentIndex / maxCount) if currentIndex < maxCount else (maxCount / currentIndex)
+            try:
+                self.log.progress(progress)
+            except Exception as e:
+                pass
+    
+    def isDocker(self):
+        cgroup = pathlib.Path('/proc/self/cgroup')
+        return pathlib.Path('/.dockerenv').is_file() or cgroup.is_file() and 'docker' in cgroup.read_text()
+    
+    def isWindows(self):
+        if any(platform.win32_ver()):
+            return True
+        return False
+    
+    def isLinux(self):
+        if platform.system().lower().startswith("linux"):
+            return True
+        return False
+    
+    def isFreeBSD(self):
+        if platform.system().lower().startswith("freebsd"):
+            return True
+        return False
+    
+    def isMacOS(self):
+        if sys.platform == "darwin":
+            return True
+        return False
     
-    def Progress(self, currentIndex, maxCount):
-        progress = (currentIndex / maxCount) if currentIndex < maxCount else (maxCount / currentIndex)
-        self.log.progress(progress)
+    def isWindows(self):
+        if any(platform.win32_ver()):
+            return True
+        return False
+    
+    def spinProcessBar(self, sleepSeconds = 1, maxPos = 30, trace = False):
+        if trace:
+            self.Trace(f"Starting spinProcessBar loop; sleepSeconds={sleepSeconds}, maxPos={maxPos}")
+        pos = 1
+        while self.stopProcessBarSpin == False:
+            if trace:
+                self.Trace(f"progressBar({pos}, {maxPos})")
+            self.progressBar(pos, maxPos)
+            pos +=1
+            if pos > maxPos:
+                pos = 1
+            time.sleep(sleepSeconds)
+    
+    def startSpinningProcessBar(self, sleepSeconds = 1, maxPos = 30, trace = False):
+        self.stopProcessBarSpin = False
+        if trace:
+            self.Trace(f"submitting spinProcessBar; sleepSeconds={sleepSeconds}, maxPos={maxPos}, trace={trace}")
+        self.submit(self.spinProcessBar, sleepSeconds, maxPos, trace)
+    
+    def stopSpinningProcessBar(self, sleepSeconds = 1):
+        self.stopProcessBarSpin = True
+        time.sleep(sleepSeconds)
+    
+    def startsWithInList(self, listToCk, itemToCk):
+        itemToCk = itemToCk.lower()
+        for listItem in listToCk:
+            if itemToCk.startswith(listItem.lower()):
+                return True
+        return False
+    
+    def indexStartsWithInList(self, listToCk, itemToCk):
+        itemToCk = itemToCk.lower()
+        index = -1
+        lenItemMatch = 0
+        returnValue = self.Constant.NOT_IN_LIST.value
+        for listItem in listToCk:
+            index += 1
+            if itemToCk.startswith(listItem.lower()):
+                if len(listItem) > lenItemMatch: # Make sure the best match is selected by getting match with longest string.
+                    lenItemMatch = len(listItem)
+                    returnValue = index
+        return returnValue
+    
+    def checkIfTagInlist(self, somelist, tagName, trace=False):
+        tagId = self.find_tags(q=tagName)
+        if len(tagId) > 0 and 'id' in tagId[0]:
+            tagId = tagId[0]['id']
+        else:
+            self.Warn(f"Could not find tag ID for tag '{tagName}'.")
+            return
+        somelist = somelist.split(",")
+        if trace:
+            self.Trace("#########################################################################")
+        scenes = self.find_scenes(f={"tags": {"value":tagId, "modifier":"INCLUDES"}}, fragment='id tags {id name} files {path width height duration size video_codec bit_rate frame_rate} details')
+        qtyResults = len(scenes)
+        self.Log(f"Found {qtyResults} scenes with tag ({tagName})")
+        Qty = 0
+        for scene in scenes:
+            Qty+=1
+            if self.startsWithInList(somelist, scene['files'][0]['path']):
+                self.Log(f"Found scene part of list; {scene['files'][0]['path']}")
+            elif trace:
+                self.Trace(f"Not part of list; {scene['files'][0]['path']}")
     
-    def run_plugin(self, plugin_id, task_mode=None, args:dict={}, asyn=False):
+    def createTagId(self, tagName, tagName_descp = "", deleteIfExist = False, ignoreAutoTag = False):
+        tagId = self.find_tags(q=tagName)
+        if len(tagId):
+            tagId = tagId[0]
+            if deleteIfExist:
+                self.destroy_tag(int(tagId['id']))
+            else:
+                return tagId['id']
+        tagId = self.create_tag({"name":tagName, "description":tagName_descp, "ignore_auto_tag": ignoreAutoTag})
+        self.Log(f"Dup-tagId={tagId['id']}")
+        return tagId['id']
+    
+    def removeTag(self, scene, tagName): # scene can be scene ID or scene metadata
+        scene_details = scene
+        if isinstance(scene, int) or 'id' not in scene:
+            scene_details = self.find_scene(scene)
+        tagIds = []
+        doesHaveTagName = False
+        for tag in scene_details['tags']:
+            if tag['name'] != tagName:
+                tagIds += [tag['id']]
+            else:
+                doesHaveTagName = True
+        if doesHaveTagName:
+            dataDict = {'id' : scene_details['id']}
+            dataDict.update({'tag_ids' : tagIds})
+            self.update_scene(dataDict)
+        return doesHaveTagName
+    
+    def addTag(self, scene, tagName, tagName_descp = "", ignoreAutoTag=False, retryCount = 12, sleepSecondsBetweenRetry = 5): # scene can be scene ID or scene metadata
+        errMsg = None
+        for i in range(0, retryCount):
+            try:
+                if errMsg != None:
+                    self.Warn(errMsg)
+                scene_details = scene
+                if isinstance(scene, int) or 'id' not in scene:
+                    scene_details = self.find_scene(scene)
+                tagIds = [self.createTagId(tagName, tagName_descp=tagName_descp, ignoreAutoTag=ignoreAutoTag)]
+                for tag in scene_details['tags']:
+                    if tag['name'] == tagName:
+                        return False
+                    else:
+                        tagIds += [tag['id']]
+                dataDict = {'id' : scene_details['id']}
+                dataDict.update({'tag_ids' : tagIds})
+                self.update_scene(dataDict)
+                return True
+            except (ConnectionResetError):
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [addTag]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            except Exception as e:
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [addTag]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            time.sleep(sleepSecondsBetweenRetry)
+
+    def copyFields(self, srcData, fieldsToCpy):
+        destData = {}
+        for key in srcData:
+            if key in fieldsToCpy:
+                destData.update({key : srcData[key]})
+        return destData
+    
+    def renameTag(self,oldTagName, newTagName):
+        tagMetadata = self.find_tags(q=oldTagName)
+        if len(tagMetadata) > 0 and 'id' in tagMetadata[0]:
+            if tagMetadata[0]['name'] == newTagName:
+                return False
+            tagMetadata[0]['name'] = newTagName
+            fieldsToCpy = ["id", "name", "description", "aliases", "ignore_auto_tag", "favorite", "image", "parent_ids", "child_ids"]
+            tagUpdateInput = self.copyFields(tagMetadata[0], fieldsToCpy)
+            self.Trace(f"Renaming tag using tagUpdateInput = {tagUpdateInput}")
+            self.update_tag(tagUpdateInput)
+            return True
+        return False
+    
+    def updateScene(self, update_input, create=False, retryCount = 24, sleepSecondsBetweenRetry = 5):
+        errMsg = None
+        for i in range(0, retryCount):
+            try:
+                if errMsg != None:
+                    self.Warn(errMsg)
+                return self.update_scene(update_input, create)
+            except (ConnectionResetError):
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [updateScene]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            except Exception as e:
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [updateScene]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            time.sleep(sleepSecondsBetweenRetry)
+    
+    def destroyScene(self, scene_id, delete_file=False, retryCount = 12, sleepSecondsBetweenRetry = 5):
+        errMsg = None
+        for i in range(0, retryCount):
+            try:
+                if errMsg != None:
+                    self.Warn(errMsg)
+                if i > 0:
+                    # Check if file still exist
+                    scene = self.find_scene(scene_id)
+                    if scene == None or len(scene) == 0:
+                        self.Warn(f"Scene {scene_id} not found in Stash.")
+                        return False
+                return self.destroy_scene(scene_id, delete_file)
+            except (ConnectionResetError):
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [updateScene]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            except Exception as e:
+                tb = traceback.format_exc()
+                errMsg = f"Exception calling [updateScene]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
+            time.sleep(sleepSecondsBetweenRetry)
+    
+    def runPlugin(self, plugin_id, task_mode=None, args:dict={}, asyn=False):
         """Runs a plugin operation.
            The operation is run immediately and does not use the job queue.
+           This is a blocking call, and does not return until plugin completes.
         Args:
             plugin_id (ID):             plugin_id
             task_name (str, optional):  Plugin task to perform
@@ -375,30 +794,73 @@ def run_plugin(self, plugin_id, task_mode=None, args:dict={}, asyn=False):
             "args": args,
         }
         if asyn:
-            self.Submit(self.call_GQL, query, variables)
+            self.submit(self.call_GQL, query, variables)
             return f"Made asynchronous call for plugin {plugin_id}"
         else:
             return self.call_GQL(query, variables)
-       
-    def find_duplicate_scenes_diff(self, distance: PhashDistance=PhashDistance.EXACT, fragment='id', duration_diff: float=10.00 ):
-        query = """
-        	query FindDuplicateScenes($distance: Int, $duration_diff: Float) {
-        		findDuplicateScenes(distance: $distance, duration_diff: $duration_diff) {
-        			...SceneSlim
-        		}
-        	}
-        """
-        if fragment:
-        	query = re.sub(r'\.\.\.SceneSlim', fragment, query)
-        else:
-        	query += "fragment SceneSlim on Scene { id  }"
-        
-        variables = { "distance": distance, "duration_diff": duration_diff }
-        result = self.call_GQL(query, variables)
-        return result['findDuplicateScenes'] 
     
-    # #################################################################################################
-    # The below functions extends class StashInterface with functions which are not yet in the class
+    def stopJobs(self, startPos = 0, startsWith = ""):
+        taskQue = self.job_queue()
+        if taskQue != None:
+            count = 0
+            for jobDetails in taskQue:
+                count+=1
+                if count > startPos:
+                    if startsWith == "" or jobDetails['description'].startswith(startsWith):
+                        self.Log(f"Killing Job ID({jobDetails['id']}); description={jobDetails['description']}")
+                        self.stop_job(jobDetails['id'])
+                    else:
+                        self.Log(f"Excluding Job ID({jobDetails['id']}); description={jobDetails['description']}; {jobDetails})")
+                else:
+                    self.Log(f"Skipping Job ID({jobDetails['id']}); description={jobDetails['description']}; {jobDetails})")
+    
+    def toJson(self, data, replaceSingleQuote=False):
+        if replaceSingleQuote:
+            data = data.replace("'", '"')
+        data = data.replace("\\", "\\\\")
+        data = data.replace("\\\\\\\\", "\\\\")
+        return json.loads(data)
+    
+    def isCorrectDbVersion(self, verNumber = 68):
+        results = self.sql_query("select version from schema_migrations")
+        # self.Log(results)
+        if len(results['rows']) == 0 or len(results['rows'][0]) == 0:
+            return False
+        return int(results['rows'][0][0]) == verNumber
+    
+    def renameFileNameInDB(self, fileId, oldName, newName, UpdateUsingIdOnly = False):
+        if self.isCorrectDbVersion():
+            query = f'update files set basename = "{newName}" where basename = "{oldName}" and id = {fileId};'
+            if UpdateUsingIdOnly:
+                query = f'update files set basename = "{newName}" where id = {fileId};'
+            self.Trace(f"Executing query ({query})")
+            results = self.sql_commit(query)
+            if 'rows_affected' in results and results['rows_affected'] == 1:
+                return True
+        return False
+    
+    def getFileNameFromDB(self, id):
+        results = self.sql_query(f'select basename from files where id = {id};')
+        self.Trace(f"results = ({results})")
+        if len(results['rows']) == 0 or len(results['rows'][0]) == 0:
+            return None
+        return results['rows'][0][0]
+    
+    # ############################################################################################################
+    # Functions which are candidates to be added to parent class use snake_case naming convention.
+    # ############################################################################################################
+    # The below functions extends class StashInterface with functions which are not yet in the class or
+    # fixes for functions which have not yet made it into official class.
+    def metadata_scan(self, paths:list=[], flags={}): # ToDo: Add option to add path to library if path not included when calling metadata_scan
+        query = "mutation MetadataScan($input:ScanMetadataInput!) { metadataScan(input: $input) }"
+        scan_metadata_input = {"paths": paths}
+        if flags:
+            scan_metadata_input.update(flags)
+        elif scan_config := self.get_configuration_defaults("scan { ...ScanMetadataOptions }").get("scan"):
+            scan_metadata_input.update(scan_config)
+        result = self.call_GQL(query, {"input": scan_metadata_input})
+        return result["metadataScan"]
+        
     def get_all_scenes(self):
         query_all_scenes = """
             query AllScenes {
@@ -451,6 +913,43 @@ def metadata_clean_generated(self, blobFiles=True, dryRun=False, imageThumbnails
     
     def rename_generated_files(self):
         return self.call_GQL("mutation MigrateHashNaming {migrateHashNaming}")
+       
+    def find_duplicate_scenes_diff(self, distance: PhashDistance=PhashDistance.EXACT, fragment='id', duration_diff: float=10.00 ):
+        query = """
+        	query FindDuplicateScenes($distance: Int, $duration_diff: Float) {
+        		findDuplicateScenes(distance: $distance, duration_diff: $duration_diff) {
+        			...SceneSlim
+        		}
+        	}
+        """
+        if fragment:
+        	query = re.sub(r'\.\.\.SceneSlim', fragment, query)
+        else:
+        	query += "fragment SceneSlim on Scene { id  }"
+        
+        variables = { "distance": distance, "duration_diff": duration_diff }
+        result = self.call_GQL(query, variables)
+        return result['findDuplicateScenes'] 
+    
+    # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    # Direct SQL associated functions
+    def get_file_metadata(self, data, raw_data = False): # data is either file ID or scene metadata
+        results = None
+        if data == None:
+            return results
+        if 'files' in data and len(data['files']) > 0 and 'id' in data['files'][0]:
+            results = self.sql_query(f"select * from files where id =  {data['files'][0]['id']}")
+        else:
+            results = self.sql_query(f"select * from files where id =  {data}")
+        if raw_data:
+            return results
+        if 'rows' in results:
+            return results['rows'][0]
+        self.Error(f"Unknown error while SQL query with data='{data}'; Results='{results}'.")
+        return None
+    
+    def set_file_basename(self, id, basename):
+        return self.sql_commit(f"update files set basename = '{basename}' where id = {id}")
 
 class mergeMetadata: # A class to merge scene metadata from source scene to destination scene
     srcData = None
@@ -471,7 +970,8 @@ def merge(self, SrcData, DestData):
         self.mergeItems('tags', 'tag_ids', [], excludeName=self.excludeMergeTags)
         self.mergeItems('performers', 'performer_ids', [])
         self.mergeItems('galleries', 'gallery_ids', [])
-        self.mergeItems('movies', 'movies', [])
+        # Looks like movies has been removed from new Stash version
+        # self.mergeItems('movies', 'movies', [])
         self.mergeItems('urls', listToAdd=self.destData['urls'], NotStartWith=self.stash.STASH_URL)
         self.mergeItem('studio', 'studio_id', 'id')
         self.mergeItem('title')
@@ -524,3 +1024,54 @@ def mergeItems(self, fieldName, updateFieldName=None, listToAdd=[], NotStartWith
                         listToAdd += [item['id']]
             self.dataDict.update({ updateFieldName : listToAdd})
             # self.stash.Trace(f"Added {fieldName} ({dataAdded}) to scene ID({self.destData['id']})", toAscii=True)
+
+class taskQueue:
+    taskqueue = None
+    def __init__(self, taskqueue):
+        self.taskqueue = taskqueue
+    
+    def tooManyScanOnTaskQueue(self, tooManyQty = 5):
+        count = 0
+        if self.taskqueue == None:
+            return False
+        for jobDetails in self.taskqueue:
+            if jobDetails['description'] == "Scanning...":
+                count += 1
+        if count < tooManyQty:
+            return False
+        return True
+    
+    def cleanJobOnTaskQueue(self):
+        for jobDetails in self.taskqueue:
+            if jobDetails['description'] == "Cleaning...":
+                return True
+        return False
+    
+    def cleanGeneratedJobOnTaskQueue(self):
+        for jobDetails in self.taskqueue:
+            if jobDetails['description'] == "Cleaning generated files...":
+                return True
+        return False
+    
+    def isRunningPluginTaskJobOnTaskQueue(self, taskName):
+        for jobDetails in self.taskqueue:
+            if jobDetails['description'] == "Running plugin task: {taskName}":
+                return True
+        return False
+    
+    def tagDuplicatesJobOnTaskQueue(self):
+        return self.isRunningPluginTaskJobOnTaskQueue("Tag Duplicates")
+    
+    def clearDupTagsJobOnTaskQueue(self):
+        return self.isRunningPluginTaskJobOnTaskQueue("Clear Tags")
+    
+    def generatePhashMatchingJobOnTaskQueue(self):
+        return self.isRunningPluginTaskJobOnTaskQueue("Generate PHASH Matching")
+    
+    def deleteDuplicatesJobOnTaskQueue(self):
+        return self.isRunningPluginTaskJobOnTaskQueue("Delete Duplicates")
+    
+    def deleteTaggedScenesJobOnTaskQueue(self):
+        return self.isRunningPluginTaskJobOnTaskQueue("Delete Tagged Scenes")
+
+
diff --git a/plugins/DupFileManager/advance_options.html b/plugins/DupFileManager/advance_options.html
new file mode 100644
index 00000000..1f5e5135
--- /dev/null
+++ b/plugins/DupFileManager/advance_options.html
@@ -0,0 +1,2708 @@
+<!doctype html>
+<html>
+  <head>
+    <title>DupFileManager Advance Menus</title>
+    <style>
+      h2 {
+        text-align: center;
+      }
+      table,
+      th,
+      td {
+        border: 1px solid black;
+      }
+      .inline {
+        display: inline;
+      }
+      .scene-details {
+        text-align: center;
+        font-size: small;
+      }
+      .reason-details {
+        text-align: left;
+        font-size: small;
+      }
+      .link-items {
+        text-align: center;
+        font-size: small;
+      }
+      .link-button {
+        background: none;
+        border: none;
+        color: blue;
+        text-decoration: underline;
+        cursor: pointer;
+        font-size: 1em;
+        font-family: serif;
+        text-align: center;
+        font-size: small;
+      }
+      .link-button:focus {
+        outline: none;
+      }
+      .link-button:active {
+        color: red;
+      }
+      html.wait,
+      html.wait * {
+        cursor: wait !important;
+      }
+    </style>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
+    <script>
+      var GqlFromParam = false;
+      const queryString = window.location.search;
+      const urlParams = new URLSearchParams(queryString);
+      console.log(urlParams);
+      var GraphQl_URL = "http://localhost:9999/graphql";
+      if (urlParams.get("GQL") != null && urlParams.get("GQL") === "")
+        GraphQl_URL = urlParams.get("GQL");
+      GqlFromParam = true;
+      console.log("GQL = " + GraphQl_URL);
+
+      function RunPluginDupFileManager(Mode, Param = 0, Async = false) {
+        $("html").addClass("wait");
+        $("body").css("cursor", "progress");
+        console.log(
+          "GraphQl_URL = " +
+            GraphQl_URL +
+            "; Mode = " +
+            Mode +
+            "; Param = " +
+            Param
+        );
+        $.ajax({
+          method: "POST",
+          url: GraphQl_URL,
+          contentType: "application/json",
+          dataType: "text",
+          cache: Async,
+          async: Async,
+          data: JSON.stringify({
+            query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
+            variables: {
+              plugin_id: "DupFileManager",
+              args: { Target: Param, mode: Mode },
+            },
+          }),
+          success: function (result) {
+            console.log(result);
+            $("html").removeClass("wait");
+            $("body").css("cursor", "default");
+          },
+        });
+        console.log("Setting default cursor");
+      }
+      $(document).ready(function () {
+        $("button").click(function () {
+          const AddedWarn =
+            "?  This will delete the files, and remove them from stash.";
+          console.log(this.id);
+          var blackliststr = "";
+          var comparestr = "less than ";
+          if (this.id.includes("Blacklist")) blackliststr = "in blacklist ";
+          if (this.id.includes("Greater")) comparestr = "greater than ";
+          else if (this.id.includes("Eq")) comparestr = "equal to ";
+
+          if (this.id === "tag_duplicates_task") {
+            RunPluginDupFileManager(this.id, this.value, true);
+          } else if (this.id.startsWith("tag_duplicates_task")) {
+            RunPluginDupFileManager(
+              "tag_duplicates_task",
+              this.value + ":" + $("#significantTimeDiff").val(),
+              true
+            );
+          } else if (this.id.startsWith("create_duplicate_report_task")) {
+            RunPluginDupFileManager(
+              "create_duplicate_report_task",
+              this.value + ":" + $("#significantTimeDiff").val(),
+              true
+            );
+          } else if (this.id === "viewreport") {
+            var reportUrl = window.location.href;
+            reportUrl = reportUrl.replace(
+              "advance_options.html",
+              "report/DuplicateTagScenes.html"
+            );
+            console.log("reportUrl = " + reportUrl);
+            window.open(reportUrl, "_blank");
+          } else if (
+            this.id === "pathToDelete" ||
+            this.id === "pathToDeleteBlacklist"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete tag scenes " +
+                  blackliststr +
+                  "having _DuplicateMarkForDeletion tags, and in path " +
+                  $("#pathToDeleteText").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#pathToDeleteText").val());
+          } else if (
+            this.id === "sizeToDeleteLess" ||
+            this.id === "sizeToDeleteGreater" ||
+            this.id === "sizeToDeleteBlacklistLess" ||
+            this.id === "sizeToDeleteBlacklistGreater"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete duplicate tag scenes " +
+                  blackliststr +
+                  "having file size " +
+                  comparestr +
+                  $("#sizeToDelete").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#sizeToDelete").val());
+          } else if (
+            this.id === "durationToDeleteLess" ||
+            this.id === "durationToDeleteGreater" ||
+            this.id === "durationToDeleteBlacklistLess" ||
+            this.id === "durationToDeleteBlacklistGreater"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete duplicate tag scenes " +
+                  blackliststr +
+                  "having file duration " +
+                  comparestr +
+                  $("#durationToDelete").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#durationToDelete").val());
+          } else if (
+            this.id === "commonResToDeleteLess" ||
+            this.id === "commonResToDeleteEq" ||
+            this.id === "commonResToDeleteGreater" ||
+            this.id === "commonResToDeleteBlacklistLess" ||
+            this.id === "commonResToDeleteBlacklistEq" ||
+            this.id === "commonResToDeleteBlacklistGreater"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete duplicate tag scenes " +
+                  blackliststr +
+                  "having resolution  " +
+                  comparestr +
+                  $("#commonResToDelete").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#commonResToDelete").val());
+          } else if (
+            this.id === "resolutionToDeleteLess" ||
+            this.id === "resolutionToDeleteEq" ||
+            this.id === "resolutionToDeleteGreater" ||
+            this.id === "resolutionToDeleteBlacklistLess" ||
+            this.id === "resolutionToDeleteBlacklistEq" ||
+            this.id === "resolutionToDeleteBlacklistGreater"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete duplicate tag scenes " +
+                  blackliststr +
+                  "having resolution  " +
+                  comparestr +
+                  $("#resolutionToDelete").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#resolutionToDelete").val());
+          } else if (
+            this.id === "ratingToDeleteLess" ||
+            this.id === "ratingToDeleteEq" ||
+            this.id === "ratingToDeleteGreater" ||
+            this.id === "ratingToDeleteBlacklistLess" ||
+            this.id === "ratingToDeleteBlacklistEq" ||
+            this.id === "ratingToDeleteBlacklistGreater"
+          ) {
+            let result = 0;
+            if ($("#ratingToDelete").val() == 1 && comparestr === "less than ")
+              result = confirm(
+                "Are you sure you want to delete duplicate tag scenes " +
+                  blackliststr +
+                  "having no rating" +
+                  AddedWarn
+              );
+            else if (
+              $("#ratingToDelete").val() == 5 &&
+              comparestr === "greater than "
+            )
+              alert(
+                "Invalid selection. There are no scenes with rating greater than 5."
+              );
+            else
+              result = confirm(
+                "Are you sure you want to delete duplicate tag scenes " +
+                  blackliststr +
+                  "having rating  " +
+                  comparestr +
+                  $("#ratingToDelete").val() +
+                  AddedWarn
+              );
+            if (result)
+              RunPluginDupFileManager(this.id, $("#ratingToDelete").val());
+          } else if (
+            this.id === "tagToDelete" ||
+            this.id === "tagToDeleteBlacklist"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete tag scenes " +
+                  blackliststr +
+                  "having _DuplicateMarkForDeletion tags, and having tag " +
+                  $("#tagToDeleteText").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#tagToDeleteText").val());
+          } else if (
+            this.id === "titleToDelete" ||
+            this.id === "titleToDeleteBlacklist"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete tag scenes " +
+                  blackliststr +
+                  "having _DuplicateMarkForDeletion tags, and having title containing " +
+                  $("#titleToDeleteText").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#titleToDeleteText").val());
+          } else if (
+            this.id === "pathStrToDelete" ||
+            this.id === "pathStrToDeleteBlacklist"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete tag scenes " +
+                  blackliststr +
+                  "having _DuplicateMarkForDeletion tags, and having path containing " +
+                  $("#pathStrToDeleteText").val() +
+                  AddedWarn
+              )
+            )
+              RunPluginDupFileManager(this.id, $("#pathStrToDeleteText").val());
+          } else if (
+            this.id === "fileNotExistToDelete" ||
+            this.id === "fileNotExistToDeleteBlacklist"
+          ) {
+            if (
+              confirm(
+                "Are you sure you want to delete tag scenes " +
+                  blackliststr +
+                  "having _DuplicateMarkForDeletion tags, and that do NOT exist in the file system?"
+              )
+            )
+              RunPluginDupFileManager(this.id, true);
+          } else if (
+            this.id === "applyCombo" ||
+            this.id === "applyComboBlacklist"
+          ) {
+            var Blacklist = "";
+            if (this.id === "applyComboBlacklist") Blacklist = "Blacklist";
+            var Param = "{";
+            if ($("#InPathCheck").prop("checked"))
+              Param +=
+                '"' +
+                "pathToDelete" +
+                Blacklist +
+                '":"' +
+                $("#pathToDeleteText").val().replace("\\", "\\\\") +
+                '", ';
+            if ($("#sizeToDeleteCombobox").val() !== "")
+              Param +=
+                '"' +
+                "sizeToDelete" +
+                Blacklist +
+                $("#sizeToDeleteCombobox").val() +
+                '":"' +
+                $("#sizeToDelete").val() +
+                '", ';
+            if ($("#durationToDeleteCombobox").val() !== "")
+              Param +=
+                '"' +
+                "durationToDelete" +
+                Blacklist +
+                $("#durationToDeleteCombobox").val() +
+                '":"' +
+                $("#durationToDelete").val() +
+                '", ';
+            if ($("#commonResToDeleteCombobox").val() !== "")
+              Param +=
+                '"' +
+                "commonResToDelete" +
+                Blacklist +
+                $("#commonResToDeleteCombobox").val() +
+                '":"' +
+                $("#commonResToDelete").val() +
+                '", ';
+            if ($("#resolutionToDeleteCombobox").val() !== "") {
+              if ($("#commonResToDeleteCombobox").val() !== "") {
+                alert(
+                  "Error: Can not select both [Common Resolution] and [Other Resolution] at the same time."
+                );
+                return;
+              }
+              Param +=
+                '"' +
+                "resolutionToDelete" +
+                Blacklist +
+                $("#resolutionToDeleteCombobox").val() +
+                '":"' +
+                $("#resolutionToDelete").val() +
+                '", ';
+            }
+            if ($("#ratingToDeleteCombobox").val() !== "")
+              Param +=
+                '"' +
+                "ratingToDelete" +
+                Blacklist +
+                $("#ratingToDeleteCombobox").val() +
+                '":"' +
+                $("#ratingToDelete").val() +
+                '", ';
+            if ($("#containTagCheck").prop("checked"))
+              Param +=
+                '"' +
+                "tagToDelete" +
+                Blacklist +
+                '":"' +
+                $("#tagToDeleteText").val() +
+                '", ';
+            if ($("#containTitleCheck").prop("checked"))
+              Param +=
+                '"' +
+                "titleToDelete" +
+                Blacklist +
+                '":"' +
+                $("#titleToDeleteText").val() +
+                '", ';
+            if ($("#containStrInPathCheck").prop("checked"))
+              Param +=
+                '"' +
+                "pathStrToDelete" +
+                Blacklist +
+                '":"' +
+                $("#pathStrToDeleteText").val().replace("\\", "\\\\") +
+                '", ';
+            if ($("#fileNotExistCheck").prop("checked"))
+              Param += '"' + "fileNotExistToDelete" + Blacklist + '":"true", ';
+            Param += "}";
+            Param = Param.replace(", }", "}");
+            if (Param === "{}") {
+              alert("Error: Must select one or more options.");
+              return;
+            }
+            console.log(Param);
+            if (
+              confirm(
+                "Are you sure you want to delete tag scenes " +
+                  blackliststr +
+                  "having _DuplicateMarkForDeletion tags, and having the selected options" +
+                  AddedWarn +
+                  "\nSelected options:\n" +
+                  Param
+              )
+            )
+              RunPluginDupFileManager(this.id, Param);
+          }
+        });
+      });
+      function DeleteDupInPath() {
+        alert("Something went wrong!!!");
+      }
+    </script>
+  </head>
+  <body>
+    <center>
+      <table style="color: darkgreen; background-color: powderblue">
+        <tr>
+          <th>
+            DupFileManager Advance
+            <b style="color: red">_DuplicateMarkForDeletion_?</b> Tagged Files
+            Menu
+          </th>
+          <th>Apply Multiple Options</th>
+        </tr>
+        <tr>
+          <td>
+            <center>
+              <button
+                type="button"
+                id="tag_duplicates_task"
+                value="-1"
+                title="Create new report which tags duplicates with tag name _DuplicateMarkForDeletion using user settings for [Match Duplicate Distance]."
+              >
+                Create Duplicate Report with Tagging
+              </button>
+              <button
+                type="button"
+                id="viewreport"
+                title="View duplicate file report."
+              >
+                View Dup Report
+              </button>
+            </center>
+          </td>
+          <td>
+            <button
+              type="button"
+              id="applyCombo"
+              title="Apply selected multiple options to delete scenes."
+            >
+              Delete
+            </button>
+            <button
+              type="button"
+              id="applyComboBlacklist"
+              title="Apply selected multiple options to delete scenes in blacklist."
+            >
+              Delete-Blacklist
+            </button>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="pathToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="pathToDeleteText">Path:</label>
+              <input
+                type="text"
+                id="pathToDeleteText"
+                name="pathToDeleteText"
+                value="C:\Downloads"
+              />
+              <button
+                type="button"
+                id="pathToDelete"
+                title="Delete tagged duplicates having file path"
+              >
+                Delete Dup in Path
+              </button>
+              <button
+                type="button"
+                id="pathToDeleteBlacklist"
+                title="Delete blacklist tagged duplicates having file path"
+              >
+                Del Blacklist Dup in Path
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="InPathCheck">In-Path:</label
+            ><input
+              type="checkbox"
+              id="InPathCheck"
+              name="InPathCheck"
+              value="true"
+            />
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="sizeToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="sizeToDelete" title="File size in bytes."
+                >File Size (bytes):</label
+              >
+              <input
+                type="number"
+                id="sizeToDelete"
+                name="sizeToDelete"
+                title="File size in bytes."
+                value="123456"
+              />
+              <label for="sizeToDeleteLess">All:</label>
+              <button
+                type="button"
+                id="sizeToDeleteLess"
+                title="Delete tagged duplicates with file size less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="sizeToDeleteGreater"
+                title="Delete tagged duplicates with file size greater than"
+              >
+                >
+              </button>
+              <label for="sizeToDeleteBlacklistLess">Blacklist:</label>
+              <button
+                type="button"
+                id="sizeToDeleteBlacklistLess"
+                title="Delete blacklist tagged duplicates with file size less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="sizeToDeleteBlacklistGreater"
+                title="Delete blacklist tagged duplicates with file size greater than"
+              >
+                >
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="sizeToDeleteCombobox">Size:</label>
+            <select id="sizeToDeleteCombobox" name="sizeToDeleteCombobox">
+              <option value="" selected="selected"></option>
+              <option value="Less">Less</option>
+              <option value="Greater">Greater</option>
+            </select>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="durationToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label
+                for="durationToDelete"
+                title="Scene duration (time) in seconds."
+                >Duration (seconds):</label
+              >
+              <input
+                type="number"
+                min="1"
+                max="14400"
+                id="durationToDelete"
+                name="durationToDelete"
+                title="Duration in seconds."
+                value="60"
+              />
+              <label for="durationToDeleteLess">All:</label>
+              <button
+                type="button"
+                id="durationToDeleteLess"
+                title="Delete tagged duplicates with duration less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="durationToDeleteGreater"
+                title="Delete tagged duplicates with duration greater than"
+              >
+                >
+              </button>
+              <label for="durationToDeleteBlacklistLess">Blacklist:</label>
+              <button
+                type="button"
+                id="durationToDeleteBlacklistLess"
+                title="Delete blacklist tagged duplicates with duration less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="durationToDeleteBlacklistGreater"
+                title="Delete blacklist tagged duplicates with duration greater than"
+              >
+                >
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="durationToDeleteCombobox">Duration:</label>
+            <select
+              id="durationToDeleteCombobox"
+              name="durationToDeleteCombobox"
+            >
+              <option value="" selected="selected"></option>
+              <option value="Less">Less</option>
+              <option value="Greater">Greater</option>
+            </select>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="commonResToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="commonResToDelete" title="Scene commonRes."
+                >Common Resolution:</label
+              >
+              <select id="commonResToDelete" name="commonResToDelete">
+                <option value="76800">320x240=76800</option>
+                <option value="150528">448x336=150528</option>
+                <option value="172800">480x360=172800</option>
+                <option value="221952">544x408=221952</option>
+                <option value="230464">554x416=230464</option>
+                <option value="230400">640x360=230400</option>
+                <option value="307200">640x480=307200</option>
+                <option value="292320">720x406=292320</option>
+                <option value="345600">720x480=345600</option>
+                <option value="331776">768x432=331776</option>
+                <option value="408960">852x480=408960</option>
+                <option value="409920">854x480=409920</option>
+                <option value="518400">960x540=518400</option>
+                <option value="921600">1280x720=921600</option>
+                <option value="2073600">1920x1080=2073600</option>
+              </select>
+              <label for="commonResToDeleteLess">All:</label>
+              <button
+                type="button"
+                id="commonResToDeleteLess"
+                title="Delete tagged duplicates with resolution less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="commonResToDeleteEq"
+                title="Delete tagged duplicates with resolution equal to"
+              >
+                =
+              </button>
+              <button
+                type="button"
+                id="commonResToDeleteGreater"
+                title="Delete tagged duplicates with resolution greater than"
+              >
+                >
+              </button>
+              <label for="commonResToDeleteBlacklistLess">Blacklist:</label>
+              <button
+                type="button"
+                id="commonResToDeleteBlacklistLess"
+                title="Delete blacklist tagged duplicates with resolution less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="commonResToDeleteBlacklistEq"
+                title="Delete blacklist tagged duplicates with resolution equal to"
+              >
+                =
+              </button>
+              <button
+                type="button"
+                id="commonResToDeleteBlacklistGreater"
+                title="Delete blacklist tagged duplicates with resolution greater than"
+              >
+                >
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="commonResToDeleteCombobox">Resolution:</label>
+            <select
+              id="commonResToDeleteCombobox"
+              name="commonResToDeleteCombobox"
+            >
+              <option value="" selected="selected"></option>
+              <option value="Less">Less</option>
+              <option value="Eq">Equal</option>
+              <option value="Greater">Greater</option>
+            </select>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="tagToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="tagToDelete" title="Scene with tag.">Tag:</label>
+              <input
+                type="text"
+                id="tagToDeleteText"
+                name="tagToDelete"
+                title="tag name."
+                value="redhead"
+              />
+              <label for="tagToDelete">All:</label>
+              <button
+                type="button"
+                id="tagToDelete"
+                title="Delete tagged duplicates with tag name"
+              >
+                Contains
+              </button>
+              <label for="tagToDeleteBlacklist">Blacklist:</label>
+              <button
+                type="button"
+                id="tagToDeleteBlacklist"
+                title="Delete blacklist tagged duplicates with tag name"
+              >
+                Contains
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="containTagCheck">Contains Tag:</label
+            ><input
+              type="checkbox"
+              id="containTagCheck"
+              name="containTagCheck"
+              value="true"
+            />
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="titleToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="titleToDelete" title="Scene having value in title."
+                >Title:</label
+              >
+              <input
+                type="text"
+                id="titleToDeleteText"
+                name="titleToDelete"
+                title="String to search for in title."
+                value="mature"
+              />
+              <label for="titleToDelete">All:</label>
+              <button
+                type="button"
+                id="titleToDelete"
+                title="Delete tagged duplicates with title name including value"
+              >
+                Contains
+              </button>
+              <label for="titleToDeleteBlacklist">Blacklist:</label>
+              <button
+                type="button"
+                id="titleToDeleteBlacklist"
+                title="Delete blacklist tagged duplicates with title name including value"
+              >
+                Contains
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="containTitleCheck">Contains Title:</label
+            ><input
+              type="checkbox"
+              id="containTitleCheck"
+              name="containTitleCheck"
+              value="true"
+            />
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="pathStrToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="pathStrToDelete" pathStr="Scene having value in path."
+                >Path String:</label
+              >
+              <input
+                type="text"
+                id="pathStrToDeleteText"
+                name="pathStrToDelete"
+                pathStr="String to search for in path."
+                value="blond"
+              />
+              <label for="pathStrToDelete">All:</label>
+              <button
+                type="button"
+                id="pathStrToDelete"
+                pathStr="Delete tagged duplicates with path having value"
+              >
+                Contains
+              </button>
+              <label for="pathStrToDeleteBlacklist">Blacklist:</label>
+              <button
+                type="button"
+                id="pathStrToDeleteBlacklist"
+                pathStr="Delete blacklist tagged duplicates with path having value"
+              >
+                Contains
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="containStrInPathCheck">Text in Path:</label
+            ><input
+              type="checkbox"
+              id="containStrInPathCheck"
+              name="containStrInPathCheck"
+              value="true"
+            />
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="ratingToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="ratingToDelete" title="Scene rating.">Rating:</label>
+              <input
+                type="number"
+                min="1"
+                max="5"
+                id="ratingToDelete"
+                name="ratingToDelete"
+                title="Scene rating (1, 2, 3, 4, or 5)"
+                value="1"
+              />
+              <label for="ratingToDeleteLess">All:</label>
+              <button
+                type="button"
+                id="ratingToDeleteLess"
+                title="Delete tagged duplicates with file rating less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="ratingToDeleteEq"
+                title="Delete tagged duplicates with rating equal to"
+              >
+                =
+              </button>
+              <button
+                type="button"
+                id="ratingToDeleteGreater"
+                title="Delete tagged duplicates with file rating greater than"
+              >
+                >
+              </button>
+              <label for="ratingToDeleteBlacklistLess">Blacklist:</label>
+              <button
+                type="button"
+                id="ratingToDeleteBlacklistLess"
+                title="Delete blacklist tagged duplicates with file rating less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="ratingToDeleteBlacklistEq"
+                title="Delete blacklist tagged duplicates with rating equal to"
+              >
+                =
+              </button>
+              <button
+                type="button"
+                id="ratingToDeleteBlacklistGreater"
+                title="Delete blacklist tagged duplicates with file rating greater than"
+              >
+                >
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="ratingToDeleteCombobox">Rating:</label>
+            <select id="ratingToDeleteCombobox" name="ratingToDeleteCombobox">
+              <option value="" selected="selected"></option>
+              <option value="Less">Less</option>
+              <option value="Eq">Equal</option>
+              <option value="Greater">Greater</option>
+            </select>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <label for="fileNotExistToDelete">All:</label>
+            <button
+              type="button"
+              id="fileNotExistToDelete"
+              title="Delete tagged duplicates for which file does NOT exist."
+            >
+              Delete Files That do Not Exist
+            </button>
+            <label for="fileNotExistToDeleteBlacklist">Blacklist:</label>
+            <button
+              type="button"
+              id="fileNotExistToDeleteBlacklist"
+              title="Delete blacklist tagged duplicates for which file does NOT exist."
+            >
+              Delete Files That do Not Exist
+            </button>
+          </td>
+          <td>
+            <label for="fileNotExistCheck">File Not Exist:</label
+            ><input
+              type="checkbox"
+              id="fileNotExistCheck"
+              name="fileNotExistCheck"
+              value="true"
+            />
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <form
+              id="resolutionToDeleteForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label for="resolutionToDelete" title="Scene resolution."
+                >Other Resolution:</label
+              >
+              <select id="resolutionToDelete" name="resolutionToDelete">
+                <option value="19200">120x160=19200</option>
+                <option value="32160">134x240=32160</option>
+                <option value="19200">160x120=19200</option>
+                <option value="25344">176x144=25344</option>
+                <option value="25920">180x144=25920</option>
+                <option value="43200">180x240=43200</option>
+                <option value="58968">182x324=58968</option>
+                <option value="59616">184x324=59616</option>
+                <option value="27648">192x144=27648</option>
+                <option value="62856">194x324=62856</option>
+                <option value="65800">200x329=65800</option>
+                <option value="72720">202x360=72720</option>
+                <option value="73440">204x360=73440</option>
+                <option value="74160">206x360=74160</option>
+                <option value="21630">210x103=21630</option>
+                <option value="31104">216x144=31104</option>
+                <option value="78480">218x360=78480</option>
+                <option value="40320">224x180=40320</option>
+                <option value="52864">224x236=52864</option>
+                <option value="56640">236x240=56640</option>
+                <option value="102816">238x432=102816</option>
+                <option value="38400">240x160=38400</option>
+                <option value="43200">240x180=43200</option>
+                <option value="79056">244x324=79056</option>
+                <option value="107136">248x432=107136</option>
+                <option value="111000">250x444=111000</option>
+                <option value="36864">256x144=36864</option>
+                <option value="126720">264x480=126720</option>
+                <option value="87480">270x324=87480</option>
+                <option value="129600">270x480=129600</option>
+                <option value="130560">272x480=130560</option>
+                <option value="61376">274x224=61376</option>
+                <option value="45440">284x160=45440</option>
+                <option value="136320">284x480=136320</option>
+                <option value="68640">286x240=68640</option>
+                <option value="64512">288x224=64512</option>
+                <option value="69120">288x240=69120</option>
+                <option value="69600">290x240=69600</option>
+                <option value="65408">292x224=65408</option>
+                <option value="69496">292x238=69496</option>
+                <option value="70080">292x240=70080</option>
+                <option value="70560">294x240=70560</option>
+                <option value="71040">296x240=71040</option>
+                <option value="66752">298x224=66752</option>
+                <option value="71520">298x240=71520</option>
+                <option value="50400">300x168=50400</option>
+                <option value="72000">300x240=72000</option>
+                <option value="75000">300x250=75000</option>
+                <option value="51340">302x170=51340</option>
+                <option value="70528">304x232=70528</option>
+                <option value="72960">304x240=72960</option>
+                <option value="69156">306x226=69156</option>
+                <option value="73440">306x240=73440</option>
+                <option value="71456">308x232=71456</option>
+                <option value="71300">310x230=71300</option>
+                <option value="73160">310x236=73160</option>
+                <option value="74400">310x240=74400</option>
+                <option value="74880">312x240=74880</option>
+                <option value="75360">314x240=75360</option>
+                <option value="54352">316x172=54352</option>
+                <option value="75208">316x238=75208</option>
+                <option value="75840">316x240=75840</option>
+                <option value="94800">316x300=94800</option>
+                <option value="73776">318x232=73776</option>
+                <option value="75684">318x238=75684</option>
+                <option value="76320">318x240=76320</option>
+                <option value="42880">320x134=42880</option>
+                <option value="43520">320x136=43520</option>
+                <option value="54400">320x170=54400</option>
+                <option value="55040">320x172=55040</option>
+                <option value="56960">320x178=56960</option>
+                <option value="57600">320x180=57600</option>
+                <option value="65920">320x206=65920</option>
+                <option value="66560">320x208=66560</option>
+                <option value="67840">320x212=67840</option>
+                <option value="68480">320x214=68480</option>
+                <option value="69760">320x218=69760</option>
+                <option value="70400">320x220=70400</option>
+                <option value="71680">320x224=71680</option>
+                <option value="72320">320x226=72320</option>
+                <option value="74880">320x234=74880</option>
+                <option value="75520">320x236=75520</option>
+                <option value="76160">320x238=76160</option>
+                <option value="76800">320x240=76800</option>
+                <option value="77440">320x242=77440</option>
+                <option value="78720">320x246=78720</option>
+                <option value="103680">320x324=103680</option>
+                <option value="77280">322x240=77280</option>
+                <option value="77760">324x240=77760</option>
+                <option value="104976">324x324=104976</option>
+                <option value="77588">326x238=77588</option>
+                <option value="78240">326x240=78240</option>
+                <option value="78064">328x238=78064</option>
+                <option value="78720">328x240=78720</option>
+                <option value="78540">330x238=78540</option>
+                <option value="79200">330x240=79200</option>
+                <option value="79680">332x240=79680</option>
+                <option value="80160">334x240=80160</option>
+                <option value="75264">336x224=75264</option>
+                <option value="80640">336x240=80640</option>
+                <option value="86016">336x256=86016</option>
+                <option value="81120">338x240=81120</option>
+                <option value="202800">338x600=202800</option>
+                <option value="81600">340x240=81600</option>
+                <option value="87040">340x256=87040</option>
+                <option value="81396">342x238=81396</option>
+                <option value="82080">342x240=82080</option>
+                <option value="82560">344x240=82560</option>
+                <option value="83040">346x240=83040</option>
+                <option value="83520">348x240=83520</option>
+                <option value="83300">350x238=83300</option>
+                <option value="84000">350x240=84000</option>
+                <option value="73216">352x208=73216</option>
+                <option value="77440">352x220=77440</option>
+                <option value="84480">352x240=84480</option>
+                <option value="92928">352x264=92928</option>
+                <option value="95744">352x272=95744</option>
+                <option value="101376">352x288=101376</option>
+                <option value="119680">352x340=119680</option>
+                <option value="225280">352x640=225280</option>
+                <option value="84252">354x238=84252</option>
+                <option value="85440">356x240=85440</option>
+                <option value="103952">356x292=103952</option>
+                <option value="85920">358x240=85920</option>
+                <option value="86400">360x240=86400</option>
+                <option value="100800">360x280=100800</option>
+                <option value="103680">360x288=103680</option>
+                <option value="129600">360x360=129600</option>
+                <option value="172800">360x480=172800</option>
+                <option value="229680">360x638=229680</option>
+                <option value="230400">360x640=230400</option>
+                <option value="73848">362x204=73848</option>
+                <option value="75296">362x208=75296</option>
+                <option value="86880">362x240=86880</option>
+                <option value="87840">366x240=87840</option>
+                <option value="175680">366x480=175680</option>
+                <option value="76544">368x208=76544</option>
+                <option value="88320">368x240=88320</option>
+                <option value="104512">368x284=104512</option>
+                <option value="105984">368x288=105984</option>
+                <option value="88060">370x238=88060</option>
+                <option value="105080">370x284=105080</option>
+                <option value="111740">370x302=111740</option>
+                <option value="89280">372x240=89280</option>
+                <option value="110856">372x298=110856</option>
+                <option value="104720">374x280=104720</option>
+                <option value="90240">376x240=90240</option>
+                <option value="106032">376x282=106032</option>
+                <option value="108288">376x288=108288</option>
+                <option value="90720">378x240=90720</option>
+                <option value="107920">380x284=107920</option>
+                <option value="110960">380x292=110960</option>
+                <option value="115520">380x304=115520</option>
+                <option value="98938">382x259=98938</option>
+                <option value="110780">382x290=110780</option>
+                <option value="82944">384x216=82944</option>
+                <option value="92160">384x240=92160</option>
+                <option value="110592">384x288=110592</option>
+                <option value="111360">384x290=111360</option>
+                <option value="184320">384x480=184320</option>
+                <option value="110396">386x286=110396</option>
+                <option value="111168">386x288=111168</option>
+                <option value="93120">388x240=93120</option>
+                <option value="110192">388x284=110192</option>
+                <option value="110760">390x284=110760</option>
+                <option value="111540">390x286=111540</option>
+                <option value="94080">392x240=94080</option>
+                <option value="93772">394x238=93772</option>
+                <option value="110880">396x280=110880</option>
+                <option value="110644">398x278=110644</option>
+                <option value="83200">400x208=83200</option>
+                <option value="84000">400x210=84000</option>
+                <option value="88000">400x220=88000</option>
+                <option value="89600">400x224=89600</option>
+                <option value="96000">400x240=96000</option>
+                <option value="106400">400x266=106400</option>
+                <option value="120000">400x300=120000</option>
+                <option value="121600">400x304=121600</option>
+                <option value="128000">400x320=128000</option>
+                <option value="288000">400x720=288000</option>
+                <option value="111504">404x276=111504</option>
+                <option value="122816">404x304=122816</option>
+                <option value="290880">404x720=290880</option>
+                <option value="110432">406x272=110432</option>
+                <option value="292320">406x720=292320</option>
+                <option value="97920">408x240=97920</option>
+                <option value="110976">408x272=110976</option>
+                <option value="98880">412x240=98880</option>
+                <option value="296640">412x720=296640</option>
+                <option value="99360">414x240=99360</option>
+                <option value="99008">416x238=99008</option>
+                <option value="99840">416x240=99840</option>
+                <option value="133120">416x320=133120</option>
+                <option value="230464">416x554=230464</option>
+                <option value="100320">418x240=100320</option>
+                <option value="100800">420x240=100800</option>
+                <option value="100436">422x238=100436</option>
+                <option value="101280">422x240=101280</option>
+                <option value="100912">424x238=100912</option>
+                <option value="101760">424x240=101760</option>
+                <option value="139920">424x330=139920</option>
+                <option value="94572">426x222=94572</option>
+                <option value="100536">426x236=100536</option>
+                <option value="101388">426x238=101388</option>
+                <option value="102240">426x240=102240</option>
+                <option value="77040">428x180=77040</option>
+                <option value="101864">428x238=101864</option>
+                <option value="102720">428x240=102720</option>
+                <option value="109568">428x256=109568</option>
+                <option value="136960">428x320=136960</option>
+                <option value="183184">428x428=183184</option>
+                <option value="102340">430x238=102340</option>
+                <option value="103200">430x240=103200</option>
+                <option value="110940">430x258=110940</option>
+                <option value="96768">432x224=96768</option>
+                <option value="103680">432x240=103680</option>
+                <option value="105408">432x244=105408</option>
+                <option value="138240">432x320=138240</option>
+                <option value="155520">432x360=155520</option>
+                <option value="165888">432x384=165888</option>
+                <option value="311040">432x720=311040</option>
+                <option value="104160">434x240=104160</option>
+                <option value="103768">436x238=103768</option>
+                <option value="104640">436x240=104640</option>
+                <option value="110376">438x252=110376</option>
+                <option value="105600">440x240=105600</option>
+                <option value="105196">442x238=105196</option>
+                <option value="111000">444x250=111000</option>
+                <option value="131424">444x296=131424</option>
+                <option value="107040">446x240=107040</option>
+                <option value="149856">446x336=149856</option>
+                <option value="107520">448x240=107520</option>
+                <option value="114688">448x256=114688</option>
+                <option value="136192">448x304=136192</option>
+                <option value="150528">448x336=150528</option>
+                <option value="164864">448x368=164864</option>
+                <option value="107100">450x238=107100</option>
+                <option value="108000">450x240=108000</option>
+                <option value="135000">450x300=135000</option>
+                <option value="145800">450x324=145800</option>
+                <option value="153000">450x340=153000</option>
+                <option value="162000">450x360=162000</option>
+                <option value="107576">452x238=107576</option>
+                <option value="108480">452x240=108480</option>
+                <option value="108960">454x240=108960</option>
+                <option value="148004">454x326=148004</option>
+                <option value="109440">456x240=109440</option>
+                <option value="164160">456x360=164160</option>
+                <option value="109920">458x240=109920</option>
+                <option value="110400">460x240=110400</option>
+                <option value="156400">460x340=156400</option>
+                <option value="165600">460x360=165600</option>
+                <option value="110880">462x240=110880</option>
+                <option value="148480">464x320=148480</option>
+                <option value="161472">464x348=161472</option>
+                <option value="111840">466x240=111840</option>
+                <option value="167760">466x360=167760</option>
+                <option value="112320">468x240=112320</option>
+                <option value="160056">468x342=160056</option>
+                <option value="168480">468x360=168480</option>
+                <option value="169200">470x360=169200</option>
+                <option value="113280">472x240=113280</option>
+                <option value="169920">472x360=169920</option>
+                <option value="170640">474x360=170640</option>
+                <option value="114240">476x240=114240</option>
+                <option value="169456">476x356=169456</option>
+                <option value="171360">476x360=171360</option>
+                <option value="114720">478x240=114720</option>
+                <option value="172080">478x360=172080</option>
+                <option value="106560">480x222=106560</option>
+                <option value="107520">480x224=107520</option>
+                <option value="115200">480x240=115200</option>
+                <option value="119040">480x248=119040</option>
+                <option value="120960">480x252=120960</option>
+                <option value="121920">480x254=121920</option>
+                <option value="122880">480x256=122880</option>
+                <option value="123840">480x258=123840</option>
+                <option value="124800">480x260=124800</option>
+                <option value="125760">480x262=125760</option>
+                <option value="126720">480x264=126720</option>
+                <option value="127680">480x266=127680</option>
+                <option value="128640">480x268=128640</option>
+                <option value="129600">480x270=129600</option>
+                <option value="130560">480x272=130560</option>
+                <option value="131520">480x274=131520</option>
+                <option value="132480">480x276=132480</option>
+                <option value="137280">480x286=137280</option>
+                <option value="138240">480x288=138240</option>
+                <option value="144960">480x302=144960</option>
+                <option value="145920">480x304=145920</option>
+                <option value="148800">480x310=148800</option>
+                <option value="150720">480x314=150720</option>
+                <option value="152640">480x318=152640</option>
+                <option value="153600">480x320=153600</option>
+                <option value="154560">480x322=154560</option>
+                <option value="155520">480x324=155520</option>
+                <option value="156480">480x326=156480</option>
+                <option value="157440">480x328=157440</option>
+                <option value="158400">480x330=158400</option>
+                <option value="159360">480x332=159360</option>
+                <option value="160320">480x334=160320</option>
+                <option value="161280">480x336=161280</option>
+                <option value="162240">480x338=162240</option>
+                <option value="163200">480x340=163200</option>
+                <option value="164160">480x342=164160</option>
+                <option value="165120">480x344=165120</option>
+                <option value="166080">480x346=166080</option>
+                <option value="168000">480x350=168000</option>
+                <option value="168960">480x352=168960</option>
+                <option value="170880">480x356=170880</option>
+                <option value="171840">480x358=171840</option>
+                <option value="172800">480x360=172800</option>
+                <option value="173760">480x362=173760</option>
+                <option value="174720">480x364=174720</option>
+                <option value="175680">480x366=175680</option>
+                <option value="176640">480x368=176640</option>
+                <option value="177600">480x370=177600</option>
+                <option value="181440">480x378=181440</option>
+                <option value="184320">480x384=184320</option>
+                <option value="187200">480x390=187200</option>
+                <option value="188160">480x392=188160</option>
+                <option value="189120">480x394=189120</option>
+                <option value="192000">480x400=192000</option>
+                <option value="230400">480x480=230400</option>
+                <option value="276480">480x576=276480</option>
+                <option value="115680">482x240=115680</option>
+                <option value="185088">482x384=185088</option>
+                <option value="411628">482x854=411628</option>
+                <option value="174240">484x360=174240</option>
+                <option value="176176">484x364=176176</option>
+                <option value="117120">488x240=117120</option>
+                <option value="175680">488x360=175680</option>
+                <option value="176400">490x360=176400</option>
+                <option value="179340">490x366=179340</option>
+                <option value="118080">492x240=118080</option>
+                <option value="177120">492x360=177120</option>
+                <option value="177840">494x360=177840</option>
+                <option value="158720">496x320=158720</option>
+                <option value="182528">496x368=182528</option>
+                <option value="186496">496x376=186496</option>
+                <option value="190464">496x384=190464</option>
+                <option value="179280">498x360=179280</option>
+                <option value="180000">500x360=180000</option>
+                <option value="187000">500x374=187000</option>
+                <option value="190000">500x380=190000</option>
+                <option value="200000">500x400=200000</option>
+                <option value="182728">502x364=182728</option>
+                <option value="184464">504x366=184464</option>
+                <option value="189992">508x374=189992</option>
+                <option value="193040">508x380=193040</option>
+                <option value="229616">508x452=229616</option>
+                <option value="139264">512x272=139264</option>
+                <option value="141312">512x276=141312</option>
+                <option value="144384">512x282=144384</option>
+                <option value="145408">512x284=145408</option>
+                <option value="147456">512x288=147456</option>
+                <option value="163840">512x320=163840</option>
+                <option value="172032">512x336=172032</option>
+                <option value="174080">512x340=174080</option>
+                <option value="188416">512x368=188416</option>
+                <option value="196608">512x384=196608</option>
+                <option value="198656">512x388=198656</option>
+                <option value="204800">512x400=204800</option>
+                <option value="212992">512x416=212992</option>
+                <option value="229376">512x448=229376</option>
+                <option value="218964">514x426=218964</option>
+                <option value="123840">516x240=123840</option>
+                <option value="185760">516x360=185760</option>
+                <option value="200208">516x388=200208</option>
+                <option value="218784">516x424=218784</option>
+                <option value="200984">518x388=200984</option>
+                <option value="218596">518x422=218596</option>
+                <option value="219632">518x424=219632</option>
+                <option value="199680">520x384=199680</option>
+                <option value="202800">520x390=202800</option>
+                <option value="203840">520x392=203840</option>
+                <option value="212160">520x408=212160</option>
+                <option value="216320">520x416=216320</option>
+                <option value="187920">522x360=187920</option>
+                <option value="218196">522x418=218196</option>
+                <option value="220080">524x420=220080</option>
+                <option value="207244">526x394=207244</option>
+                <option value="230388">526x438=230388</option>
+                <option value="160512">528x304=160512</option>
+                <option value="168960">528x320=168960</option>
+                <option value="185856">528x352=185856</option>
+                <option value="190080">528x360=190080</option>
+                <option value="194304">528x368=194304</option>
+                <option value="202752">528x384=202752</option>
+                <option value="211200">528x400=211200</option>
+                <option value="214368">528x406=214368</option>
+                <option value="219648">528x416=219648</option>
+                <option value="228960">530x432=228960</option>
+                <option value="230020">530x434=230020</option>
+                <option value="254400">530x480=254400</option>
+                <option value="212800">532x400=212800</option>
+                <option value="229824">532x432=229824</option>
+                <option value="230888">532x434=230888</option>
+                <option value="230688">534x432=230688</option>
+                <option value="190816">536x356=190816</option>
+                <option value="220832">536x412=220832</option>
+                <option value="229408">536x428=229408</option>
+                <option value="230480">536x430=230480</option>
+                <option value="219504">538x408=219504</option>
+                <option value="230264">538x428=230264</option>
+                <option value="194400">540x360=194400</option>
+                <option value="205200">540x380=205200</option>
+                <option value="219240">540x406=219240</option>
+                <option value="220320">540x408=220320</option>
+                <option value="221400">540x410=221400</option>
+                <option value="220052">542x406=220052</option>
+                <option value="221136">542x408=221136</option>
+                <option value="222220">542x410=222220</option>
+                <option value="228724">542x422=228724</option>
+                <option value="229808">542x424=229808</option>
+                <option value="165376">544x304=165376</option>
+                <option value="174080">544x320=174080</option>
+                <option value="191488">544x352=191488</option>
+                <option value="195840">544x360=195840</option>
+                <option value="200192">544x368=200192</option>
+                <option value="208896">544x384=208896</option>
+                <option value="217600">544x400=217600</option>
+                <option value="220864">544x406=220864</option>
+                <option value="221952">544x408=221952</option>
+                <option value="226304">544x416=226304</option>
+                <option value="229568">544x422=229568</option>
+                <option value="168168">546x308=168168</option>
+                <option value="220584">546x404=220584</option>
+                <option value="221676">546x406=221676</option>
+                <option value="229320">546x420=229320</option>
+                <option value="230412">546x422=230412</option>
+                <option value="197280">548x360=197280</option>
+                <option value="221392">548x404=221392</option>
+                <option value="222488">548x406=222488</option>
+                <option value="229064">548x418=229064</option>
+                <option value="230160">548x420=230160</option>
+                <option value="259752">548x474=259752</option>
+                <option value="198000">550x360=198000</option>
+                <option value="222200">550x404=222200</option>
+                <option value="228800">550x416=228800</option>
+                <option value="229900">550x418=229900</option>
+                <option value="231000">550x420=231000</option>
+                <option value="220800">552x400=220800</option>
+                <option value="221904">552x402=221904</option>
+                <option value="223008">552x404=223008</option>
+                <option value="228528">552x414=228528</option>
+                <option value="229632">552x416=229632</option>
+                <option value="230736">552x418=230736</option>
+                <option value="264960">552x480=264960</option>
+                <option value="222708">554x402=222708</option>
+                <option value="229356">554x414=229356</option>
+                <option value="230464">554x416=230464</option>
+                <option value="173472">556x312=173472</option>
+                <option value="211280">556x380=211280</option>
+                <option value="222400">556x400=222400</option>
+                <option value="229072">556x412=229072</option>
+                <option value="230184">556x414=230184</option>
+                <option value="231296">556x416=231296</option>
+                <option value="228780">558x410=228780</option>
+                <option value="231012">558x414=231012</option>
+                <option value="134400">560x240=134400</option>
+                <option value="140000">560x250=140000</option>
+                <option value="143360">560x256=143360</option>
+                <option value="161280">560x288=161280</option>
+                <option value="170240">560x304=170240</option>
+                <option value="179200">560x320=179200</option>
+                <option value="197120">560x352=197120</option>
+                <option value="206080">560x368=206080</option>
+                <option value="215040">560x384=215040</option>
+                <option value="219520">560x392=219520</option>
+                <option value="224000">560x400=224000</option>
+                <option value="229600">560x410=229600</option>
+                <option value="232960">560x416=232960</option>
+                <option value="235200">560x420=235200</option>
+                <option value="241920">560x432=241920</option>
+                <option value="257600">560x460=257600</option>
+                <option value="223676">562x398=223676</option>
+                <option value="229296">562x408=229296</option>
+                <option value="230420">562x410=230420</option>
+                <option value="203040">564x360=203040</option>
+                <option value="212064">564x376=212064</option>
+                <option value="223344">564x396=223344</option>
+                <option value="228984">564x406=228984</option>
+                <option value="230112">564x408=230112</option>
+                <option value="239136">564x424=239136</option>
+                <option value="243648">564x432=243648</option>
+                <option value="223004">566x394=223004</option>
+                <option value="229796">566x406=229796</option>
+                <option value="181760">568x320=181760</option>
+                <option value="224928">568x396=224928</option>
+                <option value="230608">568x406=230608</option>
+                <option value="572544">568x1008=572544</option>
+                <option value="229140">570x402=229140</option>
+                <option value="230280">570x404=230280</option>
+                <option value="256500">570x450=256500</option>
+                <option value="137280">572x240=137280</option>
+                <option value="229944">572x402=229944</option>
+                <option value="244816">572x428=244816</option>
+                <option value="247104">572x432=247104</option>
+                <option value="274560">572x480=274560</option>
+                <option value="409552">572x716=409552</option>
+                <option value="225008">574x392=225008</option>
+                <option value="230748">574x402=230748</option>
+                <option value="137088">576x238=137088</option>
+                <option value="138240">576x240=138240</option>
+                <option value="155520">576x270=155520</option>
+                <option value="161280">576x280=161280</option>
+                <option value="162432">576x282=162432</option>
+                <option value="165888">576x288=165888</option>
+                <option value="170496">576x296=170496</option>
+                <option value="173952">576x302=173952</option>
+                <option value="175104">576x304=175104</option>
+                <option value="177408">576x308=177408</option>
+                <option value="178560">576x310=178560</option>
+                <option value="179712">576x312=179712</option>
+                <option value="180864">576x314=180864</option>
+                <option value="183168">576x318=183168</option>
+                <option value="184320">576x320=184320</option>
+                <option value="185472">576x322=185472</option>
+                <option value="186624">576x324=186624</option>
+                <option value="187776">576x326=187776</option>
+                <option value="190080">576x330=190080</option>
+                <option value="192384">576x334=192384</option>
+                <option value="193536">576x336=193536</option>
+                <option value="195840">576x340=195840</option>
+                <option value="202752">576x352=202752</option>
+                <option value="207360">576x360=207360</option>
+                <option value="210816">576x366=210816</option>
+                <option value="214272">576x372=214272</option>
+                <option value="220032">576x382=220032</option>
+                <option value="221184">576x384=221184</option>
+                <option value="224640">576x390=224640</option>
+                <option value="226944">576x394=226944</option>
+                <option value="229248">576x398=229248</option>
+                <option value="230400">576x400=230400</option>
+                <option value="238464">576x414=238464</option>
+                <option value="239616">576x416=239616</option>
+                <option value="241920">576x420=241920</option>
+                <option value="243072">576x422=243072</option>
+                <option value="244224">576x424=244224</option>
+                <option value="246528">576x428=246528</option>
+                <option value="247680">576x430=247680</option>
+                <option value="248832">576x432=248832</option>
+                <option value="249984">576x434=249984</option>
+                <option value="251136">576x436=251136</option>
+                <option value="252288">576x438=252288</option>
+                <option value="253440">576x440=253440</option>
+                <option value="254592">576x442=254592</option>
+                <option value="255744">576x444=255744</option>
+                <option value="256896">576x446=256896</option>
+                <option value="260352">576x452=260352</option>
+                <option value="261504">576x454=261504</option>
+                <option value="264960">576x460=264960</option>
+                <option value="266112">576x462=266112</option>
+                <option value="269568">576x468=269568</option>
+                <option value="271872">576x472=271872</option>
+                <option value="276480">576x480=276480</option>
+                <option value="278784">576x484=278784</option>
+                <option value="208080">578x360=208080</option>
+                <option value="225420">578x390=225420</option>
+                <option value="230044">578x398=230044</option>
+                <option value="231200">578x400=231200</option>
+                <option value="267036">578x462=267036</option>
+                <option value="277440">578x480=277440</option>
+                <option value="185600">580x320=185600</option>
+                <option value="190240">580x328=190240</option>
+                <option value="191400">580x330=191400</option>
+                <option value="225040">580x388=225040</option>
+                <option value="229680">580x396=229680</option>
+                <option value="251720">580x434=251720</option>
+                <option value="252880">580x436=252880</option>
+                <option value="209520">582x360=209520</option>
+                <option value="225816">582x388=225816</option>
+                <option value="230472">582x396=230472</option>
+                <option value="210240">584x360=210240</option>
+                <option value="225424">584x386=225424</option>
+                <option value="228928">584x392=228928</option>
+                <option value="230096">584x394=230096</option>
+                <option value="261632">584x448=261632</option>
+                <option value="226196">586x386=226196</option>
+                <option value="229712">586x392=229712</option>
+                <option value="230884">586x394=230884</option>
+                <option value="281280">586x480=281280</option>
+                <option value="211680">588x360=211680</option>
+                <option value="226968">588x386=226968</option>
+                <option value="229320">588x390=229320</option>
+                <option value="230496">588x392=230496</option>
+                <option value="258720">588x440=258720</option>
+                <option value="270480">588x460=270480</option>
+                <option value="282240">588x480=282240</option>
+                <option value="226560">590x384=226560</option>
+                <option value="230100">590x390=230100</option>
+                <option value="231280">590x392=231280</option>
+                <option value="189440">592x320=189440</option>
+                <option value="196544">592x332=196544</option>
+                <option value="197728">592x334=197728</option>
+                <option value="198912">592x336=198912</option>
+                <option value="217856">592x368=217856</option>
+                <option value="226144">592x382=226144</option>
+                <option value="229696">592x388=229696</option>
+                <option value="230880">592x390=230880</option>
+                <option value="255744">592x432=255744</option>
+                <option value="260480">592x440=260480</option>
+                <option value="262848">592x444=262848</option>
+                <option value="265216">592x448=265216</option>
+                <option value="284160">592x480=284160</option>
+                <option value="213840">594x360=213840</option>
+                <option value="229284">594x386=229284</option>
+                <option value="200256">596x336=200256</option>
+                <option value="236016">596x396=236016</option>
+                <option value="267008">596x448=267008</option>
+                <option value="215280">598x360=215280</option>
+                <option value="228436">598x382=228436</option>
+                <option value="229632">598x384=229632</option>
+                <option value="230828">598x386=230828</option>
+                <option value="287040">598x480=287040</option>
+                <option value="144000">600x240=144000</option>
+                <option value="202800">600x338=202800</option>
+                <option value="216000">600x360=216000</option>
+                <option value="229200">600x382=229200</option>
+                <option value="244800">600x408=244800</option>
+                <option value="270000">600x450=270000</option>
+                <option value="282000">600x470=282000</option>
+                <option value="288000">600x480=288000</option>
+                <option value="216720">602x360=216720</option>
+                <option value="227556">602x378=227556</option>
+                <option value="229964">602x382=229964</option>
+                <option value="288960">602x480=288960</option>
+                <option value="222272">604x368=222272</option>
+                <option value="229520">604x380=229520</option>
+                <option value="273008">604x452=273008</option>
+                <option value="218160">606x360=218160</option>
+                <option value="229068">606x378=229068</option>
+                <option value="204288">608x336=204288</option>
+                <option value="207936">608x342=207936</option>
+                <option value="214016">608x352=214016</option>
+                <option value="231040">608x380=231040</option>
+                <option value="243200">608x400=243200</option>
+                <option value="245632">608x404=245632</option>
+                <option value="262656">608x432=262656</option>
+                <option value="272384">608x448=272384</option>
+                <option value="277248">608x456=277248</option>
+                <option value="291840">608x480=291840</option>
+                <option value="301568">608x496=301568</option>
+                <option value="656640">608x1080=656640</option>
+                <option value="229360">610x376=229360</option>
+                <option value="292800">610x480=292800</option>
+                <option value="200736">612x328=200736</option>
+                <option value="220320">612x360=220320</option>
+                <option value="228888">612x374=228888</option>
+                <option value="231336">612x378=231336</option>
+                <option value="293760">612x480=293760</option>
+                <option value="230864">614x376=230864</option>
+                <option value="294720">614x480=294720</option>
+                <option value="200816">616x326=200816</option>
+                <option value="205744">616x334=205744</option>
+                <option value="280896">616x456=280896</option>
+                <option value="284592">616x462=284592</option>
+                <option value="295680">616x480=295680</option>
+                <option value="222480">618x360=222480</option>
+                <option value="274392">618x444=274392</option>
+                <option value="296640">618x480=296640</option>
+                <option value="198400">620x320=198400</option>
+                <option value="204600">620x330=204600</option>
+                <option value="215760">620x348=215760</option>
+                <option value="218240">620x352=218240</option>
+                <option value="223200">620x360=223200</option>
+                <option value="230640">620x372=230640</option>
+                <option value="255440">620x412=255440</option>
+                <option value="257920">620x416=257920</option>
+                <option value="287680">620x464=287680</option>
+                <option value="297600">620x480=297600</option>
+                <option value="230140">622x370=230140</option>
+                <option value="298560">622x480=298560</option>
+                <option value="209664">624x336=209664</option>
+                <option value="219648">624x352=219648</option>
+                <option value="224640">624x360=224640</option>
+                <option value="229632">624x368=229632</option>
+                <option value="230880">624x370=230880</option>
+                <option value="239616">624x384=239616</option>
+                <option value="259584">624x416=259584</option>
+                <option value="270816">624x434=270816</option>
+                <option value="289536">624x464=289536</option>
+                <option value="292032">624x468=292032</option>
+                <option value="298272">624x478=298272</option>
+                <option value="299520">624x480=299520</option>
+                <option value="220352">626x352=220352</option>
+                <option value="225360">626x360=225360</option>
+                <option value="230368">626x368=230368</option>
+                <option value="300480">626x480=300480</option>
+                <option value="296416">628x472=296416</option>
+                <option value="298928">628x476=298928</option>
+                <option value="301440">628x480=301440</option>
+                <option value="229320">630x364=229320</option>
+                <option value="298620">630x474=298620</option>
+                <option value="302400">630x480=302400</option>
+                <option value="224992">632x356=224992</option>
+                <option value="227520">632x360=227520</option>
+                <option value="299568">632x474=299568</option>
+                <option value="303360">632x480=303360</option>
+                <option value="228240">634x360=228240</option>
+                <option value="230776">634x364=230776</option>
+                <option value="233312">634x368=233312</option>
+                <option value="304320">634x480=304320</option>
+                <option value="228960">636x360=228960</option>
+                <option value="230232">636x362=230232</option>
+                <option value="232776">636x366=232776</option>
+                <option value="269664">636x424=269664</option>
+                <option value="301464">636x474=301464</option>
+                <option value="302736">636x476=302736</option>
+                <option value="305280">636x480=305280</option>
+                <option value="213092">638x334=213092</option>
+                <option value="229680">638x360=229680</option>
+                <option value="230956">638x362=230956</option>
+                <option value="233508">638x366=233508</option>
+                <option value="298584">638x468=298584</option>
+                <option value="303688">638x476=303688</option>
+                <option value="306240">638x480=306240</option>
+                <option value="333036">638x522=333036</option>
+                <option value="163840">640x256=163840</option>
+                <option value="168960">640x264=168960</option>
+                <option value="170240">640x266=170240</option>
+                <option value="171520">640x268=171520</option>
+                <option value="172800">640x270=172800</option>
+                <option value="174080">640x272=174080</option>
+                <option value="175360">640x274=175360</option>
+                <option value="185600">640x290=185600</option>
+                <option value="203520">640x318=203520</option>
+                <option value="206080">640x322=206080</option>
+                <option value="207360">640x324=207360</option>
+                <option value="211200">640x330=211200</option>
+                <option value="212480">640x332=212480</option>
+                <option value="215040">640x336=215040</option>
+                <option value="216320">640x338=216320</option>
+                <option value="217600">640x340=217600</option>
+                <option value="218880">640x342=218880</option>
+                <option value="220160">640x344=220160</option>
+                <option value="221440">640x346=221440</option>
+                <option value="222720">640x348=222720</option>
+                <option value="224000">640x350=224000</option>
+                <option value="225280">640x352=225280</option>
+                <option value="227840">640x356=227840</option>
+                <option value="229120">640x358=229120</option>
+                <option value="230400">640x360=230400</option>
+                <option value="231680">640x362=231680</option>
+                <option value="235520">640x368=235520</option>
+                <option value="245760">640x384=245760</option>
+                <option value="250880">640x392=250880</option>
+                <option value="256000">640x400=256000</option>
+                <option value="262400">640x410=262400</option>
+                <option value="264960">640x414=264960</option>
+                <option value="266240">640x416=266240</option>
+                <option value="270080">640x422=270080</option>
+                <option value="272640">640x426=272640</option>
+                <option value="273920">640x428=273920</option>
+                <option value="275200">640x430=275200</option>
+                <option value="276480">640x432=276480</option>
+                <option value="279040">640x436=279040</option>
+                <option value="282880">640x442=282880</option>
+                <option value="284160">640x444=284160</option>
+                <option value="290560">640x454=290560</option>
+                <option value="291840">640x456=291840</option>
+                <option value="294400">640x460=294400</option>
+                <option value="295680">640x462=295680</option>
+                <option value="296960">640x464=296960</option>
+                <option value="298240">640x466=298240</option>
+                <option value="299520">640x468=299520</option>
+                <option value="303360">640x474=303360</option>
+                <option value="305920">640x478=305920</option>
+                <option value="307200">640x480=307200</option>
+                <option value="312320">640x488=312320</option>
+                <option value="313600">640x490=313600</option>
+                <option value="327680">640x512=327680</option>
+                <option value="405760">640x634=405760</option>
+                <option value="486400">640x760=486400</option>
+                <option value="227268">642x354=227268</option>
+                <option value="231120">642x360=231120</option>
+                <option value="234972">642x366=234972</option>
+                <option value="308160">642x480=308160</option>
+                <option value="231840">644x360=231840</option>
+                <option value="234416">644x364=234416</option>
+                <option value="309120">644x480=309120</option>
+                <option value="235144">646x364=235144</option>
+                <option value="310080">646x480=310080</option>
+                <option value="233280">648x360=233280</option>
+                <option value="235872">648x364=235872</option>
+                <option value="279936">648x432=279936</option>
+                <option value="298080">648x460=298080</option>
+                <option value="311040">648x480=311040</option>
+                <option value="237900">650x366=237900</option>
+                <option value="239200">650x368=239200</option>
+                <option value="312000">650x480=312000</option>
+                <option value="312960">652x480=312960</option>
+                <option value="235440">654x360=235440</option>
+                <option value="285144">654x436=285144</option>
+                <option value="313920">654x480=313920</option>
+                <option value="230912">656x352=230912</option>
+                <option value="241408">656x368=241408</option>
+                <option value="314880">656x480=314880</option>
+                <option value="322752">656x492=322752</option>
+                <option value="315840">658x480=315840</option>
+                <option value="244200">660x370=244200</option>
+                <option value="245520">660x372=245520</option>
+                <option value="246840">660x374=246840</option>
+                <option value="290400">660x440=290400</option>
+                <option value="316800">660x480=316800</option>
+                <option value="317760">662x480=317760</option>
+                <option value="239040">664x360=239040</option>
+                <option value="350592">664x528=350592</option>
+                <option value="239760">666x360=239760</option>
+                <option value="346320">666x520=346320</option>
+                <option value="320640">668x480=320640</option>
+                <option value="334000">668x500=334000</option>
+                <option value="251920">670x376=251920</option>
+                <option value="247296">672x368=247296</option>
+                <option value="279552">672x416=279552</option>
+                <option value="301056">672x448=301056</option>
+                <option value="322560">672x480=322560</option>
+                <option value="238596">674x354=238596</option>
+                <option value="242640">674x360=242640</option>
+                <option value="243360">676x360=243360</option>
+                <option value="282568">676x418=282568</option>
+                <option value="324480">676x480=324480</option>
+                <option value="244080">678x360=244080</option>
+                <option value="325440">678x480=325440</option>
+                <option value="261120">680x384=261120</option>
+                <option value="307360">680x452=307360</option>
+                <option value="308720">680x454=308720</option>
+                <option value="326400">680x480=326400</option>
+                <option value="245520">682x360=245520</option>
+                <option value="325996">682x478=325996</option>
+                <option value="327360">682x480=327360</option>
+                <option value="246240">684x360=246240</option>
+                <option value="262656">684x384=262656</option>
+                <option value="328320">684x480=328320</option>
+                <option value="246960">686x360=246960</option>
+                <option value="329280">686x480=329280</option>
+                <option value="264192">688x384=264192</option>
+                <option value="266944">688x388=266944</option>
+                <option value="297216">688x432=297216</option>
+                <option value="308224">688x448=308224</option>
+                <option value="330240">688x480=330240</option>
+                <option value="352256">688x512=352256</option>
+                <option value="357760">688x520=357760</option>
+                <option value="361888">688x526=361888</option>
+                <option value="363264">688x528=363264</option>
+                <option value="385280">688x560=385280</option>
+                <option value="396288">688x576=396288</option>
+                <option value="248400">690x360=248400</option>
+                <option value="331200">690x480=331200</option>
+                <option value="268496">692x388=268496</option>
+                <option value="312784">692x452=312784</option>
+                <option value="331732">694x478=331732</option>
+                <option value="261696">696x376=261696</option>
+                <option value="322944">696x464=322944</option>
+                <option value="334080">696x480=334080</option>
+                <option value="356352">696x512=356352</option>
+                <option value="373056">696x536=373056</option>
+                <option value="389760">696x560=389760</option>
+                <option value="398112">696x572=398112</option>
+                <option value="335040">698x480=335040</option>
+                <option value="369940">698x530=369940</option>
+                <option value="280000">700x400=280000</option>
+                <option value="333200">700x476=333200</option>
+                <option value="334600">700x478=334600</option>
+                <option value="336000">700x480=336000</option>
+                <option value="373800">700x534=373800</option>
+                <option value="376600">700x538=376600</option>
+                <option value="392000">700x560=392000</option>
+                <option value="336960">702x480=336960</option>
+                <option value="381888">702x544=381888</option>
+                <option value="214016">704x304=214016</option>
+                <option value="216832">704x308=216832</option>
+                <option value="247808">704x352=247808</option>
+                <option value="259072">704x368=259072</option>
+                <option value="270336">704x384=270336</option>
+                <option value="278784">704x396=278784</option>
+                <option value="281600">704x400=281600</option>
+                <option value="292864">704x416=292864</option>
+                <option value="304128">704x432=304128</option>
+                <option value="309760">704x440=309760</option>
+                <option value="326656">704x464=326656</option>
+                <option value="329472">704x468=329472</option>
+                <option value="337920">704x480=337920</option>
+                <option value="339328">704x482=339328</option>
+                <option value="357632">704x508=357632</option>
+                <option value="360448">704x512=360448</option>
+                <option value="371712">704x528=371712</option>
+                <option value="377344">704x536=377344</option>
+                <option value="405504">704x576=405504</option>
+                <option value="251336">706x356=251336</option>
+                <option value="336056">706x476=336056</option>
+                <option value="338880">706x480=338880</option>
+                <option value="334176">708x472=334176</option>
+                <option value="338424">708x478=338424</option>
+                <option value="339840">708x480=339840</option>
+                <option value="410640">708x580=410640</option>
+                <option value="339380">710x478=339380</option>
+                <option value="263440">712x370=263440</option>
+                <option value="284800">712x400=284800</option>
+                <option value="290496">712x408=290496</option>
+                <option value="333216">712x468=333216</option>
+                <option value="341760">712x480=341760</option>
+                <option value="380208">712x534=380208</option>
+                <option value="384480">712x540=384480</option>
+                <option value="391600">712x550=391600</option>
+                <option value="286400">716x400=286400</option>
+                <option value="309312">716x432=309312</option>
+                <option value="340816">716x476=340816</option>
+                <option value="343680">716x480=343680</option>
+                <option value="345112">716x482=345112</option>
+                <option value="383776">716x536=383776</option>
+                <option value="386640">716x540=386640</option>
+                <option value="409552">716x572=409552</option>
+                <option value="290072">718x404=290072</option>
+                <option value="344640">718x480=344640</option>
+                <option value="384848">718x536=384848</option>
+                <option value="397772">718x554=397772</option>
+                <option value="213120">720x296=213120</option>
+                <option value="216000">720x300=216000</option>
+                <option value="218880">720x304=218880</option>
+                <option value="220320">720x306=220320</option>
+                <option value="244800">720x340=244800</option>
+                <option value="253440">720x352=253440</option>
+                <option value="254880">720x354=254880</option>
+                <option value="259200">720x360=259200</option>
+                <option value="262080">720x364=262080</option>
+                <option value="264960">720x368=264960</option>
+                <option value="270720">720x376=270720</option>
+                <option value="273600">720x380=273600</option>
+                <option value="276480">720x384=276480</option>
+                <option value="277920">720x386=277920</option>
+                <option value="282240">720x392=282240</option>
+                <option value="285120">720x396=285120</option>
+                <option value="288000">720x400=288000</option>
+                <option value="289440">720x402=289440</option>
+                <option value="290880">720x404=290880</option>
+                <option value="292320">720x406=292320</option>
+                <option value="293760">720x408=293760</option>
+                <option value="296640">720x412=296640</option>
+                <option value="298080">720x414=298080</option>
+                <option value="299520">720x416=299520</option>
+                <option value="302400">720x420=302400</option>
+                <option value="303840">720x422=303840</option>
+                <option value="311040">720x432=311040</option>
+                <option value="316800">720x440=316800</option>
+                <option value="322560">720x448=322560</option>
+                <option value="334080">720x464=334080</option>
+                <option value="336960">720x468=336960</option>
+                <option value="345600">720x480=345600</option>
+                <option value="349920">720x486=349920</option>
+                <option value="357120">720x496=357120</option>
+                <option value="365760">720x508=365760</option>
+                <option value="368640">720x512=368640</option>
+                <option value="374400">720x520=374400</option>
+                <option value="380160">720x528=380160</option>
+                <option value="385920">720x536=385920</option>
+                <option value="388800">720x540=388800</option>
+                <option value="391680">720x544=391680</option>
+                <option value="403200">720x560=403200</option>
+                <option value="408960">720x568=408960</option>
+                <option value="414720">720x576=414720</option>
+                <option value="921600">720x1280=921600</option>
+                <option value="346560">722x480=346560</option>
+                <option value="347520">724x480=347520</option>
+                <option value="393856">724x544=393856</option>
+                <option value="290400">726x400=290400</option>
+                <option value="922020">726x1270=922020</option>
+                <option value="297024">728x408=297024</option>
+                <option value="349440">728x480=349440</option>
+                <option value="396032">728x544=396032</option>
+                <option value="409136">728x562=409136</option>
+                <option value="522704">728x718=522704</option>
+                <option value="410260">730x562=410260</option>
+                <option value="409572">734x558=409572</option>
+                <option value="264960">736x360=264960</option>
+                <option value="304704">736x414=304704</option>
+                <option value="353280">736x480=353280</option>
+                <option value="409216">736x556=409216</option>
+                <option value="410688">736x558=410688</option>
+                <option value="354240">738x480=354240</option>
+                <option value="408852">738x554=408852</option>
+                <option value="307840">740x416=307840</option>
+                <option value="409960">740x554=409960</option>
+                <option value="409584">742x552=409584</option>
+                <option value="309504">744x416=309504</option>
+                <option value="408808">746x548=408808</option>
+                <option value="314160">748x420=314160</option>
+                <option value="409904">748x548=409904</option>
+                <option value="315000">750x420=315000</option>
+                <option value="316500">750x422=316500</option>
+                <option value="360000">750x480=360000</option>
+                <option value="409500">750x546=409500</option>
+                <option value="346840">754x460=346840</option>
+                <option value="363840">758x480=363840</option>
+                <option value="410836">758x542=410836</option>
+                <option value="322240">760x424=322240</option>
+                <option value="325280">760x428=325280</option>
+                <option value="364800">760x480=364800</option>
+                <option value="365760">762x480=365760</option>
+                <option value="331776">768x432=331776</option>
+                <option value="333312">768x434=333312</option>
+                <option value="356352">768x464=356352</option>
+                <option value="368640">768x480=368640</option>
+                <option value="391680">768x510=391680</option>
+                <option value="442368">768x576=442368</option>
+                <option value="408100">770x530=408100</option>
+                <option value="370560">772x480=370560</option>
+                <option value="408672">774x528=408672</option>
+                <option value="329160">780x422=329160</option>
+                <option value="343200">780x440=343200</option>
+                <option value="366600">780x470=366600</option>
+                <option value="374400">780x480=374400</option>
+                <option value="410280">780x526=410280</option>
+                <option value="376320">784x480=376320</option>
+                <option value="409248">784x522=409248</option>
+                <option value="410816">784x524=410816</option>
+                <option value="339552">786x432=339552</option>
+                <option value="410292">786x522=410292</option>
+                <option value="378240">788x480=378240</option>
+                <option value="408672">792x516=408672</option>
+                <option value="285840">794x360=285840</option>
+                <option value="356608">796x448=356608</option>
+                <option value="382080">796x480=382080</option>
+                <option value="294400">800x368=294400</option>
+                <option value="328000">800x410=328000</option>
+                <option value="358400">800x448=358400</option>
+                <option value="360000">800x450=360000</option>
+                <option value="361600">800x452=361600</option>
+                <option value="384000">800x480=384000</option>
+                <option value="480000">800x600=480000</option>
+                <option value="386880">806x480=386880</option>
+                <option value="410256">814x504=410256</option>
+                <option value="391680">816x480=391680</option>
+                <option value="394560">822x480=394560</option>
+                <option value="297360">826x360=297360</option>
+                <option value="398400">830x480=398400</option>
+                <option value="386048">832x464=386048</option>
+                <option value="389376">832x468=389376</option>
+                <option value="399360">832x480=399360</option>
+                <option value="400320">834x480=400320</option>
+                <option value="408660">834x490=408660</option>
+                <option value="403200">840x480=403200</option>
+                <option value="404160">842x480=404160</option>
+                <option value="303840">844x360=303840</option>
+                <option value="405120">844x480=405120</option>
+                <option value="402696">846x476=402696</option>
+                <option value="406080">846x480=406080</option>
+                <option value="409464">846x484=409464</option>
+                <option value="379904">848x448=379904</option>
+                <option value="383296">848x452=383296</option>
+                <option value="403648">848x476=403648</option>
+                <option value="405344">848x478=405344</option>
+                <option value="407040">848x480=407040</option>
+                <option value="408736">848x482=408736</option>
+                <option value="410432">848x484=410432</option>
+                <option value="408000">850x480=408000</option>
+                <option value="409700">850x482=409700</option>
+                <option value="411400">850x484=411400</option>
+                <option value="301608">852x354=301608</option>
+                <option value="337392">852x396=337392</option>
+                <option value="340800">852x400=340800</option>
+                <option value="362952">852x426=362952</option>
+                <option value="381696">852x448=381696</option>
+                <option value="383400">852x450=383400</option>
+                <option value="385104">852x452=385104</option>
+                <option value="391920">852x460=391920</option>
+                <option value="400440">852x470=400440</option>
+                <option value="402144">852x472=402144</option>
+                <option value="407256">852x478=407256</option>
+                <option value="408960">852x480=408960</option>
+                <option value="410664">852x482=410664</option>
+                <option value="304024">854x356=304024</option>
+                <option value="305732">854x358=305732</option>
+                <option value="307440">854x360=307440</option>
+                <option value="314272">854x368=314272</option>
+                <option value="382592">854x448=382592</option>
+                <option value="384300">854x450=384300</option>
+                <option value="386008">854x452=386008</option>
+                <option value="389424">854x456=389424</option>
+                <option value="392840">854x460=392840</option>
+                <option value="394548">854x462=394548</option>
+                <option value="396256">854x464=396256</option>
+                <option value="397964">854x466=397964</option>
+                <option value="401380">854x470=401380</option>
+                <option value="404796">854x474=404796</option>
+                <option value="408212">854x478=408212</option>
+                <option value="409920">854x480=409920</option>
+                <option value="411628">854x482=411628</option>
+                <option value="308160">856x360=308160</option>
+                <option value="410880">856x480=410880</option>
+                <option value="411840">858x480=411840</option>
+                <option value="309600">860x360=309600</option>
+                <option value="412800">860x480=412800</option>
+                <option value="416240">860x484=416240</option>
+                <option value="413760">862x480=413760</option>
+                <option value="311040">864x360=311040</option>
+                <option value="414720">864x480=414720</option>
+                <option value="497664">864x576=497664</option>
+                <option value="559872">864x648=559872</option>
+                <option value="415680">866x480=415680</option>
+                <option value="416640">868x480=416640</option>
+                <option value="417600">870x480=417600</option>
+                <option value="418560">872x480=418560</option>
+                <option value="627840">872x720=627840</option>
+                <option value="419520">874x480=419520</option>
+                <option value="420480">876x480=420480</option>
+                <option value="374028">878x426=374028</option>
+                <option value="422400">880x480=422400</option>
+                <option value="440000">880x500=440000</option>
+                <option value="633600">880x720=633600</option>
+                <option value="426240">888x480=426240</option>
+                <option value="427200">890x480=427200</option>
+                <option value="428160">892x480=428160</option>
+                <option value="595856">892x668=595856</option>
+                <option value="448788">894x502=448788</option>
+                <option value="430080">896x480=430080</option>
+                <option value="602112">896x672=602112</option>
+                <option value="645120">896x720=645120</option>
+                <option value="431040">898x480=431040</option>
+                <option value="432000">900x480=432000</option>
+                <option value="439200">900x488=439200</option>
+                <option value="648000">900x720=648000</option>
+                <option value="712800">900x792=712800</option>
+                <option value="432960">902x480=432960</option>
+                <option value="434880">906x480=434880</option>
+                <option value="435840">908x480=435840</option>
+                <option value="436800">910x480=436800</option>
+                <option value="437760">912x480=437760</option>
+                <option value="623808">912x684=623808</option>
+                <option value="438720">914x480=438720</option>
+                <option value="439680">916x480=439680</option>
+                <option value="440640">918x480=440640</option>
+                <option value="510788">922x554=510788</option>
+                <option value="639408">924x692=639408</option>
+                <option value="666720">926x720=666720</option>
+                <option value="446400">930x480=446400</option>
+                <option value="671040">932x720=671040</option>
+                <option value="448320">934x480=448320</option>
+                <option value="449280">936x480=449280</option>
+                <option value="673920">936x720=673920</option>
+                <option value="675360">938x720=675360</option>
+                <option value="676800">940x720=676800</option>
+                <option value="452160">942x480=452160</option>
+                <option value="678240">942x720=678240</option>
+                <option value="679680">944x720=679680</option>
+                <option value="681120">946x720=681120</option>
+                <option value="511920">948x540=511920</option>
+                <option value="237500">950x250=237500</option>
+                <option value="507300">950x534=507300</option>
+                <option value="509200">950x536=509200</option>
+                <option value="513000">950x540=513000</option>
+                <option value="479808">952x504=479808</option>
+                <option value="506464">952x532=506464</option>
+                <option value="510272">952x536=510272</option>
+                <option value="685440">952x720=685440</option>
+                <option value="457920">954x480=457920</option>
+                <option value="686880">954x720=686880</option>
+                <option value="689760">958x720=689760</option>
+                <option value="399360">960x416=399360</option>
+                <option value="460800">960x480=460800</option>
+                <option value="480000">960x500=480000</option>
+                <option value="491520">960x512=491520</option>
+                <option value="497280">960x518=497280</option>
+                <option value="514560">960x536=514560</option>
+                <option value="518400">960x540=518400</option>
+                <option value="520320">960x542=520320</option>
+                <option value="522240">960x544=522240</option>
+                <option value="531840">960x554=531840</option>
+                <option value="614400">960x640=614400</option>
+                <option value="683520">960x712=683520</option>
+                <option value="691200">960x720=691200</option>
+                <option value="1228800">960x1280=1228800</option>
+                <option value="692640">962x720=692640</option>
+                <option value="694080">964x720=694080</option>
+                <option value="464640">968x480=464640</option>
+                <option value="526592">968x544=526592</option>
+                <option value="696960">968x720=696960</option>
+                <option value="698400">970x720=698400</option>
+                <option value="702720">976x720=702720</option>
+                <option value="541812">978x554=541812</option>
+                <option value="540960">980x552=540960</option>
+                <option value="707040">982x720=707040</option>
+                <option value="709920">986x720=709920</option>
+                <option value="539648">992x544=539648</option>
+                <option value="553536">992x558=553536</option>
+                <option value="564000">1000x564=564000</option>
+                <option value="750000">1000x750=750000</option>
+                <option value="554208">1004x552=554208</option>
+                <option value="670672">1004x668=670672</option>
+                <option value="483840">1008x480=483840</option>
+                <option value="653184">1008x648=653184</option>
+                <option value="677376">1008x672=677376</option>
+                <option value="484800">1010x480=484800</option>
+                <option value="485760">1012x480=485760</option>
+                <option value="581152">1016x572=581152</option>
+                <option value="586368">1018x576=586368</option>
+                <option value="588672">1022x576=588672</option>
+                <option value="565248">1024x552=565248</option>
+                <option value="577536">1024x564=577536</option>
+                <option value="589824">1024x576=589824</option>
+                <option value="630784">1024x616=630784</option>
+                <option value="632832">1024x618=632832</option>
+                <option value="786432">1024x768=786432</option>
+                <option value="142416">1032x138=142416</option>
+                <option value="598560">1032x580=598560</option>
+                <option value="606192">1038x584=606192</option>
+                <option value="607360">1040x584=607360</option>
+                <option value="748800">1040x720=748800</option>
+                <option value="508800">1060x480=508800</option>
+                <option value="631760">1060x596=631760</option>
+                <option value="763200">1060x720=763200</option>
+                <option value="482736">1068x452=482736</option>
+                <option value="512640">1068x480=512640</option>
+                <option value="640800">1068x600=640800</option>
+                <option value="647488">1072x604=647488</option>
+                <option value="919776">1072x858=919776</option>
+                <option value="773280">1074x720=773280</option>
+                <option value="774720">1076x720=774720</option>
+                <option value="776160">1078x720=776160</option>
+                <option value="777600">1080x720=777600</option>
+                <option value="1166400">1080x1080=1166400</option>
+                <option value="1751760">1080x1622=1751760</option>
+                <option value="2073600">1080x1920=2073600</option>
+                <option value="665856">1088x612=665856</option>
+                <option value="675136">1096x616=675136</option>
+                <option value="793440">1102x720=793440</option>
+                <option value="684480">1104x620=684480</option>
+                <option value="794880">1104x720=794880</option>
+                <option value="691392">1108x624=691392</option>
+                <option value="797760">1108x720=797760</option>
+                <option value="921856">1108x832=921856</option>
+                <option value="923520">1110x832=923520</option>
+                <option value="809280">1124x720=809280</option>
+                <option value="542400">1130x480=542400</option>
+                <option value="727040">1136x640=727040</option>
+                <option value="546240">1138x480=546240</option>
+                <option value="819360">1138x720=819360</option>
+                <option value="552000">1150x480=552000</option>
+                <option value="828000">1150x720=828000</option>
+                <option value="829440">1152x720=829440</option>
+                <option value="766208">1168x656=766208</option>
+                <option value="760500">1170x650=760500</option>
+                <option value="783520">1180x664=783520</option>
+                <option value="793584">1188x668=793584</option>
+                <option value="572160">1192x480=572160</option>
+                <option value="859680">1194x720=859680</option>
+                <option value="803712">1196x672=803712</option>
+                <option value="811200">1200x676=811200</option>
+                <option value="864000">1200x720=864000</option>
+                <option value="918696">1212x758=918696</option>
+                <option value="2622240">1214x2160=2622240</option>
+                <option value="2626560">1216x2160=2626560</option>
+                <option value="832320">1224x680=832320</option>
+                <option value="800928">1236x648=800928</option>
+                <option value="860256">1236x696=860256</option>
+                <option value="1012684">1238x818=1012684</option>
+                <option value="851136">1248x682=851136</option>
+                <option value="811296">1252x648=811296</option>
+                <option value="881408">1252x704=881408</option>
+                <option value="813888">1256x648=813888</option>
+                <option value="920856">1258x732=920856</option>
+                <option value="899968">1264x712=899968</option>
+                <option value="921648">1266x728=921648</option>
+                <option value="914400">1270x720=914400</option>
+                <option value="915840">1272x720=915840</option>
+                <option value="918384">1272x722=918384</option>
+                <option value="917280">1274x720=917280</option>
+                <option value="919828">1274x722=919828</option>
+                <option value="686488">1276x538=686488</option>
+                <option value="918720">1276x720=918720</option>
+                <option value="920160">1278x720=920160</option>
+                <option value="668160">1280x522=668160</option>
+                <option value="680960">1280x532=680960</option>
+                <option value="683520">1280x534=683520</option>
+                <option value="686080">1280x536=686080</option>
+                <option value="688640">1280x538=688640</option>
+                <option value="696320">1280x544=696320</option>
+                <option value="701440">1280x548=701440</option>
+                <option value="716800">1280x560=716800</option>
+                <option value="852480">1280x666=852480</option>
+                <option value="880640">1280x688=880640</option>
+                <option value="883200">1280x690=883200</option>
+                <option value="885760">1280x692=885760</option>
+                <option value="890880">1280x696=890880</option>
+                <option value="898560">1280x702=898560</option>
+                <option value="901120">1280x704=901120</option>
+                <option value="916480">1280x716=916480</option>
+                <option value="919040">1280x718=919040</option>
+                <option value="921600">1280x720=921600</option>
+                <option value="924160">1280x722=924160</option>
+                <option value="1228800">1280x960=1228800</option>
+                <option value="1310720">1280x1024=1310720</option>
+                <option value="1382400">1280x1080=1382400</option>
+                <option value="923040">1282x720=923040</option>
+                <option value="924480">1284x720=924480</option>
+                <option value="928800">1290x720=928800</option>
+                <option value="933120">1296x720=933120</option>
+                <option value="943200">1310x720=943200</option>
+                <option value="960480">1334x720=960480</option>
+                <option value="961920">1336x720=961920</option>
+                <option value="964800">1340x720=964800</option>
+                <option value="967680">1344x720=967680</option>
+                <option value="972000">1350x720=972000</option>
+                <option value="1458000">1350x1080=1458000</option>
+                <option value="977760">1358x720=977760</option>
+                <option value="979200">1360x720=979200</option>
+                <option value="980640">1362x720=980640</option>
+                <option value="983520">1366x720=983520</option>
+                <option value="984960">1368x720=984960</option>
+                <option value="986400">1370x720=986400</option>
+                <option value="989280">1374x720=989280</option>
+                <option value="992160">1378x720=992160</option>
+                <option value="993600">1380x720=993600</option>
+                <option value="1496880">1386x1080=1496880</option>
+                <option value="900720">1390x648=900720</option>
+                <option value="1008000">1400x720=1008000</option>
+                <option value="1512000">1400x1080=1512000</option>
+                <option value="1522800">1410x1080=1522800</option>
+                <option value="1524960">1412x1080=1524960</option>
+                <option value="1527120">1414x1080=1527120</option>
+                <option value="1529280">1416x1080=1529280</option>
+                <option value="1022400">1420x720=1022400</option>
+                <option value="1533600">1420x1080=1533600</option>
+                <option value="1023840">1422x720=1023840</option>
+                <option value="1025280">1424x720=1025280</option>
+                <option value="1540080">1426x1080=1540080</option>
+                <option value="1542240">1428x1080=1542240</option>
+                <option value="1546560">1432x1080=1546560</option>
+                <option value="1548720">1434x1080=1548720</option>
+                <option value="1036800">1440x720=1036800</option>
+                <option value="1166400">1440x810=1166400</option>
+                <option value="1555200">1440x1080=1555200</option>
+                <option value="2073600">1440x1440=2073600</option>
+                <option value="1557360">1442x1080=1557360</option>
+                <option value="1120544">1444x776=1120544</option>
+                <option value="1559520">1444x1080=1559520</option>
+                <option value="1561680">1446x1080=1561680</option>
+                <option value="1563840">1448x1080=1563840</option>
+                <option value="1566000">1450x1080=1566000</option>
+                <option value="1568160">1452x1080=1568160</option>
+                <option value="1570320">1454x1080=1570320</option>
+                <option value="1576800">1460x1080=1576800</option>
+                <option value="1058400">1470x720=1058400</option>
+                <option value="1589760">1472x1080=1589760</option>
+                <option value="1594080">1476x1080=1594080</option>
+                <option value="1080000">1500x720=1080000</option>
+                <option value="1307968">1528x856=1307968</option>
+                <option value="1327104">1536x864=1327104</option>
+                <option value="1117440">1552x720=1117440</option>
+                <option value="1717200">1590x1080=1717200</option>
+                <option value="1749600">1620x1080=1749600</option>
+                <option value="1805760">1672x1080=1805760</option>
+                <option value="1213920">1686x720=1213920</option>
+                <option value="1827360">1692x1080=1827360</option>
+                <option value="1219680">1694x720=1219680</option>
+                <option value="1831680">1696x1080=1831680</option>
+                <option value="1244160">1728x720=1244160</option>
+                <option value="1866240">1728x1080=1866240</option>
+                <option value="1249920">1736x720=1249920</option>
+                <option value="1257120">1746x720=1257120</option>
+                <option value="1265116">1762x718=1265116</option>
+                <option value="1939680">1796x1080=1939680</option>
+                <option value="1944000">1800x1080=1944000</option>
+                <option value="2592000">1800x1440=2592000</option>
+                <option value="2077488">1832x1134=2077488</option>
+                <option value="2076480">1854x1120=2076480</option>
+                <option value="2669760">1854x1440=2669760</option>
+                <option value="2075760">1860x1116=2075760</option>
+                <option value="2077216">1868x1112=2077216</option>
+                <option value="2058480">1906x1080=2058480</option>
+                <option value="2744640">1906x1440=2744640</option>
+                <option value="2060640">1908x1080=2060640</option>
+                <option value="2070440">1910x1084=2070440</option>
+                <option value="2072608">1912x1084=2072608</option>
+                <option value="2073112">1916x1082=2073112</option>
+                <option value="2076944">1916x1084=2076944</option>
+                <option value="2071440">1918x1080=2071440</option>
+                <option value="2075276">1918x1082=2075276</option>
+                <option value="1536000">1920x800=1536000</option>
+                <option value="1566720">1920x816=1566720</option>
+                <option value="1996800">1920x1040=1996800</option>
+                <option value="2073600">1920x1080=2073600</option>
+                <option value="2764800">1920x1440=2764800</option>
+                <option value="2075760">1922x1080=2075760</option>
+                <option value="2077920">1924x1080=2077920</option>
+                <option value="2082240">1928x1080=2082240</option>
+                <option value="2836800">1970x1440=2836800</option>
+                <option value="2839680">1972x1440=2839680</option>
+                <option value="2845440">1976x1440=2845440</option>
+                <option value="2162160">2002x1080=2162160</option>
+                <option value="2207520">2044x1080=2207520</option>
+                <option value="2211840">2048x1080=2211840</option>
+                <option value="2214000">2050x1080=2214000</option>
+                <option value="2274480">2106x1080=2274480</option>
+                <option value="3396288">2128x1596=3396288</option>
+                <option value="8294400">2160x3840=8294400</option>
+                <option value="1638720">2276x720=1638720</option>
+                <option value="3591360">2494x1440=3591360</option>
+                <option value="3686400">2560x1440=3686400</option>
+                <option value="4147200">2880x1440=4147200</option>
+                <option value="6363360">2946x2160=6363360</option>
+                <option value="8294400">3840x2160=8294400</option>
+                <option value="8847360">4096x2160=8847360</option>
+                <option value="9331200">4320x2160=9331200</option>
+              </select>
+              <label for="resolutionToDeleteLess">All:</label>
+              <button
+                type="button"
+                id="resolutionToDeleteLess"
+                title="Delete tagged duplicates with resolution less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="resolutionToDeleteEq"
+                title="Delete tagged duplicates with resolution equal to"
+              >
+                =
+              </button>
+              <button
+                type="button"
+                id="resolutionToDeleteGreater"
+                title="Delete tagged duplicates with resolution greater than"
+              >
+                >
+              </button>
+              <label for="resolutionToDeleteBlacklistLess">Blacklist:</label>
+              <button
+                type="button"
+                id="resolutionToDeleteBlacklistLess"
+                title="Delete blacklist tagged duplicates with resolution less than"
+              >
+                <
+              </button>
+              <button
+                type="button"
+                id="resolutionToDeleteBlacklistEq"
+                title="Delete blacklist tagged duplicates with resolution equal to"
+              >
+                =
+              </button>
+              <button
+                type="button"
+                id="resolutionToDeleteBlacklistGreater"
+                title="Delete blacklist tagged duplicates with resolution greater than"
+              >
+                >
+              </button>
+            </form>
+          </td>
+          <td>
+            <label for="resolutionToDeleteCombobox">Resolution:</label>
+            <select
+              id="resolutionToDeleteCombobox"
+              name="resolutionToDeleteCombobox"
+            >
+              <option value="" selected="selected"></option>
+              <option value="Less">Less</option>
+              <option value="Eq">Equal</option>
+              <option value="Greater">Greater</option>
+            </select>
+          </td>
+        </tr>
+      </table>
+    </center>
+    <div id="div1"></div>
+    <br />
+
+    <center>
+      <table style="color: darkgreen; background-color: powderblue">
+        <tr>
+          <th colspan="2">
+            Create report with different
+            <b style="color: red">[Match Duplicate Distance]</b> options <br />
+            <div style="font-size: 12px">
+              Overrides user [Match Duplicate Distance] and
+              [significantTimeDiff] settings
+            </div>
+            <form
+              id="significantTimeDiffForm"
+              action="javascript:DeleteDupInPath();"
+              target="_self"
+            >
+              <label
+                for="significantTimeDiff"
+                title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%."
+                >Time Difference%:</label
+              >
+              <input
+                type="number"
+                min="0.25"
+                max="1.00"
+                step="0.01"
+                id="significantTimeDiff"
+                name="significantTimeDiff"
+                title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%."
+                value="0.90"
+              />
+            </form>
+          </th>
+        </tr>
+        <tr>
+          <td>
+            <table style="color: darkgreen; background-color: powderblue">
+              <tr>
+                <th
+                  title="Create report with tagging (_DuplicateMarkForDeletion_)"
+                >
+                  Create Report with Tagging
+                </th>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="tag_duplicates_task0"
+                      value="0"
+                      title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_0 and using [Match Duplicate Distance]=0 (Exact Match)."
+                    >
+                      Create Duplicate Tagging Report [Exact Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="tag_duplicates_task1"
+                      value="1"
+                      title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_1 and using [Match Duplicate Distance]=1 (High Match)."
+                    >
+                      Create Duplicate Tagging Report [High Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="tag_duplicates_task2"
+                      value="2"
+                      title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_2 and using [Match Duplicate Distance]=2 (Medium Match)."
+                    >
+                      Create Duplicate Tagging Report [Medium Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="tag_duplicates_task3"
+                      value="3"
+                      title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_3 and using [Match Duplicate Distance]=3 (Low Match)."
+                    >
+                      Create Duplicate Tagging Report [Low Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+            </table>
+          </td>
+          <td>
+            <table style="color: darkgreen; background-color: powderblue">
+              <tr>
+                <th title="Create report with NO tagging (NO Dup Tag)">
+                  Create Report without Tagging
+                </th>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="create_duplicate_report_task0"
+                      value="0"
+                      title="Create report using [Match Duplicate Distance]=0 (Exact Match). NO tagging."
+                    >
+                      Create Duplicate Report [Exact Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="create_duplicate_report_task1"
+                      value="1"
+                      title="Create report using [Match Duplicate Distance]=1 (High Match). NO tagging."
+                    >
+                      Create Duplicate Report [High Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="create_duplicate_report_task2"
+                      value="2"
+                      title="Create report using [Match Duplicate Distance]=2 (Medium Match). NO tagging."
+                    >
+                      Create Duplicate Report [Medium Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  <center>
+                    <button
+                      type="button"
+                      id="create_duplicate_report_task3"
+                      value="3"
+                      title="Create report using [Match Duplicate Distance]=3 (Low Match). NO tagging."
+                    >
+                      Create Duplicate Report [Low Match]
+                    </button>
+                  </center>
+                </td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+        <tr>
+          <td style="font-size: 12px" colspan="2">
+            <b>Details:</b>
+            <ol type="I" style="padding-left: 16px">
+              <li>Match Duplicate Distance Number Details</li>
+              <ol type="1" start="0" style="padding-left: 16px">
+                <li><b style="color: red">Exact Match</b></li>
+                <ol type="a" style="padding-left: 16px">
+                  <li>Safest and most reliable option</li>
+                  <li>Uses tag name _DuplicateMarkForDeletion<b>_0</b></li>
+                  <li>
+                    Has the fewest results, and it's very rare to have false
+                    matches.
+                  </li>
+                </ol>
+                <li><b style="color: red">High Match</b></li>
+                <ol type="a" style="padding-left: 16px">
+                  <li>Recommended Setting</li>
+                  <li>Safe and usually reliable</li>
+                  <li>Uses tag name _DuplicateMarkForDeletion<b>_1</b></li>
+                  <li>
+                    Scenes tagged by Exact Match will have both tags
+                    (_DuplicateMarkForDeletion_0 and
+                    _DuplicateMarkForDeletion_1)
+                  </li>
+                </ol>
+                <li><b style="color: red">Medium Match</b></li>
+                <ol type="a" style="padding-left: 16px">
+                  <li>Not so safe. Some false matches</li>
+                  <li>
+                    To reduce false matches use a time difference of .96 or
+                    higher.
+                  </li>
+                  <li>Uses tag name _DuplicateMarkForDeletion<b>_2</b></li>
+                  <li>Scenes tagged by 0 and 1 will have three tags.</li>
+                </ol>
+                <li><b style="color: red">Low Match</b></li>
+                <ol type="a" style="padding-left: 16px">
+                  <li>Unsafe, and many false matches</li>
+                  <li>
+                    To reduce false matches use a time difference of .98 or
+                    higher.
+                  </li>
+                  <li>Uses tag name _DuplicateMarkForDeletion<b>_3</b></li>
+                  <li>Scenes tagged by 0, 1, and 2 will have four tags.</li>
+                  <li>Has the most results, but with many false matches.</li>
+                </ol>
+              </ol>
+              <li>Time Difference</li>
+              <ol type="1" style="padding-left: 16px">
+                <li>
+                  Significant time difference setting, where 1 equals 100% and
+                  (.9) equals 90%.
+                </li>
+                <li>
+                  This setting overrides the setting in
+                  DupFileManager_config.py.
+                </li>
+                <ol type="a" style="padding-left: 16px">
+                  <li>
+                    See setting <b style="color: red">significantTimeDiff</b> in
+                    DupFileManager_config.py
+                  </li>
+                </ol>
+                <li>
+                  This setting is generally not useful for
+                  <b style="color: red">[Exact Match]</b> reports.
+                </li>
+                <li>
+                  This is an important setting when creating Low or Medium match
+                  reports. It will reduce false matches.
+                </li>
+              </ol>
+              <li>Report with tagging</li>
+              <ol type="1" style="padding-left: 16px">
+                <li>
+                  Reports with tagging will work with above
+                  <b>DupFileManager Advance Menu</b>.
+                </li>
+                <li>The report can take serveral minutes to complete.</li>
+                <li>
+                  It takes much more time to produce a report with tagging
+                  compare to creating a report without tagging.
+                </li>
+              </ol>
+              <li>Report WITHOUT tagging</li>
+              <ol type="1" style="padding-left: 16px">
+                <li>
+                  Reports with no tagging can <b style="color: red">NOT</b> be
+                  used with above <b>DupFileManager Advance Menu</b>.
+                </li>
+                <li>
+                  The report is created much faster. It usually takes a few
+                  seconds to complete.
+                </li>
+                <li>
+                  This is the recommended report type to create if the
+                  <b>DupFileManager Advance Menu</b> is not needed or desired.
+                </li>
+              </ol>
+            </ol>
+          </td>
+        </tr>
+      </table>
+    </center>
+  </body>
+</html>
diff --git a/plugins/DupFileManager/requirements.txt b/plugins/DupFileManager/requirements.txt
index d503550d..19069845 100644
--- a/plugins/DupFileManager/requirements.txt
+++ b/plugins/DupFileManager/requirements.txt
@@ -1,4 +1,3 @@
 stashapp-tools >= 0.2.50
-pyYAML
-watchdog
+requests
 Send2Trash
\ No newline at end of file