1- import { Mutex } from ' async-mutex' ;
2- import * as Mega from ' megajs' ;
3- import { Config , MegaAccount , UploadMode , UploadQuery } from ' ./types' ;
4- import database from ' ./database' ;
5- import { Readable } from ' stream' ;
1+ import { Mutex } from " async-mutex" ;
2+ import * as Mega from " megajs" ;
3+ import { Config , MegaAccount , UploadMode , UploadQuery } from " ./types" ;
4+ import database from " ./database" ;
5+ import { Readable } from " stream" ;
66
77function streamToBuffer ( stream : NodeJS . ReadableStream ) : Promise < Buffer > {
88 return new Promise ( ( resolve , reject ) => {
99 const chunks : Buffer [ ] = [ ] ;
10- stream . on ( ' data' , ( chunk ) => chunks . push ( chunk ) ) ;
11- stream . on ( ' end' , ( ) => resolve ( Buffer . concat ( chunks ) ) ) ;
12- stream . on ( ' error' , ( err ) => {
13- console . error ( ' Error stream:' , err ) ;
10+ stream . on ( " data" , ( chunk ) => chunks . push ( chunk ) ) ;
11+ stream . on ( " end" , ( ) => resolve ( Buffer . concat ( chunks ) ) ) ;
12+ stream . on ( " error" , ( err ) => {
13+ console . error ( " Error stream:" , err ) ;
1414 reject ( err ) ;
1515 } ) ;
1616 } ) ;
@@ -19,24 +19,27 @@ function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
1919async function fullUpload ( uploadStream : any ) : Promise < any > {
2020 return new Promise ( ( resolve , reject ) => {
2121 // Timeout for lag uploads
22- const timeout = setTimeout ( ( ) => {
23- reject ( new Error ( 'Upload timeout after 5 mins' ) ) ;
24- } , 5 * 60 * 1000 ) ;
25-
26- uploadStream . on ( 'complete' , ( file : any ) => {
22+ const timeout = setTimeout (
23+ ( ) => {
24+ reject ( new Error ( "Upload timeout after 5 mins" ) ) ;
25+ } ,
26+ 5 * 60 * 1000 ,
27+ ) ;
28+
29+ uploadStream . on ( "complete" , ( file : any ) => {
2730 clearTimeout ( timeout ) ;
2831 file . link ( ( err : any , url : string ) => {
2932 if ( err ) {
30- console . error ( ' Error linking file:' , err ) ;
33+ console . error ( " Error linking file:" , err ) ;
3134 return reject ( err ) ;
3235 }
3336 resolve ( { name : file . name , size : file . size , mime : file . mime , url } ) ;
3437 } ) ;
3538 } ) ;
3639
37- uploadStream . on ( ' error' , ( err : any ) => {
40+ uploadStream . on ( " error" , ( err : any ) => {
3841 clearTimeout ( timeout ) ;
39- console . error ( ' Could not upload media:' , err ) ;
42+ console . error ( " Could not upload media:" , err ) ;
4043 reject ( err ) ;
4144 } ) ;
4245 } ) ;
@@ -51,22 +54,25 @@ class MegaClient {
5154 constructor ( config : Config ) {
5255 this . config = config ;
5356 }
54-
57+
5558 async initialize ( ) {
56- await database . initialize ( this . config . DATABASE_URL ) ;
59+ if ( this . config . FILENAMES || this . config . autoDelete ?. enable ) {
60+ await database . initialize ( this . config . DATABASE_URL ) ;
61+ }
62+
5763 const creds = this . config . mega . accounts ;
58- if ( ! creds ) throw new Error ( ' No MEGA accounts found' ) ;
64+ if ( ! creds ) throw new Error ( " No MEGA accounts found" ) ;
5965
60- for ( const entry of creds . split ( ';' ) ) {
61- const [ email , password ] = entry . split ( ':' ) ;
66+ for ( const entry of creds . split ( ";" ) ) {
67+ const [ email , password ] = entry . split ( ":" ) ;
6268 if ( email && password ) {
6369 try {
6470 const storage = new ( Mega as any ) . Storage ( { email, password } ) ;
6571 await storage . ready ;
66- this . accounts . push ( {
67- email,
68- password,
69- storage
72+ this . accounts . push ( {
73+ email,
74+ password,
75+ storage,
7076 } ) ;
7177
7278 this . accountMutexes . set ( email , new Mutex ( ) ) ;
@@ -77,68 +83,71 @@ class MegaClient {
7783 }
7884 }
7985
80- if ( ! this . accounts . length ) throw new Error ( ' No valid MEGA accounts' ) ;
86+ if ( ! this . accounts . length ) throw new Error ( " No valid MEGA accounts" ) ;
8187 }
82-
8388 private selectAccount ( mode : UploadMode , query ?: UploadQuery ) : MegaAccount {
84- if ( mode === ' dual' && query ?. email ) {
85- const account = this . accounts . find ( a => a . email === query . email ) ;
89+ if ( mode === " dual" && query ?. email ) {
90+ const account = this . accounts . find ( ( a ) => a . email === query . email ) ;
8691 if ( ! account ) throw new Error ( `No account for ${ query . email } ` ) ;
8792 return account ;
8893 }
89-
94+
9095 const account = this . accounts [ this . currentAccountIndex ] ;
91- this . currentAccountIndex = ( this . currentAccountIndex + 1 ) % this . accounts . length ;
96+ this . currentAccountIndex =
97+ ( this . currentAccountIndex + 1 ) % this . accounts . length ;
9298 return account ;
9399 }
94100
95101 getAccountByEmail ( email : string ) : MegaAccount | null {
96- return this . accounts . find ( acc => acc . email === email ) || null ;
102+ return this . accounts . find ( ( acc ) => acc . email === email ) || null ;
97103 }
98104
99105 getZeroAcc ( ) : MegaAccount {
100- if ( ! this . accounts . length ) throw new Error ( ' No accounts available' ) ;
106+ if ( ! this . accounts . length ) throw new Error ( " No accounts available" ) ;
101107 return this . accounts [ 0 ] ;
102108 }
103109
104110 async uploadFile (
105111 filename : string ,
106112 input : NodeJS . ReadableStream ,
107- mode : UploadMode = ' single' ,
108- query ?: UploadQuery
113+ mode : UploadMode = " single" ,
114+ query ?: UploadQuery ,
109115 ) {
110116 const account = this . selectAccount ( mode , query ) ;
111117
112118 if ( ! account || ! account . storage ) {
113- throw new Error ( 'Storage not available for this account - payment required or banned' ) ;
119+ throw new Error (
120+ "Storage not available for this account - payment required or banned" ,
121+ ) ;
114122 }
115123
116124 const mutex = this . accountMutexes . get ( account . email ) ;
117- if ( ! mutex ) throw new Error ( ' Account mutex not found' ) ;
125+ if ( ! mutex ) throw new Error ( " Account mutex not found" ) ;
118126
119127 const release = await mutex . acquire ( ) ;
120128
121129 try {
122- //console.log(`Uploading ${filename} to ${account.email}`);
123130 const buffer = await streamToBuffer ( input ) ;
124131 const size = buffer . length ;
125132
126133 if ( size === 0 ) {
127- throw new Error ( ' File is empty' ) ;
134+ throw new Error ( " File is empty" ) ;
128135 }
129136
130- const uploadStream = account . storage . upload ( {
131- name : filename ,
132- size : size ,
133- allowUploadBuffering : true
137+ const uploadStream = account . storage . upload ( {
138+ name : filename ,
139+ size : size ,
140+ allowUploadBuffering : true ,
134141 } ) ;
135142
136143 Readable . from ( buffer ) . pipe ( uploadStream ) ;
137144 const result = await fullUpload ( uploadStream ) ;
138- // console.log(`Successfully uploaded ${filename} to ${account.email}`);
139145 return result ;
140146 } catch ( error ) {
141- throw new Error ( 'Upload failed: ' + ( error instanceof Error ? error . message : 'Unknown error' ) ) ;
147+ throw new Error (
148+ "Upload failed: " +
149+ ( error instanceof Error ? error . message : "Unknown error" ) ,
150+ ) ;
142151 } finally {
143152 release ( ) ;
144153 }
@@ -147,37 +156,38 @@ class MegaClient {
147156 async uploadBuffer (
148157 filename : string ,
149158 buffer : Buffer ,
150- mode : UploadMode = ' single' ,
151- query ?: UploadQuery
159+ mode : UploadMode = " single" ,
160+ query ?: UploadQuery ,
152161 ) {
153162 if ( ! buffer || buffer . length === 0 ) {
154- throw new Error ( ' Buffer is empty or null' ) ;
163+ throw new Error ( " Buffer is empty or null" ) ;
155164 }
156165
157166 return this . uploadFile ( filename , Readable . from ( buffer ) , mode , query ) ;
158167 }
159168
160169 async getFile ( filePath : string ) {
161170 const primary = this . getZeroAcc ( ) ;
162- const fileName = filePath . split ( '/' ) . pop ( ) || filePath ;
163- const file = Object . values ( primary . storage . files ) . find ( ( f : any ) => f . name === fileName ) ;
164- if ( ! file ) throw new Error ( 'File not found' ) ;
171+ const fileName = filePath . split ( "/" ) . pop ( ) || filePath ;
172+ const file = Object . values ( primary . storage . files ) . find (
173+ ( f : any ) => f . name === fileName ,
174+ ) ;
175+ if ( ! file ) throw new Error ( "File not found" ) ;
165176 return file ;
166177 }
167178
168179 async scheduleDelete ( name : string , mins : number ) {
169180 const deleteTime = Date . now ( ) + mins * 60_000 ;
170181 await database . save ( { fileName : name , deleteTime } ) ;
171- // console.log(`Scheduled ${name} for deletion in ${mins} minutes`);
172182 }
173183
174184 async processExpired ( ) {
175185 const now = Date . now ( ) ;
176186 const expired = await database . findExpired ( now ) ;
177-
187+
178188 for ( const record of expired ) {
179189 let fileDeleted = false ;
180-
190+
181191 for ( const account of this . accounts ) {
182192 try {
183193 const files = account . storage . root . children ;
@@ -190,7 +200,10 @@ class MegaClient {
190200 break ;
191201 }
192202 } catch ( error ) {
193- console . error ( `Failed to delete ${ record . fileName } from ${ account . email } :` , error ) ;
203+ console . error (
204+ `Failed to delete ${ record . fileName } from ${ account . email } :` ,
205+ error ,
206+ ) ;
194207 }
195208 }
196209
@@ -208,20 +221,49 @@ class MegaClient {
208221
209222 async cleanup ( ) {
210223 try {
211- await this . processExpired ( ) ;
224+ if ( this . config . autoDelete ?. enable ) {
225+ await this . processExpired ( ) ;
226+ }
212227 await database . disconnect ( ) ;
213- console . log ( ' Cleanup completed' ) ;
228+ console . log ( " Cleanup completed" ) ;
214229 } catch ( error ) {
215- console . error ( ' Cleanup failed:' , error ) ;
230+ console . error ( " Cleanup failed:" , error ) ;
216231 }
217232 }
218-
219233 getAccountCount ( ) : number {
220234 return this . accounts . length ;
221235 }
222236
223237 getAccountEmails ( ) : string [ ] {
224- return this . accounts . map ( acc => acc . email ) ;
238+ return this . accounts . map ( ( acc ) => acc . email ) ;
239+ }
240+
241+ public getAccounts ( ) : MegaAccount [ ] {
242+ return this . accounts ;
243+ }
244+
245+ public async deleteFileByName ( fileName : string ) : Promise < boolean > {
246+ for ( const account of this . accounts ) {
247+ try {
248+ await account . storage . reload ( ) ;
249+ const file = account . storage . find ( fileName ) ;
250+ if ( file && ! file . directory ) {
251+ await file . delete ( true ) ;
252+ return true ;
253+ }
254+ } catch ( error ) {
255+ console . error (
256+ `Failed to delete ${ fileName } from ${ account . email } :` ,
257+ error ,
258+ ) ;
259+ }
260+ }
261+ return false ;
262+ }
263+ public async getFileNameFromUrl ( url : string ) : Promise < string > {
264+ const file = Mega . File . fromURL ( url ) ;
265+ await file . loadAttributes ( ) ;
266+ return file . name || "unknown" ;
225267 }
226268
227269 async getStorageInfo ( ) : Promise < any [ ] > {
@@ -239,7 +281,7 @@ class MegaClient {
239281 console . error ( `Failed to get acc info for ${ account . email } :` , error ) ;
240282 info . push ( {
241283 email : account . email ,
242- error : ' Failed to get acc info' ,
284+ error : " Failed to get acc info" ,
243285 } ) ;
244286 }
245287 }
0 commit comments