UNPKG

3.76 kBJavaScriptView Raw
1const fs = require('fs-extra');
2const yaml = require('js-yaml');
3const { CONST, XSet } = require('./utils');
4const { Config } = require('./config');
5const { Subscription } = require('./dmhy/subscription');
6
7const { defaultDatabasePath, defaultISubsDir } = CONST;
8
9// isub 資料庫自存的 Supscriptions 紀錄,作為初始化用,與訂閱用的不同
10// dbpath 存 SID 與 Threads 的 Map
11// sub subscription 的縮寫
12
13/**
14 * @class Database
15 * @member {string} dbpath
16 * @member {string} isubsDir
17 * @member {Config} config
18 * @member {Subscription[]} subscriptions
19 */
20class Database {
21 /**
22 * Creates an instance of Database.
23 * @param {any} options [{ dbpath = defaultDatabasePath, isubsDir = defaultISubsDir, config = new Config() }={}]
24 * @memberof Database
25 */
26 constructor({ dbpath = defaultDatabasePath, isubsDir = defaultISubsDir, config = new Config() } = {}) {
27 this.dbpath = dbpath;
28 if (!fs.existsSync(this.dbpath)) {
29 fs.ensureFileSync(this.dbpath);
30 const empty = { };
31 fs.writeFileSync(this.dbpath, JSON.stringify(empty));
32 }
33
34 this.isubsDir = isubsDir;
35 if (!fs.existsSync(isubsDir)) {
36 fs.mkdirpSync(isubsDir);
37 }
38
39 this.config = config;
40 if (!(config instanceof Config)) {
41 throw new Error(`Bad config`);
42 }
43
44 const threadsMap = JSON.parse(fs.readFileSync(this.dbpath, 'utf-8'));
45 const isubs = fs.readdirSync(this.isubsDir);
46 this.subscriptions = isubs.map((isub) => new Subscription(`${this.isubsDir}/${isub}`));
47 this.subscriptions.forEach((sub) => {
48 sub.loadThreads(threadsMap[sub.sid]);
49 });
50 }
51
52 /**
53 * @param {Subscription} sub
54 * @return {boolean} success
55 * @memberof Database
56 */
57 add(sub) {
58 if (!(sub instanceof Subscription)) {
59 throw new TypeError('Parameter should be a Subscription.');
60 }
61 sub.generateSid(this.subscriptions.map((s) => s.sid));
62 this.subscriptions.push(sub);
63 return true;
64 }
65
66 /**
67 * @param {string} sid
68 * @return {?Subscription} Subscription removed
69 * @memberof Database
70 */
71 remove(sid) {
72 const index = this.subscriptions.findIndex((sub) => {
73 return sub.sid === sid;
74 });
75 if (index >= 0) {
76 return this.subscriptions.splice(index, 1)[0];
77 }
78 return null;
79 }
80
81 /**
82 * @memberof Database
83 */
84 save() {
85 const threadsMap = this.subscriptions.reduce((prev, sub) => {
86 prev[sub.sid] = sub.threads;
87 return prev;
88 }, {});
89 fs.writeFileSync(this.dbpath, JSON.stringify(threadsMap));
90
91 fs.removeSync(this.isubsDir);
92 fs.mkdirsSync(this.isubsDir);
93
94 this.subscriptions.forEach((sub) => {
95 let { sid, title, keywords, unkeywords, episodeParser, userBlacklistPatterns } = sub;
96 if (episodeParser) {
97 episodeParser = episodeParser.toString();
98 }
99 userBlacklistPatterns = userBlacklistPatterns.map((ubp) => ubp.toString());
100 const yamlData = yaml.safeDump({ sid, title, keywords, unkeywords, episodeParser, userBlacklistPatterns });
101 fs.writeFileSync(`${this.isubsDir}/${sid}.yml`, yamlData);
102 });
103 }
104
105 /**
106 * @param {Subscription} sub
107 * @return {?Subscription} Subscription
108 * @memberof Database
109 */
110 find(sub) {
111 if (sub.sid) {
112 return this.subscriptions.find((thissub) => thissub.sid === sub.sid) || null;
113 }
114 return this.subscriptions.find((thissub) => {
115 return thissub.title === sub.title &&
116 (new XSet(thissub.keywords)).isSuperset(sub.keywords) &&
117 (new XSet(thissub.unkeywords)).isSuperset(sub.unkeywords);
118 }) || null;
119 }
120
121 /**
122 * @memberof Database
123 */
124 sort() {
125 this.subscriptions.forEach((sub) => sub.sort());
126 this.subscriptions.sort((a, b) => b.latest - a.latest);
127 }
128}
129
130exports.Database = Database;