Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aandrew-me
GitHub Repository: aandrew-me/ytDownloader
Path: blob/main/src/history.js
448 views
1
/**
2
* Download History Manager
3
*/
4
5
const fs = require("fs");
6
const path = require("path");
7
const crypto = require("crypto");
8
const {app} = require("electron");
9
10
class DownloadHistory {
11
constructor() {
12
this.historyFile = path.join(
13
app.getPath("userData"),
14
"download_history.json"
15
);
16
this.maxHistoryItems = 800;
17
this.history = [];
18
this.initialized = this._loadHistory().then((history) => {
19
this.history = history;
20
});
21
}
22
_generateUniqueId() {
23
return crypto.randomUUID();
24
}
25
26
async _loadHistory() {
27
try {
28
if (fs.existsSync(this.historyFile)) {
29
const data = await fs.promises.readFile(
30
this.historyFile,
31
"utf8"
32
);
33
return JSON.parse(data) || [];
34
}
35
} catch (error) {
36
console.error("Error loading history:", error);
37
}
38
return [];
39
}
40
41
async _saveHistory() {
42
try {
43
await fs.promises.writeFile(
44
this.historyFile,
45
JSON.stringify(this.history, null, 2)
46
);
47
} catch (error) {
48
console.error("Error saving history:", error);
49
}
50
}
51
52
async addDownload(downloadInfo) {
53
await this.initialized;
54
55
const historyItem = {
56
id: this._generateUniqueId(),
57
title: downloadInfo.title || "Unknown",
58
url: downloadInfo.url || "",
59
filename: downloadInfo.filename || "",
60
filePath: downloadInfo.filePath || "",
61
fileSize: downloadInfo.fileSize || 0,
62
format: downloadInfo.format || "unknown",
63
thumbnail: downloadInfo.thumbnail || "",
64
duration: downloadInfo.duration || 0,
65
downloadDate: new Date().toISOString(),
66
timestamp: Date.now(),
67
};
68
69
// Add to beginning for most recent first
70
this.history.unshift(historyItem);
71
72
// Keep only recent items
73
if (this.history.length > this.maxHistoryItems) {
74
this.history = this.history.slice(0, this.maxHistoryItems);
75
}
76
77
await this._saveHistory();
78
return historyItem;
79
}
80
81
async getHistory() {
82
await this.initialized;
83
return this.history;
84
}
85
86
async getFilteredHistory(options = {}) {
87
await this.initialized;
88
89
let filtered = [...this.history];
90
91
if (options.format) {
92
filtered = filtered.filter(
93
(item) =>
94
item.format.toLowerCase() === options.format.toLowerCase()
95
);
96
}
97
98
if (options.searchTerm) {
99
const term = options.searchTerm.toLowerCase();
100
filtered = filtered.filter(
101
(item) =>
102
item.title.toLowerCase().includes(term) ||
103
item.url.toLowerCase().includes(term)
104
);
105
}
106
107
if (options.limit) {
108
filtered = filtered.slice(0, options.limit);
109
}
110
111
return filtered;
112
}
113
114
async getHistoryItem(id) {
115
await this.initialized;
116
return this.history.find((item) => item.id === id) || null;
117
}
118
119
async removeHistoryItem(id) {
120
await this.initialized;
121
122
const index = this.history.findIndex((item) => item.id === id);
123
if (index !== -1) {
124
this.history.splice(index, 1);
125
await this._saveHistory();
126
return true;
127
}
128
return false;
129
}
130
131
async clearHistory() {
132
await this.initialized;
133
134
this.history = [];
135
await this._saveHistory();
136
}
137
138
async getStats() {
139
await this.initialized;
140
141
const stats = {
142
totalDownloads: this.history.length,
143
totalSize: 0,
144
byFormat: {},
145
oldestDownload: null,
146
newestDownload: null,
147
};
148
149
this.history.forEach((item) => {
150
stats.totalSize += item.fileSize || 0;
151
152
const fmt = item.format.toLowerCase();
153
stats.byFormat[fmt] = (stats.byFormat[fmt] || 0) + 1;
154
});
155
156
if (this.history.length > 0) {
157
stats.newestDownload = this.history[0];
158
stats.oldestDownload = this.history[this.history.length - 1];
159
}
160
161
return stats;
162
}
163
164
async exportAsJSON() {
165
await this.initialized;
166
return JSON.stringify(this.history, null, 2);
167
}
168
169
_sanitizeCSVField(value) {
170
if (value == null) {
171
value = "";
172
}
173
174
const stringValue = String(value);
175
176
let sanitized = stringValue.replace(/"/g, '""');
177
178
const dangerousChars = ["=", "+", "-", "@"];
179
if (sanitized.length > 0 && dangerousChars.includes(sanitized[0])) {
180
sanitized = "'" + sanitized;
181
}
182
183
return `"${sanitized}"`;
184
}
185
186
async exportAsCSV() {
187
await this.initialized;
188
189
if (this.history.length === 0) return "No history to export\n";
190
191
const headers = [
192
"Title",
193
"URL",
194
"Filename",
195
"Format",
196
"File Size (bytes)",
197
"Download Date",
198
];
199
const rows = this.history.map((item) => [
200
this._sanitizeCSVField(item.title),
201
this._sanitizeCSVField(item.url),
202
this._sanitizeCSVField(item.filename),
203
this._sanitizeCSVField(item.format),
204
this._sanitizeCSVField(item.fileSize),
205
this._sanitizeCSVField(item.downloadDate),
206
]);
207
208
return (
209
headers.join(",") +
210
"\n" +
211
rows.map((row) => row.join(",")).join("\n")
212
);
213
}
214
}
215
216
module.exports = DownloadHistory;
217
218