retold-remote 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/retold-remote.css +3 -0
- package/docs/README.md +181 -0
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +10 -0
- package/docs/_topbar.md +3 -0
- package/docs/audio-viewer.md +133 -0
- package/docs/ebook-reader.md +90 -0
- package/docs/image-viewer.md +90 -0
- package/docs/server-setup.md +262 -0
- package/docs/video-viewer.md +134 -0
- package/html/docs.html +59 -0
- package/html/index.html +1 -1
- package/package.json +20 -6
- package/source/Pict-Application-RetoldRemote.js +122 -2
- package/source/RetoldRemote-ExtensionMaps.js +33 -0
- package/source/cli/RetoldRemote-Server-Setup.js +211 -67
- package/source/cli/commands/RetoldRemote-Command-Serve.js +5 -26
- package/source/providers/Pict-Provider-AISortManager.js +456 -0
- package/source/providers/Pict-Provider-CollectionManager.js +1200 -0
- package/source/providers/Pict-Provider-FormattingUtilities.js +109 -0
- package/source/providers/Pict-Provider-GalleryFilterSort.js +2 -11
- package/source/providers/Pict-Provider-GalleryNavigation.js +187 -409
- package/source/providers/Pict-Provider-RetoldRemoteIcons.js +52 -0
- package/source/providers/Pict-Provider-ToastNotification.js +96 -0
- package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +88 -0
- package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +190 -0
- package/source/providers/keyboard-handlers/KeyHandler-Sidebar.js +65 -0
- package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +57 -0
- package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +197 -0
- package/source/server/RetoldRemote-AISortService.js +879 -0
- package/source/server/RetoldRemote-ArchiveService.js +2 -12
- package/source/server/RetoldRemote-AudioWaveformService.js +7 -16
- package/source/server/RetoldRemote-CollectionService.js +843 -0
- package/source/server/RetoldRemote-EbookService.js +7 -16
- package/source/server/RetoldRemote-FileOperationService.js +560 -0
- package/source/server/RetoldRemote-MediaService.js +15 -14
- package/source/server/RetoldRemote-MetadataCache.js +411 -0
- package/source/server/RetoldRemote-ParimeCache.js +349 -0
- package/source/server/RetoldRemote-ThumbnailCache.js +52 -20
- package/source/server/RetoldRemote-VideoFrameService.js +7 -15
- package/source/views/PictView-Remote-AudioExplorer.js +10 -43
- package/source/views/PictView-Remote-CollectionsPanel.js +1486 -0
- package/source/views/PictView-Remote-Gallery.js +237 -44
- package/source/views/PictView-Remote-ImageViewer.js +1 -34
- package/source/views/PictView-Remote-Layout.js +411 -20
- package/source/views/PictView-Remote-MediaViewer.js +311 -28
- package/source/views/PictView-Remote-SettingsPanel.js +275 -133
- package/source/views/PictView-Remote-TopBar.js +701 -14
- package/source/views/PictView-Remote-VLCSetup.js +8 -39
- package/source/views/PictView-Remote-VideoExplorer.js +20 -54
- package/web-application/css/docuserve.css +73 -0
- package/web-application/css/retold-remote.css +3 -0
- package/web-application/docs/README.md +181 -0
- package/web-application/docs/_cover.md +14 -0
- package/web-application/docs/_sidebar.md +10 -0
- package/web-application/docs/_topbar.md +3 -0
- package/web-application/docs/audio-viewer.md +133 -0
- package/web-application/docs/ebook-reader.md +90 -0
- package/web-application/docs/image-viewer.md +90 -0
- package/web-application/docs/server-setup.md +262 -0
- package/web-application/docs/video-viewer.md +134 -0
- package/web-application/docs.html +59 -0
- package/web-application/index.html +1 -1
- package/web-application/js/pict-docuserve.min.js +58 -0
- package/web-application/js/pict.min.js +2 -2
- package/web-application/js/pict.min.js.map +1 -1
- package/web-application/retold-remote.js +2652 -970
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +60 -13
- package/web-application/retold-remote.min.js.map +1 -1
- package/server.js +0 -43
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retold Remote -- AI Sort Manager Provider
|
|
3
|
+
*
|
|
4
|
+
* Client-side state management and API communication for the
|
|
5
|
+
* AI-powered file sorting feature. Manages AI endpoint settings,
|
|
6
|
+
* connection testing, folder scanning, and sort plan generation.
|
|
7
|
+
*
|
|
8
|
+
* Sort plans are created as operation-plan collections, so all
|
|
9
|
+
* preview, editing, and execution happens through the existing
|
|
10
|
+
* collections infrastructure.
|
|
11
|
+
*
|
|
12
|
+
* Settings are persisted via localStorage alongside other RetoldRemote
|
|
13
|
+
* settings.
|
|
14
|
+
*
|
|
15
|
+
* @license MIT
|
|
16
|
+
*/
|
|
17
|
+
const libPictProvider = require('pict-provider');
|
|
18
|
+
|
|
19
|
+
const _DefaultProviderConfiguration =
|
|
20
|
+
{
|
|
21
|
+
ProviderIdentifier: 'RetoldRemote-AISortManager',
|
|
22
|
+
AutoInitialize: true,
|
|
23
|
+
AutoSolveWithApp: false
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class AISortManagerProvider extends libPictProvider
|
|
27
|
+
{
|
|
28
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
29
|
+
{
|
|
30
|
+
super(pFable, pOptions, pServiceHash);
|
|
31
|
+
|
|
32
|
+
// Track in-progress operations
|
|
33
|
+
this._generating = false;
|
|
34
|
+
this._scanning = false;
|
|
35
|
+
this._testingConnection = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// -- State Accessors --------------------------------------------------
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Shortcut to the RetoldRemote AppData namespace.
|
|
42
|
+
*/
|
|
43
|
+
_getRemote()
|
|
44
|
+
{
|
|
45
|
+
return this.pict.AppData.RetoldRemote;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get the AI sort settings from AppData.
|
|
50
|
+
*/
|
|
51
|
+
_getSettings()
|
|
52
|
+
{
|
|
53
|
+
let tmpRemote = this._getRemote();
|
|
54
|
+
if (!tmpRemote.AISortSettings)
|
|
55
|
+
{
|
|
56
|
+
tmpRemote.AISortSettings =
|
|
57
|
+
{
|
|
58
|
+
AIEndpoint: 'http://localhost:11434',
|
|
59
|
+
AIModel: 'llama3.1',
|
|
60
|
+
AIProvider: 'ollama',
|
|
61
|
+
NamingTemplate: '{artist}/{album}/{track} - {title}'
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return tmpRemote.AISortSettings;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the toast notification provider.
|
|
69
|
+
*/
|
|
70
|
+
_getToast()
|
|
71
|
+
{
|
|
72
|
+
return this.pict.providers['RetoldRemote-ToastNotification'];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the collection manager provider.
|
|
77
|
+
*/
|
|
78
|
+
_getCollectionManager()
|
|
79
|
+
{
|
|
80
|
+
return this.pict.providers['RetoldRemote-CollectionManager'];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// -- Settings Management ----------------------------------------------
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Update AI sort settings.
|
|
87
|
+
*
|
|
88
|
+
* @param {object} pSettings - Settings object with any of: AIEndpoint, AIModel, AIProvider, NamingTemplate
|
|
89
|
+
*/
|
|
90
|
+
updateSettings(pSettings)
|
|
91
|
+
{
|
|
92
|
+
let tmpSettings = this._getSettings();
|
|
93
|
+
|
|
94
|
+
if (pSettings.AIEndpoint !== undefined)
|
|
95
|
+
{
|
|
96
|
+
tmpSettings.AIEndpoint = pSettings.AIEndpoint;
|
|
97
|
+
}
|
|
98
|
+
if (pSettings.AIModel !== undefined)
|
|
99
|
+
{
|
|
100
|
+
tmpSettings.AIModel = pSettings.AIModel;
|
|
101
|
+
}
|
|
102
|
+
if (pSettings.AIProvider !== undefined)
|
|
103
|
+
{
|
|
104
|
+
tmpSettings.AIProvider = pSettings.AIProvider;
|
|
105
|
+
}
|
|
106
|
+
if (pSettings.NamingTemplate !== undefined)
|
|
107
|
+
{
|
|
108
|
+
tmpSettings.NamingTemplate = pSettings.NamingTemplate;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Persist settings
|
|
112
|
+
this.pict.PictApplication.saveSettings();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// -- API Methods ------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Test the AI endpoint connection.
|
|
119
|
+
*
|
|
120
|
+
* @param {Function} [fCallback] - Optional callback(pError, pResult)
|
|
121
|
+
*/
|
|
122
|
+
testConnection(fCallback)
|
|
123
|
+
{
|
|
124
|
+
let tmpSelf = this;
|
|
125
|
+
let tmpCallback = (typeof fCallback === 'function') ? fCallback : () => {};
|
|
126
|
+
|
|
127
|
+
if (this._testingConnection)
|
|
128
|
+
{
|
|
129
|
+
return tmpCallback(new Error('Connection test already in progress'));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this._testingConnection = true;
|
|
133
|
+
let tmpSettings = this._getSettings();
|
|
134
|
+
|
|
135
|
+
let tmpToast = this._getToast();
|
|
136
|
+
|
|
137
|
+
fetch('/api/ai-sort/test-connection',
|
|
138
|
+
{
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify(
|
|
142
|
+
{
|
|
143
|
+
AIEndpoint: tmpSettings.AIEndpoint,
|
|
144
|
+
AIModel: tmpSettings.AIModel,
|
|
145
|
+
AIProvider: tmpSettings.AIProvider
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
.then((pResponse) => pResponse.json())
|
|
149
|
+
.then((pData) =>
|
|
150
|
+
{
|
|
151
|
+
tmpSelf._testingConnection = false;
|
|
152
|
+
|
|
153
|
+
if (pData.Success)
|
|
154
|
+
{
|
|
155
|
+
if (tmpToast)
|
|
156
|
+
{
|
|
157
|
+
tmpToast.show('AI connected (' + pData.ResponseTime + 'ms)');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else
|
|
161
|
+
{
|
|
162
|
+
if (tmpToast)
|
|
163
|
+
{
|
|
164
|
+
tmpToast.show('AI connection failed: ' + (pData.Error || 'Unknown error'));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Update the test button state if settings panel is open
|
|
169
|
+
tmpSelf._updateTestButtonState(pData.Success);
|
|
170
|
+
|
|
171
|
+
return tmpCallback(null, pData);
|
|
172
|
+
})
|
|
173
|
+
.catch((pError) =>
|
|
174
|
+
{
|
|
175
|
+
tmpSelf._testingConnection = false;
|
|
176
|
+
|
|
177
|
+
if (tmpToast)
|
|
178
|
+
{
|
|
179
|
+
tmpToast.show('AI connection error: ' + pError.message);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
tmpSelf._updateTestButtonState(false);
|
|
183
|
+
|
|
184
|
+
return tmpCallback(pError);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Scan a folder for audio files and return metadata.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} pPath - Folder path relative to content root
|
|
192
|
+
* @param {boolean} [pRecursive] - Scan subdirectories
|
|
193
|
+
* @param {Function} [fCallback] - Optional callback(pError, pResult)
|
|
194
|
+
*/
|
|
195
|
+
scanFolder(pPath, pRecursive, fCallback)
|
|
196
|
+
{
|
|
197
|
+
let tmpSelf = this;
|
|
198
|
+
let tmpCallback = (typeof fCallback === 'function') ? fCallback : () => {};
|
|
199
|
+
|
|
200
|
+
if (this._scanning)
|
|
201
|
+
{
|
|
202
|
+
return tmpCallback(new Error('Scan already in progress'));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this._scanning = true;
|
|
206
|
+
|
|
207
|
+
fetch('/api/ai-sort/scan',
|
|
208
|
+
{
|
|
209
|
+
method: 'POST',
|
|
210
|
+
headers: { 'Content-Type': 'application/json' },
|
|
211
|
+
body: JSON.stringify(
|
|
212
|
+
{
|
|
213
|
+
Path: pPath || '',
|
|
214
|
+
Recursive: pRecursive || false
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
.then((pResponse) => pResponse.json())
|
|
218
|
+
.then((pData) =>
|
|
219
|
+
{
|
|
220
|
+
tmpSelf._scanning = false;
|
|
221
|
+
|
|
222
|
+
let tmpToast = tmpSelf._getToast();
|
|
223
|
+
if (tmpToast)
|
|
224
|
+
{
|
|
225
|
+
if (pData.Success)
|
|
226
|
+
{
|
|
227
|
+
tmpToast.show('Scanned ' + pData.FileCount + ' audio files');
|
|
228
|
+
}
|
|
229
|
+
else
|
|
230
|
+
{
|
|
231
|
+
tmpToast.show('Scan failed: ' + (pData.Error || 'Unknown error'));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return tmpCallback(null, pData);
|
|
236
|
+
})
|
|
237
|
+
.catch((pError) =>
|
|
238
|
+
{
|
|
239
|
+
tmpSelf._scanning = false;
|
|
240
|
+
|
|
241
|
+
let tmpToast = tmpSelf._getToast();
|
|
242
|
+
if (tmpToast)
|
|
243
|
+
{
|
|
244
|
+
tmpToast.show('Scan error: ' + pError.message);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return tmpCallback(pError);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Generate a sort plan for a folder.
|
|
253
|
+
*
|
|
254
|
+
* Creates an operation-plan collection and opens it in the
|
|
255
|
+
* collections panel for preview and editing.
|
|
256
|
+
*
|
|
257
|
+
* @param {string} pPath - Folder path relative to content root
|
|
258
|
+
* @param {Function} [fCallback] - Optional callback(pError, pResult)
|
|
259
|
+
*/
|
|
260
|
+
generateSortPlan(pPath, fCallback)
|
|
261
|
+
{
|
|
262
|
+
let tmpSelf = this;
|
|
263
|
+
let tmpCallback = (typeof fCallback === 'function') ? fCallback : () => {};
|
|
264
|
+
|
|
265
|
+
if (this._generating)
|
|
266
|
+
{
|
|
267
|
+
let tmpToast = this._getToast();
|
|
268
|
+
if (tmpToast)
|
|
269
|
+
{
|
|
270
|
+
tmpToast.show('Sort plan generation already in progress...');
|
|
271
|
+
}
|
|
272
|
+
return tmpCallback(new Error('Generation already in progress'));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this._generating = true;
|
|
276
|
+
let tmpSettings = this._getSettings();
|
|
277
|
+
|
|
278
|
+
let tmpToast = this._getToast();
|
|
279
|
+
if (tmpToast)
|
|
280
|
+
{
|
|
281
|
+
tmpToast.show('Generating sort plan...');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
fetch('/api/ai-sort/generate-plan',
|
|
285
|
+
{
|
|
286
|
+
method: 'POST',
|
|
287
|
+
headers: { 'Content-Type': 'application/json' },
|
|
288
|
+
body: JSON.stringify(
|
|
289
|
+
{
|
|
290
|
+
Path: pPath || '',
|
|
291
|
+
NamingTemplate: tmpSettings.NamingTemplate,
|
|
292
|
+
Recursive: true,
|
|
293
|
+
AIEndpoint: tmpSettings.AIEndpoint,
|
|
294
|
+
AIModel: tmpSettings.AIModel,
|
|
295
|
+
AIProvider: tmpSettings.AIProvider
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
.then((pResponse) => pResponse.json())
|
|
299
|
+
.then((pData) =>
|
|
300
|
+
{
|
|
301
|
+
tmpSelf._generating = false;
|
|
302
|
+
|
|
303
|
+
if (!pData.Success)
|
|
304
|
+
{
|
|
305
|
+
if (tmpToast)
|
|
306
|
+
{
|
|
307
|
+
tmpToast.show('Sort plan failed: ' + (pData.Error || pData.Message || 'Unknown error'));
|
|
308
|
+
}
|
|
309
|
+
return tmpCallback(new Error(pData.Error || pData.Message));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!pData.CollectionGUID)
|
|
313
|
+
{
|
|
314
|
+
if (tmpToast)
|
|
315
|
+
{
|
|
316
|
+
tmpToast.show(pData.Message || 'No audio files found');
|
|
317
|
+
}
|
|
318
|
+
return tmpCallback(null, pData);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Open the generated collection in the panel
|
|
322
|
+
let tmpCollManager = tmpSelf._getCollectionManager();
|
|
323
|
+
if (tmpCollManager)
|
|
324
|
+
{
|
|
325
|
+
let tmpRemote = tmpSelf._getRemote();
|
|
326
|
+
tmpRemote.CollectionsPanelMode = 'detail';
|
|
327
|
+
|
|
328
|
+
if (!tmpRemote.CollectionsPanelOpen)
|
|
329
|
+
{
|
|
330
|
+
tmpCollManager.togglePanel();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
tmpCollManager.fetchCollection(pData.CollectionGUID);
|
|
334
|
+
tmpCollManager.fetchCollections();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (tmpToast)
|
|
338
|
+
{
|
|
339
|
+
let tmpMsg = 'Sort plan created: ' + pData.TotalFiles + ' files';
|
|
340
|
+
if (pData.TaggedFiles > 0)
|
|
341
|
+
{
|
|
342
|
+
tmpMsg += ' (' + pData.TaggedFiles + ' by tags';
|
|
343
|
+
if (pData.AIFiles > 0)
|
|
344
|
+
{
|
|
345
|
+
tmpMsg += ', ' + pData.AIFiles + ' by AI';
|
|
346
|
+
}
|
|
347
|
+
tmpMsg += ')';
|
|
348
|
+
}
|
|
349
|
+
tmpToast.show(tmpMsg);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return tmpCallback(null, pData);
|
|
353
|
+
})
|
|
354
|
+
.catch((pError) =>
|
|
355
|
+
{
|
|
356
|
+
tmpSelf._generating = false;
|
|
357
|
+
|
|
358
|
+
if (tmpToast)
|
|
359
|
+
{
|
|
360
|
+
tmpToast.show('Sort plan error: ' + pError.message);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return tmpCallback(pError);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// -- UI Helpers --------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Update the test connection button state in the settings panel.
|
|
371
|
+
*
|
|
372
|
+
* @param {boolean} pSuccess - Whether the test was successful
|
|
373
|
+
*/
|
|
374
|
+
_updateTestButtonState(pSuccess)
|
|
375
|
+
{
|
|
376
|
+
let tmpBtn = document.getElementById('RetoldRemote-AISortTestBtn');
|
|
377
|
+
if (!tmpBtn)
|
|
378
|
+
{
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
tmpBtn.disabled = false;
|
|
383
|
+
|
|
384
|
+
if (pSuccess === true)
|
|
385
|
+
{
|
|
386
|
+
tmpBtn.style.borderColor = 'var(--retold-accent)';
|
|
387
|
+
tmpBtn.style.color = 'var(--retold-accent)';
|
|
388
|
+
tmpBtn.textContent = 'Connected';
|
|
389
|
+
setTimeout(() =>
|
|
390
|
+
{
|
|
391
|
+
tmpBtn.textContent = 'Test Connection';
|
|
392
|
+
tmpBtn.style.borderColor = '';
|
|
393
|
+
tmpBtn.style.color = '';
|
|
394
|
+
}, 3000);
|
|
395
|
+
}
|
|
396
|
+
else if (pSuccess === false)
|
|
397
|
+
{
|
|
398
|
+
tmpBtn.style.borderColor = 'var(--retold-danger-muted)';
|
|
399
|
+
tmpBtn.style.color = 'var(--retold-danger-muted)';
|
|
400
|
+
tmpBtn.textContent = 'Failed';
|
|
401
|
+
setTimeout(() =>
|
|
402
|
+
{
|
|
403
|
+
tmpBtn.textContent = 'Test Connection';
|
|
404
|
+
tmpBtn.style.borderColor = '';
|
|
405
|
+
tmpBtn.style.color = '';
|
|
406
|
+
}, 3000);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Get a preview of the naming template with sample data.
|
|
412
|
+
*
|
|
413
|
+
* @param {string} pTemplate - Naming template
|
|
414
|
+
* @returns {string} Preview string
|
|
415
|
+
*/
|
|
416
|
+
getTemplatePreview(pTemplate)
|
|
417
|
+
{
|
|
418
|
+
let tmpTemplate = pTemplate || '{artist}/{album}/{track} - {title}';
|
|
419
|
+
let tmpPreview = tmpTemplate
|
|
420
|
+
.replace(/\{artist\}/gi, 'Pink Floyd')
|
|
421
|
+
.replace(/\{album\}/gi, 'Dark Side of the Moon')
|
|
422
|
+
.replace(/\{title\}/gi, 'Time')
|
|
423
|
+
.replace(/\{track\}/gi, '04')
|
|
424
|
+
.replace(/\{year\}/gi, '1973')
|
|
425
|
+
.replace(/\{genre\}/gi, 'Progressive Rock');
|
|
426
|
+
|
|
427
|
+
return tmpPreview + '.mp3';
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Whether the AI sort button should be visible for the current folder.
|
|
432
|
+
*
|
|
433
|
+
* Returns true if we're in gallery mode (browsing a folder).
|
|
434
|
+
*
|
|
435
|
+
* @returns {boolean}
|
|
436
|
+
*/
|
|
437
|
+
isAvailable()
|
|
438
|
+
{
|
|
439
|
+
let tmpRemote = this._getRemote();
|
|
440
|
+
return tmpRemote.ActiveMode === 'gallery';
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Whether a generation is currently in progress.
|
|
445
|
+
*
|
|
446
|
+
* @returns {boolean}
|
|
447
|
+
*/
|
|
448
|
+
isGenerating()
|
|
449
|
+
{
|
|
450
|
+
return this._generating;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
AISortManagerProvider.default_configuration = _DefaultProviderConfiguration;
|
|
455
|
+
|
|
456
|
+
module.exports = AISortManagerProvider;
|