First non-beta release for FileMonitor Plugin

- Added Tools-UI option to get FileMonitor running status.
- Added Stash toolbar icon to get FileMonitor running status.
This commit is contained in:
David Maisonave
2024-11-29 07:11:04 -05:00
parent d19dd5cdc0
commit 655cf7051a
11 changed files with 214 additions and 72 deletions

View File

@@ -48,7 +48,6 @@
const React = PluginApi.React; const React = PluginApi.React;
const GQL = PluginApi.GQL; const GQL = PluginApi.GQL;
const { Button } = PluginApi.libraries.Bootstrap; const { Button } = PluginApi.libraries.Bootstrap;
const { faEthernet } = PluginApi.libraries.FontAwesomeSolid;
const { Link, NavLink, } = PluginApi.libraries.ReactRouterDOM; const { Link, NavLink, } = PluginApi.libraries.ReactRouterDOM;
// ToolTip text // 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 CreateReportButtonToolTip = "Tag duplicate files, and create a new duplicate file report listing all duplicate files and using existing DupFileManager plugin options selected.";
@@ -57,12 +56,12 @@
const ShowReportButtonToolTip = "Open link to the duplicate file (HTML) report created in local path."; 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."; const ReportMenuButtonToolTip = "Main report menu for DupFileManager. Create and show duplicate files on an HTML report.";
// Buttons // Buttons
const DupFileManagerReportMenuButton = React.createElement(Link, { to: "/plugin/DupFileManager", title: ReportMenuButtonToolTip }, React.createElement(Button, null, "DupFileManager Report Menu")); const DupFileManagerReportMenuButton = React.createElement(Link, { to: "/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")); const ToolsMenuOptionButton = React.createElement(Link, { to: "/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 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 File Deletion Menu]."}, React.createElement(Button, null, "Show [Advance Duplicate File Deletion Menu]"));} function GetAdvanceMenuButton(){return React.createElement("a", { href: AdvanceMenuOptionUrl, title: "Open link to the [Advance Duplicate File Deletion Menu]."}, React.createElement(Button, null, "Show [Advance Duplicate File Deletion Menu]"));}
function GetCreateReportNoTagButton(ButtonText){return React.createElement(Link, { to: "/plugin/DupFileManager_CreateReportWithNoTagging", title: CreateReportNoTagButtonToolTip }, React.createElement(Button, null, ButtonText));} function GetCreateReportNoTagButton(ButtonText){return React.createElement(Link, { to: "/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));} function GetCreateReportButton(ButtonText){return React.createElement(Link, { to: "/DupFileManager_CreateReport", title: CreateReportButtonToolTip }, React.createElement(Button, null, ButtonText));}
const { LoadingIndicator, } = PluginApi.components; const { LoadingIndicator, } = PluginApi.components;
const HomePage = () => { const HomePage = () => {
@@ -132,34 +131,34 @@
React.createElement("p", null), React.createElement("p", null),
GetShowReportButton(GetLocalDuplicateReportPath(), "Show Duplicate-File Report"), GetShowReportButton(GetLocalDuplicateReportPath(), "Show Duplicate-File Report"),
React.createElement("p", null), 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(Link, { to: "/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("hr", {class:"dotted"}),
React.createElement("h3", {class:"submenu"}, "Tagged Duplicates Options"), React.createElement("h3", {class:"submenu"}, "Tagged Duplicates Options"),
React.createElement("p", null), React.createElement("p", null),
GetAdvanceMenuButton(), GetAdvanceMenuButton(),
React.createElement("p", null), 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(Link, { to: "/DupFileManager_deleteTaggedDuplicatesTask", title: "Delete scenes previously given duplicate tag (_DuplicateMarkForDeletion)." }, React.createElement(Button, null, "Delete Tagged Duplicates")),
React.createElement("p", null), 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(Link, { to: "/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("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(Link, { to: "/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("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(Link, { to: "/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("p", null),
React.createElement("hr", {class:"dotted"}), React.createElement("hr", {class:"dotted"}),
React.createElement("h3", {class:"submenu"}, "Tagged Management Options"), React.createElement("h3", {class:"submenu"}, "Tagged Management Options"),
React.createElement("p", null), 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(Link, { to: "/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("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(Link, { to: "/DupFileManager_deleteAllDupFileManagerTags", title: "Delete all DupFileManager tags from stash." }, React.createElement(Button, null, "Delete All DupFileManager Tags")),
React.createElement("p", null), 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(Link, { to: "/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("hr", {class:"dotted"}),
React.createElement("h3", {class:"submenu"}, "Miscellaneous Options"), 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(Link, { to: "/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),
React.createElement("p", null), React.createElement("p", null),
@@ -276,40 +275,41 @@
} }
return ToolsAndUtilities(); return ToolsAndUtilities();
}; };
PluginApi.register.route("/plugin/DupFileManager", HomePage); PluginApi.register.route("/DupFileManager", HomePage);
PluginApi.register.route("/plugin/DupFileManager_CreateReport", CreateReport); PluginApi.register.route("/DupFileManager_CreateReport", CreateReport);
PluginApi.register.route("/plugin/DupFileManager_CreateReportWithNoTagging", CreateReportWithNoTagging); PluginApi.register.route("/DupFileManager_CreateReportWithNoTagging", CreateReportWithNoTagging);
PluginApi.register.route("/plugin/DupFileManager_ToolsAndUtilities", ToolsAndUtilities); PluginApi.register.route("/DupFileManager_ToolsAndUtilities", ToolsAndUtilities);
PluginApi.register.route("/plugin/DupFileManager_ClearAllDuplicateTags", ClearAllDuplicateTags); PluginApi.register.route("/DupFileManager_ClearAllDuplicateTags", ClearAllDuplicateTags);
PluginApi.register.route("/plugin/DupFileManager_deleteLocalDupReportHtmlFiles", deleteLocalDupReportHtmlFiles); PluginApi.register.route("/DupFileManager_deleteLocalDupReportHtmlFiles", deleteLocalDupReportHtmlFiles);
PluginApi.register.route("/plugin/DupFileManager_deleteAllDupFileManagerTags", deleteAllDupFileManagerTags); PluginApi.register.route("/DupFileManager_deleteAllDupFileManagerTags", deleteAllDupFileManagerTags);
PluginApi.register.route("/plugin/DupFileManager_generatePHASH_Matching", generatePHASH_Matching); PluginApi.register.route("/DupFileManager_generatePHASH_Matching", generatePHASH_Matching);
PluginApi.register.route("/plugin/DupFileManager_tagGrayList", tagGrayList); PluginApi.register.route("/DupFileManager_tagGrayList", tagGrayList);
PluginApi.register.route("/plugin/DupFileManager_deleteTaggedDuplicatesTask", deleteTaggedDuplicatesTask); PluginApi.register.route("/DupFileManager_deleteTaggedDuplicatesTask", deleteTaggedDuplicatesTask);
PluginApi.register.route("/plugin/DupFileManager_deleteBlackListTaggedDuplicatesTask", deleteBlackListTaggedDuplicatesTask); PluginApi.register.route("/DupFileManager_deleteBlackListTaggedDuplicatesTask", deleteBlackListTaggedDuplicatesTask);
PluginApi.register.route("/plugin/DupFileManager_deleteTaggedDuplicatesLwrResOrLwrDuration", deleteTaggedDuplicatesLwrResOrLwrDuration); PluginApi.register.route("/DupFileManager_deleteTaggedDuplicatesLwrResOrLwrDuration", deleteTaggedDuplicatesLwrResOrLwrDuration);
PluginApi.register.route("/plugin/DupFileManager_deleteBlackListTaggedDuplicatesLwrResOrLwrDuration", deleteBlackListTaggedDuplicatesLwrResOrLwrDuration); PluginApi.register.route("/DupFileManager_deleteBlackListTaggedDuplicatesLwrResOrLwrDuration", deleteBlackListTaggedDuplicatesLwrResOrLwrDuration);
PluginApi.patch.before("SettingsToolsSection", function (props) { PluginApi.patch.before("SettingsToolsSection", function (props) {
const { Setting, } = PluginApi.components; const { Setting, } = PluginApi.components;
return [ return [
{ {
children: (React.createElement(React.Fragment, null, children: (React.createElement(React.Fragment, null,
props.children, 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: "/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"))}), React.createElement(Setting, { heading: React.createElement(Link, { to: "/DupFileManager_ToolsAndUtilities", title: ToolsMenuToolTip }, React.createElement(Button, null, "DupFileManager Tools and Utilities"))}),
)), )),
}, },
]; ];
}); });
const { faFileVideo } = PluginApi.libraries.FontAwesomeSolid;
PluginApi.patch.before("MainNavBar.UtilityItems", function (props) { PluginApi.patch.before("MainNavBar.UtilityItems", function (props) {
const { Icon, } = PluginApi.components; const { Icon, } = PluginApi.components;
return [ return [
{ {
children: (React.createElement(React.Fragment, null, children: (React.createElement(React.Fragment, null,
props.children, props.children,
React.createElement(NavLink, { className: "nav-utility", exact: true, to: "/plugin/DupFileManager" }, React.createElement(NavLink, { className: "nav-utility", exact: true, to: "/DupFileManager" },
React.createElement(Button, { className: "minimal d-flex align-items-center h-100", title: ReportMenuButtonToolTip }, React.createElement(Button, { className: "minimal d-flex align-items-center h-100", title: ReportMenuButtonToolTip },
React.createElement(Icon, { icon: faEthernet }))))) React.createElement(Icon, { icon: faFileVideo }))))) // faFileVideo fa-FileImport
} }
]; ];
}); });

View File

@@ -1,6 +1,6 @@
name: DupFileManager name: DupFileManager
description: Manages duplicate files. description: Manages duplicate files.
version: 1.0.0 version: 1.0.1
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
ui: ui:
javascript: javascript:

View File

@@ -1,4 +1,4 @@
# DupFileManager: Ver 1.0.0 (By David Maisonave) # DupFileManager: Ver 1.0.1 (By David Maisonave)
DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which manages duplicate files 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. It has both **task** and **tools-UI** components.
@@ -101,9 +101,10 @@ That's it!!!
- Full bottom extended portion of the Advanced Menu screen. - 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) - ![Screenshot 2024-11-22 232208](https://github.com/user-attachments/assets/bf1f3021-3a8c-4875-9737-60ee3d7fe675)
### Future Planned Features ### Future Planned Features or Fixes
- Add logic to merge performers and galaries seperatly from tag merging on report. Planned for 1.5.0 Version. - Add logic to merge performers and galaries seperatly from tag merging on report. Planned for 1.5.0 Version.
- Add code to report to make it when the report updates the screen (due to tag merging), it stays in the same row position. Planned for 1.5.0 Version. - Add code to report to make it when the report updates the screen (due to tag merging), it stays in the same row position. Planned for 1.5.0 Version.
- Fix errors on HTML page listed in https://validator.w3.org. Planned for 1.9.0 Version.
- Add logic to merge group metadata when selecting merge option on report. Planned for 2.0.0 Version. - Add logic to merge group metadata when selecting merge option on report. Planned for 2.0.0 Version.
- Add advanced menu directly to the Settings->Tools menu. Planned for 2.0.0 Version. - Add advanced menu directly to the Settings->Tools menu. Planned for 2.0.0 Version.
- Add report directly to the Settings->Tools menu. Planned for 2.0.0 Version. - Add report directly to the Settings->Tools menu. Planned for 2.0.0 Version.

View File

@@ -281,31 +281,33 @@ function DeleteDupInPath(){
<tr><th><div><b style="color:red;"><i>DupFileManager</i></b></div>Advance Duplicate File Deletion Menu</th><th>Apply Multiple Options</th></tr> <tr><th><div><b style="color:red;"><i>DupFileManager</i></b></div>Advance Duplicate File Deletion Menu</th><th>Apply Multiple Options</th></tr>
<tr> <tr>
<td> <td>
<table style="border-collapse: collapse; border: none;"><tr style="border: none;"> <table style="border-collapse: collapse; border: none;">
<tr><th colspan="3" style="font-size: 12px;border: none;">Create report overriding user [Match Duplicate Distance] and [significantTimeDiff] settings</th></tr> <tr><th colspan="3" style="font-size: 12px;border: none;">Create report overriding user [Match Duplicate Distance] and [significantTimeDiff] settings</th></tr>
<td style="border: none;"><div class="dropdown"> <tr>
<button type="button" id="create_duplicate_report_task" value="-1" title="Create new report WITHOUT tags using user settings for [Match Duplicate Distance].">Create Duplicate Report <i class="fa fa-caret-down"></i></button> <td style="border: none;"><div class="dropdown">
<div class="dropdown-content"> <button type="button" id="create_duplicate_report_task" value="-1" title="Create new report WITHOUT tags using user settings for [Match Duplicate Distance].">Create Duplicate Report <i class="fa fa-caret-down"></i></button>
<div><button type="button" id="create_duplicate_report_task0a" value="0" title="Create report using [Match Duplicate Distance]=0 (Exact Match). NO tagging.">Create Duplicate Report [Exact Match]</button></div> <div class="dropdown-content">
<div><button type="button" id="create_duplicate_report_task1a" value="1" title="Create report using [Match Duplicate Distance]=1 (High Match). NO tagging.">Create Duplicate Report [High Match]</button></div> <div><button type="button" id="create_duplicate_report_task0a" value="0" title="Create report using [Match Duplicate Distance]=0 (Exact Match). NO tagging.">Create Duplicate Report [Exact Match]</button></div>
<div><button type="button" id="create_duplicate_report_task2a" value="2" title="Create report using [Match Duplicate Distance]=2 (Medium Match). NO tagging.">Create Duplicate Report [Medium Match]</button></div> <div><button type="button" id="create_duplicate_report_task1a" value="1" title="Create report using [Match Duplicate Distance]=1 (High Match). NO tagging.">Create Duplicate Report [High Match]</button></div>
<div><button type="button" id="create_duplicate_report_task3a" value="3" title="Create report using [Match Duplicate Distance]=3 (Low Match). NO tagging.">Create Duplicate Report [Low Match]</button></div> <div><button type="button" id="create_duplicate_report_task2a" value="2" title="Create report using [Match Duplicate Distance]=2 (Medium Match). NO tagging.">Create Duplicate Report [Medium Match]</button></div>
<div style="height:2px;width:220px;border-width:0;color:gray;background-color:gray;">_</div> <div><button type="button" id="create_duplicate_report_task3a" value="3" title="Create report using [Match Duplicate Distance]=3 (Low Match). NO tagging.">Create Duplicate Report [Low Match]</button></div>
<div><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 (With Default Match Setting)</button></div> <div style="height:2px;width:220px;border-width:0;color:gray;background-color:gray;">_</div>
<div><button type="button" id="tag_duplicates_task0a" 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></div> <div><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 (With Default Match Setting)</button></div>
<div><button type="button" id="tag_duplicates_task1a" 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></div> <div><button type="button" id="tag_duplicates_task0a" 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></div>
<div><button type="button" id="tag_duplicates_task2a" 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></div> <div><button type="button" id="tag_duplicates_task1a" 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></div>
<div><button type="button" id="tag_duplicates_task3a" 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></div> <div><button type="button" id="tag_duplicates_task2a" 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></div>
</div> <div><button type="button" id="tag_duplicates_task3a" 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></div>
</div></td> </div>
<td style="border: none;padding: 0 15px;"> </div></td>
<label for="significantTimeDiff" title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%.">Time Difference%:</label> <td style="border: none;padding: 0 15px;">
<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"> <label for="significantTimeDiff" title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%.">Time Difference%:</label>
</td> <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">
<td style="border: none;padding: 0 15px;"> </td>
<button type="button" id="viewreport" title="View duplicate file report.">View Dup Report</button> <td style="border: none;padding: 0 15px;">
</td> <button type="button" id="viewreport" title="View duplicate file report.">View Dup Report</button>
</tr></table> </td>
</tr>
</table>
</td> </td>
<td> <td>
<div class="dropdown"> <div class="dropdown">
@@ -340,7 +342,7 @@ function DeleteDupInPath(){
<tr> <tr>
<td><form id="pathToDeleteForm" action="javascript:DeleteDupInPath();" target="_self"> <td><form id="pathToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
<label for="pathToDeleteText">Path:</label> <label for="pathToDeleteText">Path:</label>
<input type="text" id="pathToDeleteText" name="pathToDeleteText" value="C:\Downloads"> <input type="text" id="pathToDeleteText" name="pathToDeleteText" placeholder="C:\Downloads" value="">
<button type="button" id="pathToDelete" title="Delete tagged duplicates having file path">Delete Dup in Path</button> <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> <button type="button" id="pathToDeleteBlacklist" title="Delete blacklist tagged duplicates having file path">Del Blacklist Dup in Path</button>
</form> </form>
@@ -350,7 +352,7 @@ function DeleteDupInPath(){
<tr> <tr>
<td><form id="sizeToDeleteForm" action="javascript:DeleteDupInPath();" target="_self"> <td><form id="sizeToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
<label for="sizeToDelete" title="File size in bytes.">File Size (bytes):</label> <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"> <input type="number" min="0" step="1000000" id="sizeToDelete" name="sizeToDelete" title="File size in bytes." placeholder="123456" value="">
<label for="sizeToDeleteLess">All:</label> <label for="sizeToDeleteLess">All:</label>
<button type="button" id="sizeToDeleteLess" title="Delete tagged duplicates with file size less than"><</button> <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> <button type="button" id="sizeToDeleteGreater" title="Delete tagged duplicates with file size greater than">></button>
@@ -370,7 +372,7 @@ function DeleteDupInPath(){
<tr> <tr>
<td><form id="durationToDeleteForm" action="javascript:DeleteDupInPath();" target="_self"> <td><form id="durationToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
<label for="durationToDelete" title="Scene duration (time) in seconds.">Duration (seconds):</label> <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"> <input type="number" min="1" max="14400" id="durationToDelete" name="durationToDelete" title="Duration in seconds." placeholder="60" value="">
<label for="durationToDeleteLess">All:</label> <label for="durationToDeleteLess">All:</label>
<button type="button" id="durationToDeleteLess" title="Delete tagged duplicates with duration less than"><</button> <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> <button type="button" id="durationToDeleteGreater" title="Delete tagged duplicates with duration greater than">></button>
@@ -429,7 +431,7 @@ function DeleteDupInPath(){
<tr> <tr>
<td><form id="tagToDeleteForm" action="javascript:DeleteDupInPath();" target="_self"> <td><form id="tagToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
<label for="tagToDelete" title="Scene with tag.">Tag:</label> <label for="tagToDelete" title="Scene with tag.">Tag:</label>
<input type="text" id="tagToDeleteText" name="tagToDelete" title="tag name." value="redhead"> <input type="text" id="tagToDeleteText" name="tagToDelete" title="tag name." placeholder="redhead" value="">
<label for="tagToDelete">All:</label> <label for="tagToDelete">All:</label>
<button type="button" id="tagToDelete" title="Delete tagged duplicates with tag name">Contains</button> <button type="button" id="tagToDelete" title="Delete tagged duplicates with tag name">Contains</button>
<label for="tagToDeleteBlacklist">Blacklist:</label> <label for="tagToDeleteBlacklist">Blacklist:</label>
@@ -441,7 +443,7 @@ function DeleteDupInPath(){
<tr> <tr>
<td><form id="titleToDeleteForm" action="javascript:DeleteDupInPath();" target="_self"> <td><form id="titleToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
<label for="titleToDelete" title="Scene having value in title.">Title:</label> <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"> <input type="text" id="titleToDeleteText" name="titleToDelete" title="String to search for in title." placeholder="mature" value="">
<label for="titleToDelete">All:</label> <label for="titleToDelete">All:</label>
<button type="button" id="titleToDelete" title="Delete tagged duplicates with title name including value">Contains</button> <button type="button" id="titleToDelete" title="Delete tagged duplicates with title name including value">Contains</button>
<label for="titleToDeleteBlacklist">Blacklist:</label> <label for="titleToDeleteBlacklist">Blacklist:</label>
@@ -453,7 +455,7 @@ function DeleteDupInPath(){
<tr> <tr>
<td><form id="pathStrToDeleteForm" action="javascript:DeleteDupInPath();" target="_self"> <td><form id="pathStrToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
<label for="pathStrToDelete" pathStr="Scene having value in path.">Path String:</label> <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"> <input type="text" id="pathStrToDeleteText" name="pathStrToDelete" pathStr="String to search for in path." placeholder="blond" value="">
<label for="pathStrToDelete">All:</label> <label for="pathStrToDelete">All:</label>
<button type="button" id="pathStrToDelete" pathStr="Delete tagged duplicates with path having value">Contains</button> <button type="button" id="pathStrToDelete" pathStr="Delete tagged duplicates with path having value">Contains</button>
<label for="pathStrToDeleteBlacklist">Blacklist:</label> <label for="pathStrToDeleteBlacklist">Blacklist:</label>

View File

@@ -43,3 +43,7 @@
- This change was needed because sometimes the browser refuse to open local URL's with params on the URL. - This change was needed because sometimes the browser refuse to open local URL's with params on the URL.
- Using cookies also allows check options status to stay the same after refresh. - Using cookies also allows check options status to stay the same after refresh.
- Added code to [**Advance Duplicate File Deletion Menu**] to delete based on flags. - Added code to [**Advance Duplicate File Deletion Menu**] to delete based on flags.
### 1.0.1
- Change [**Advance Duplicate File Deletion Menu**] default input values to placeholder text.
- Change stash toolbar icon for DupFileManager to a file icon with a video camera.
- Removed **plugin/** from the URL for the Tools-UI menu.

View File

@@ -1,4 +1,4 @@
# FileMonitor: Ver 0.9.2 (By David Maisonave) # FileMonitor: Ver 1.0.0 (By David Maisonave)
FileMonitor is a [Stash](https://github.com/stashapp/stash) plugin with the following two main features: FileMonitor is a [Stash](https://github.com/stashapp/stash) plugin with the following two main features:
@@ -170,3 +170,5 @@ Please use the following link to report FileMonitor bugs:
Please use the following link to report FileMonitor Feature Request:[FileMonitor Feature Reques](https://github.com/David-Maisonave/Axter-Stash/issues/new?assignees=&labels=Enhancement&projects=&template=feature_request_plugin.yml&title=%F0%9F%92%A1%EF%B8%8F%5BEnhancement%5D%3A%5BFileMonitor%5D+Your_Short_title) Please use the following link to report FileMonitor Feature Request:[FileMonitor Feature Reques](https://github.com/David-Maisonave/Axter-Stash/issues/new?assignees=&labels=Enhancement&projects=&template=feature_request_plugin.yml&title=%F0%9F%92%A1%EF%B8%8F%5BEnhancement%5D%3A%5BFileMonitor%5D+Your_Short_title)
Please do **NOT** use the feature request to include any problems associated with errors. Instead use the bug report for error issues. Please do **NOT** use the feature request to include any problems associated with errors. Instead use the bug report for error issues.
### Future Planned Features or Fixes

File diff suppressed because one or more lines are too long

View File

@@ -5,19 +5,20 @@
# Example: python filemonitor.py --url http://localhost:9999 # Example: python filemonitor.py --url http://localhost:9999
try: try:
import ModulesValidate import ModulesValidate
ModulesValidate.modulesInstalled(["stashapp-tools", "watchdog", "schedule", "requests"]) ModulesValidate.modulesInstalled(["stashapp-tools", "watchdog", "schedule", "requests"], silent=True)
except Exception as e: except Exception as e:
import traceback, sys import traceback, sys
tb = traceback.format_exc() tb = traceback.format_exc()
print(f"ModulesValidate Exception. Error: {e}\nTraceBack={tb}", file=sys.stderr) print(f"ModulesValidate Exception. Error: {e}\nTraceBack={tb}", file=sys.stderr)
from StashPluginHelper import StashPluginHelper
import os, sys, time, pathlib, argparse, platform, traceback, logging import os, sys, time, pathlib, argparse, platform, traceback, logging
from StashPluginHelper import StashPluginHelper
from StashPluginHelper import taskQueue from StashPluginHelper import taskQueue
from threading import Lock, Condition from threading import Lock, Condition
from multiprocessing import shared_memory from multiprocessing import shared_memory
from filemonitor_config import config from filemonitor_config import config
from filemonitor_task_examples import task_examples from filemonitor_task_examples import task_examples
from filemonitor_self_unit_test import self_unit_test from filemonitor_self_unit_test import self_unit_test
from datetime import datetime
config['task_scheduler'] = config['task_scheduler'] + task_examples['task_scheduler'] config['task_scheduler'] = config['task_scheduler'] + task_examples['task_scheduler']
if self_unit_test['selfUnitTest_repeat']: if self_unit_test['selfUnitTest_repeat']:
@@ -62,6 +63,13 @@ stash = StashPluginHelper(
maxbytes=5*1024*1024, maxbytes=5*1024*1024,
apiKey=parse_args.apikey apiKey=parse_args.apikey
) )
doJsonReturnModeTypes = ["getFileMonitorRunningStatus"]
doJsonReturn = False
if len(sys.argv) < 2 and stash.PLUGIN_TASK_NAME in doJsonReturnModeTypes:
doJsonReturn = True
stash.log_to_norm = stash.LogTo.FILE
stash.status(logLevel=logging.DEBUG) stash.status(logLevel=logging.DEBUG)
stash.Log(f"\nStarting (__file__={__file__}) (stash.CALLED_AS_STASH_PLUGIN={stash.CALLED_AS_STASH_PLUGIN}) (stash.DEBUG_TRACING={stash.DEBUG_TRACING}) (stash.DRY_RUN={stash.DRY_RUN}) (stash.PLUGIN_TASK_NAME={stash.PLUGIN_TASK_NAME})************************************************") stash.Log(f"\nStarting (__file__={__file__}) (stash.CALLED_AS_STASH_PLUGIN={stash.CALLED_AS_STASH_PLUGIN}) (stash.DEBUG_TRACING={stash.DEBUG_TRACING}) (stash.DRY_RUN={stash.DRY_RUN}) (stash.PLUGIN_TASK_NAME={stash.PLUGIN_TASK_NAME})************************************************")
stash.Trace(f"stash.JSON_INPUT={stash.JSON_INPUT}") stash.Trace(f"stash.JSON_INPUT={stash.JSON_INPUT}")
@@ -72,6 +80,7 @@ signal = Condition(mutex)
shouldUpdate = False shouldUpdate = False
SHAREDMEMORY_NAME = "DavidMaisonaveAxter_FileMonitor" # Unique name for shared memory SHAREDMEMORY_NAME = "DavidMaisonaveAxter_FileMonitor" # Unique name for shared memory
SHAREDMEMORY_SIZE = 4
RECURSIVE = stash.pluginSettings["recursiveDisabled"] == False RECURSIVE = stash.pluginSettings["recursiveDisabled"] == False
SCAN_MODIFIED = stash.pluginConfig["scanModified"] SCAN_MODIFIED = stash.pluginConfig["scanModified"]
RUN_CLEAN_AFTER_DELETE = stash.pluginConfig["runCleanAfterDelete"] RUN_CLEAN_AFTER_DELETE = stash.pluginConfig["runCleanAfterDelete"]
@@ -502,7 +511,7 @@ def start_library_monitor():
global lastScanJob global lastScanJob
try: try:
# Create shared memory buffer which can be used as singleton logic or to get a signal to quit task from external script # Create shared memory buffer which can be used as singleton logic or to get a signal to quit task from external script
shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=True, size=4) shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=True, size=SHAREDMEMORY_SIZE)
except: except:
stash.Error(f"Could not open shared memory map ({SHAREDMEMORY_NAME}). Change File Monitor must be running. Can not run multiple instance of Change File Monitor. Stop FileMonitor before trying to start it again.") stash.Error(f"Could not open shared memory map ({SHAREDMEMORY_NAME}). Change File Monitor must be running. Can not run multiple instance of Change File Monitor. Stop FileMonitor before trying to start it again.")
return return
@@ -743,7 +752,7 @@ def stop_library_monitor():
os.remove(SPECIAL_FILE_NAME) os.remove(SPECIAL_FILE_NAME)
stash.Trace("Opening shared memory map.") stash.Trace("Opening shared memory map.")
try: try:
shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=False, size=4) shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=False, size=SHAREDMEMORY_SIZE)
except: except:
# If FileMonitor is running as plugin, then it's expected behavior that SharedMemory will not be available. # If FileMonitor is running as plugin, then it's expected behavior that SharedMemory will not be available.
stash.Trace(f"Could not open shared memory map ({SHAREDMEMORY_NAME}). Change File Monitor must not be running.") stash.Trace(f"Could not open shared memory map ({SHAREDMEMORY_NAME}). Change File Monitor must not be running.")
@@ -759,7 +768,7 @@ def stop_library_monitor():
def start_library_monitor_service(): def start_library_monitor_service():
# First check if FileMonitor is already running # First check if FileMonitor is already running
try: try:
shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=False, size=4) shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=False, size=SHAREDMEMORY_SIZE)
shm_a.close() shm_a.close()
shm_a.unlink() shm_a.unlink()
stash.Error("FileMonitor is already running. Need to stop FileMonitor before trying to start it again.") stash.Error("FileMonitor is already running. Need to stop FileMonitor before trying to start it again.")
@@ -846,6 +855,21 @@ def manageTagggedScenes(clearTag=True):
runTypeID=0 runTypeID=0
runTypeName=["NothingToDo", "stop_library_monitor", "StartFileMonitorAsAServiceTaskID", "StartFileMonitorAsAPluginTaskID", "CommandLineStartLibMonitor"] runTypeName=["NothingToDo", "stop_library_monitor", "StartFileMonitorAsAServiceTaskID", "StartFileMonitorAsAPluginTaskID", "CommandLineStartLibMonitor"]
def getFileMonitorRunningStatus():
FileMonitorStatus = "NOT running"
try:
shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=False, size=SHAREDMEMORY_SIZE)
shm_a.close()
shm_a.unlink()
FileMonitorStatus = "RUNNING"
stash.Log("FileMonitor is running...")
except:
pass
stash.Log("FileMonitor is NOT running!!!")
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete")
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', FileMonitorStatus:'{FileMonitorStatus}'" + "}")
try: try:
if parse_args.stop or parse_args.restart or stash.PLUGIN_TASK_NAME == "stop_library_monitor": if parse_args.stop or parse_args.restart or stash.PLUGIN_TASK_NAME == "stop_library_monitor":
runTypeID=1 runTypeID=1
@@ -876,6 +900,9 @@ try:
runTypeID=7 runTypeID=7
manageTagggedScenes() manageTagggedScenes()
stash.Trace(f"{CLEAR_SYNC_LIBRARY_TAG} EXIT") stash.Trace(f"{CLEAR_SYNC_LIBRARY_TAG} EXIT")
elif stash.PLUGIN_TASK_NAME == "getFileMonitorRunningStatus":
getFileMonitorRunningStatus()
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
elif not stash.CALLED_AS_STASH_PLUGIN: elif not stash.CALLED_AS_STASH_PLUGIN:
runTypeID=4 runTypeID=4
if parse_args.kill_job_task_que != None and parse_args.kill_job_task_que != "": if parse_args.kill_job_task_que != None and parse_args.kill_job_task_que != "":
@@ -889,7 +916,8 @@ try:
except Exception as e: except Exception as e:
tb = traceback.format_exc() tb = traceback.format_exc()
stash.Error(f"Exception while running FileMonitor. runType='{runTypeName[runTypeID]}'; Error: {e}\nTraceBack={tb}") stash.Error(f"Exception while running FileMonitor. runType='{runTypeName[runTypeID]}'; Error: {e}\nTraceBack={tb}")
if doJsonReturn:
sys.stdout.write("{" + f"Exception : '{e}; See log file for TraceBack' " + "}")
stash.Trace("\n*********************************\nEXITING ***********************\n*********************************") stash.Trace("\n*********************************\nEXITING ***********************\n*********************************")
# ToDo: Add option to add path to library if path not included when calling metadata_scan # ToDo: Add option to add path to library if path not included when calling metadata_scan

View File

@@ -1,7 +1,10 @@
name: FileMonitor name: FileMonitor
description: Monitors the Stash library folders, and updates Stash if any changes occurs in the Stash library paths. description: Monitors the Stash library folders, and updates Stash if any changes occurs in the Stash library paths.
version: 0.9.2 version: 1.0.0
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/FileMonitor url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/FileMonitor
ui:
javascript:
- filemonitor.js
settings: settings:
recursiveDisabled: recursiveDisabled:
displayName: No Recursive displayName: No Recursive

View File

@@ -0,0 +1,4 @@
##### This page was added starting on version 1.0.0 to keep track of newly added features between versions.
### 1.0.0
- Added Tools-UI option to get FileMonitor running status.
- Added Stash toolbar icon to get FileMonitor running status.

View File

@@ -85,3 +85,5 @@ Please use the following link to report RenameFile Feature Request:[RenameFile F
Please do **NOT** use the feature request to include any problems associated with errors. Instead use the bug report for error issues. Please do **NOT** use the feature request to include any problems associated with errors. Instead use the bug report for error issues.
**Note:** This script is **largely** based on the [Renamer](https://github.com/Serechops/Serechops-Stash/tree/main/plugins/Renamer) script. **Note:** This script is **largely** based on the [Renamer](https://github.com/Serechops/Serechops-Stash/tree/main/plugins/Renamer) script.
### Future Planned Features or Fixes