1+ var site = { } , controller = false ;
2+
3+ function setSiteData ( siteData )
4+ {
5+ site = siteData ;
6+ }
7+
8+ // Search comic/manga in site
9+ async function searchComic ( title )
10+ {
11+ if ( controller ) controller . abort ( ) ;
12+ controller = new AbortController ( ) ;
13+
14+ const variables = new URLSearchParams ( {
15+ q : title ,
16+ page : 1 ,
17+ perPage : 10 ,
18+ fields : 'title,main_picture' , // 'title,main_picture,synopsis',
19+ } ) ;
20+
21+ const options = {
22+ method : 'GET' ,
23+ headers : {
24+ 'Authorization' : 'Bearer ' + site . config . session . token ,
25+ 'Accept' : 'application/json' ,
26+ } ,
27+ signal : controller . signal ,
28+ } ;
29+
30+ try
31+ {
32+ const response = await fetch ( 'https://api.myanimelist.net/v2/manga?' + variables . toString ( ) , options ) ;
33+
34+ if ( response . status == 400 || response . status == 401 )
35+ {
36+ tracking . invalidateSession ( site . key , true ) ;
37+ return null ;
38+ }
39+ else if ( response . status == 200 )
40+ {
41+ const json = await response . json ( ) ;
42+ const results = ( json . data || [ ] ) . map ( function ( item ) {
43+
44+ const node = item . node || { } ;
45+
46+ return {
47+ id : node . id ,
48+ title : node . title ,
49+ image : node . main_picture . medium ,
50+ // synopsis: media.synopsis || null,
51+ } ;
52+
53+ } ) ;
54+
55+ return results ;
56+ }
57+ }
58+ catch ( error ) { }
59+
60+ return [ ] ;
61+ }
62+
63+ // Return data of comic/manga
64+ async function getComicData ( siteId )
65+ {
66+ const options = {
67+ method : 'GET' ,
68+ headers : {
69+ 'Authorization' : 'Bearer ' + site . config . session . token ,
70+ 'Accept' : 'application/json' ,
71+ } ,
72+ } ;
73+
74+ try
75+ {
76+ const response = await fetch ( 'https://api.myanimelist.net/v2/manga/' + siteId + '?fields=title,main_picture,num_chapters,num_volumes,synopsis,my_list_status,status' , options ) ;
77+
78+ if ( response . status == 400 || response . status == 401 )
79+ {
80+ tracking . invalidateSession ( site . key , true ) ;
81+ return null ;
82+ }
83+ else if ( response . status == 200 )
84+ {
85+ const json = await response . json ( ) ;
86+
87+ if ( json . id )
88+ {
89+ return {
90+ title : json . title ,
91+ image : json . main_picture . medium ,
92+ // synopsis: synopsis,
93+ chapters : + json . num_chapters || 0 ,
94+ volumes : + json . num_volumes || 0 ,
95+ progress : {
96+ chapters : + json ?. my_list_status ?. num_chapters_read || 0 ,
97+ volumes : + json ?. my_list_status ?. num_volumes_read || 0 ,
98+ // status: json?.my_list_status?.status || '',
99+ } ,
100+ } ;
101+ }
102+ }
103+ }
104+ catch ( error ) { }
105+
106+ return { } ;
107+ }
108+
109+ // Loging to site
110+ async function login ( )
111+ {
112+ const challenge = crypto . hash ( 'sha512' , crypto . randomUUID ( ) , 'hex' ) ;
113+ const url = await tracking . getRedirectResult ( site . key , 'https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=' + site . auth . clientId + '&code_challenge=' + challenge + '&redirect_uri=opencomic://tracking/myanimelist&response_type=code' ) ;
114+ const code = url . searchParams . get ( 'code' ) || url . searchParams . get ( 'token' ) ;
115+
116+ if ( ! code )
117+ return { valid : false } ;
118+
119+ const variables = new URLSearchParams ( {
120+ grant_type : 'authorization_code' ,
121+ client_id : site . auth . clientId ,
122+ redirect_uri : 'opencomic://tracking/myanimelist' ,
123+ code : code ,
124+ code_verifier : challenge ,
125+ } ) ;
126+
127+ const options = {
128+ method : 'POST' ,
129+ headers : {
130+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
131+ 'Accept' : 'application/json' ,
132+ } ,
133+ body : variables . toString ( ) ,
134+ } ;
135+
136+ try
137+ {
138+ const response = await fetch ( 'https://myanimelist.net/v1/oauth2/token' , options ) ;
139+
140+ if ( response . status == 200 )
141+ {
142+ const json = await response . json ( ) ;
143+ return { valid : true , token : json . access_token } ;
144+ }
145+ }
146+ catch ( error ) { }
147+
148+ return { valid : false } ;
149+ }
150+
151+ // Track comic/manga
152+ async function track ( toTrack )
153+ {
154+ const options = {
155+ method : 'GET' ,
156+ headers : {
157+ 'Authorization' : 'Bearer ' + site . config . session . token ,
158+ 'Accept' : 'application/json' ,
159+ } ,
160+ } ;
161+
162+ try
163+ {
164+ const response = await fetch ( 'https://api.myanimelist.net/v2/manga/' + toTrack . id + '?fields=title,main_picture,num_chapters,num_volumes,synopsis,my_list_status,status' , options ) ;
165+
166+ if ( response . status == 400 || response . status == 401 )
167+ {
168+ tracking . invalidateSession ( site . key , true ) ;
169+ }
170+ else if ( response . status == 200 )
171+ {
172+ const json = await response . json ( ) ;
173+
174+ const totalChapters = + json . num_chapters || 0 ;
175+ const { status : userStatus , num_chapters_read : userChapters , num_volumes_read : userVolumes } = json ?. my_list_status || { } ;
176+
177+ let status , chapters , volumes ;
178+
179+ // Status
180+ if ( totalChapters && toTrack . chapters && toTrack . chapters == totalChapters )
181+ status = 'completed' ;
182+
183+ // Chapters
184+ if ( toTrack . chapters && ( ! userChapters || toTrack . chapters > userChapters ) )
185+ chapters = toTrack . chapters ;
186+
187+ // Volumes
188+ if ( toTrack . volumes && ( ! userVolumes || toTrack . volumes > userVolumes ) )
189+ volumes = toTrack . volumes ;
190+
191+ const variables = new URLSearchParams ( ) ;
192+ if ( status ) variables . append ( 'status' , status ) ;
193+ if ( chapters ) variables . append ( 'num_chapters_read' , chapters ) ;
194+ if ( volumes ) variables . append ( 'num_volumes_read' , volumes ) ;
195+
196+ if ( ! status && ! chapters && ! volumes )
197+ return ; // Nothing to update
198+
199+ const options = {
200+ method : 'PUT' ,
201+ headers : {
202+ 'Authorization' : 'Bearer ' + site . config . session . token ,
203+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
204+ 'Accept' : 'application/json' ,
205+ } ,
206+ body : variables . toString ( ) ,
207+ } ;
208+
209+ fetch ( 'https://api.myanimelist.net/v2/manga/' + toTrack . id + '/my_list_status' , options ) ;
210+ }
211+ }
212+ catch ( error )
213+ {
214+ console . error ( error ) ;
215+ }
216+ }
217+
218+ module . exports = {
219+ setSiteData : setSiteData ,
220+ searchComic : searchComic ,
221+ getComicData : getComicData ,
222+ login : login ,
223+ track : track ,
224+ } ;
0 commit comments