@@ -38,9 +38,9 @@ class KomiktapScraper {
3838 // series detail
3939 'detail_title' : '.entry-title' ,
4040 'detail_cover' : '.thumb img' ,
41- 'detail_status' : '.imptdt ' ,
42- 'detail_type' : '.imptdt ' ,
43- 'detail_infoList' : '.fmed ' ,
41+ 'detail_status' : '.infotable tr ' ,
42+ 'detail_type' : '.infotable tr ' ,
43+ 'detail_infoList' : '.infotable tr ' ,
4444 'detail_genres' : '.seriestugenre a' ,
4545 'detail_synopsis' : '[itemprop="description"]' ,
4646
@@ -250,6 +250,9 @@ class KomiktapScraper {
250250
251251 // Parse chapters
252252 final chapters = _parseChapterList (document);
253+
254+ // Parse favorites
255+ final favorites = _parseFavorites (document);
253256
254257 return KomiktapSeriesDetail (
255258 id: slug,
@@ -259,11 +262,100 @@ class KomiktapScraper {
259262 status: status,
260263 type: contentType,
261264 tags: genres,
262- author: metadata['author' ] as String ? ,
265+ author: (metadata['author' ] ?? metadata['artist' ] ?? metadata['posted_by' ]) as String ? ,
266+ lastUpdate: (metadata['updated_on' ] ?? metadata['posted_on' ]) as DateTime ? ,
263267 chapters: chapters,
268+ favorites: favorites,
264269 );
265270 }
266271
272+ // ... (keep existing methods)
273+
274+ Map <String , dynamic > _parseMetadata (Document doc) {
275+ final metadata = < String , dynamic > {};
276+
277+ // Parse all .infotable rows
278+ final infoRows = doc.querySelectorAll (_getSelector ('detail_infoList' ));
279+
280+ for (final row in infoRows) {
281+ final cells = row.querySelectorAll ('td' );
282+ if (cells.length < 2 ) continue ;
283+
284+ final label = cells[0 ].text.trim ().toLowerCase ();
285+ final value = cells[1 ].text.trim ();
286+
287+ if (label.contains ('author' )) {
288+ metadata['author' ] = value;
289+ } else if (label.contains ('artist' )) {
290+ metadata['artist' ] = value;
291+ } else if (label.contains ('serialization' )) {
292+ metadata['serialization' ] = value;
293+ } else if (label.contains ('posted by' )) {
294+ metadata['posted_by' ] = value;
295+ } else if (label.contains ('updated on' ) || label.contains ('posted on' )) {
296+ // Extract date from time tag or text
297+ DateTime ? date;
298+ final timeEl = cells[1 ].querySelector ('time' );
299+ if (timeEl != null ) {
300+ final datetime = timeEl.attributes['datetime' ];
301+ if (datetime != null ) {
302+ date = DateTime .tryParse (datetime);
303+ }
304+ }
305+
306+ // Fallback to text parsing
307+ date ?? = _parseDate (value);
308+
309+ if (date != null ) {
310+ if (label.contains ('updated' )) {
311+ metadata['updated_on' ] = date;
312+ } else {
313+ metadata['posted_on' ] = date;
314+ }
315+ }
316+ }
317+ }
318+
319+ return metadata;
320+ }
321+
322+ DateTime ? _parseDate (String ? dateText) {
323+ if (dateText == null || dateText.isEmpty) return null ;
324+
325+ try {
326+ // Try direct ISO parse first
327+ final parsed = DateTime .tryParse (dateText);
328+ if (parsed != null ) return parsed;
329+
330+ // Handle relative dates like "2 hours ago", "3 days ago"
331+ final relativePattern = RegExp (r'(\d+)\s+(hour|day|week|month)s?\s+ago' ,
332+ caseSensitive: false );
333+ final match = relativePattern.firstMatch (dateText);
334+
335+ if (match != null ) {
336+ final amount = int .tryParse (match.group (1 )! ) ?? 0 ;
337+ final unit = match.group (2 )! .toLowerCase ();
338+
339+ final now = DateTime .now ();
340+ switch (unit) {
341+ case 'hour' :
342+ return now.subtract (Duration (hours: amount));
343+ case 'day' :
344+ return now.subtract (Duration (days: amount));
345+ case 'week' :
346+ return now.subtract (Duration (days: amount * 7 ));
347+ case 'month' :
348+ return DateTime (now.year, now.month - amount, now.day);
349+ }
350+ }
351+
352+ return null ;
353+ } catch (_) {
354+ return null ;
355+ }
356+ }
357+
358+
267359 List <KomiktapChapterInfo > _parseChapterList (Document document) {
268360 final items = document.querySelectorAll (_getSelector ('detail_chapterList' ));
269361
@@ -455,66 +547,19 @@ class KomiktapScraper {
455547 .toList ();
456548 }
457549
458- Map <String , dynamic > _parseMetadata (Document doc) {
459- final metadata = < String , dynamic > {};
460-
461- // Parse all .infotable rows
462- final infoRows = doc.querySelectorAll (_getSelector ('detail_infoList' ));
463-
464- for (final row in infoRows) {
465- final cells = row.querySelectorAll ('td' );
466- if (cells.length < 2 ) continue ;
467-
468- final label = cells[0 ].text.trim ().toLowerCase ();
469- final value = cells[1 ].text.trim ();
470-
471- if (label.contains ('author' )) {
472- metadata['author' ] = value;
473- } else if (label.contains ('artist' )) {
474- metadata['artist' ] = value;
475- } else if (label.contains ('serialization' )) {
476- metadata['serialization' ] = value;
477- } else if (label.contains ('posted by' )) {
478- metadata['posted_by' ] = value;
479- }
480- }
481-
482- return metadata;
483- }
484-
485- DateTime ? _parseDate (String ? dateText) {
486- if (dateText == null || dateText.isEmpty) return null ;
487-
550+ int ? _parseFavorites (Document doc) {
488551 try {
489- // Try direct ISO parse first
490- final parsed = DateTime .tryParse (dateText);
491- if (parsed != null ) return parsed;
492-
493- // Handle relative dates like "2 hours ago", "3 days ago"
494- final relativePattern = RegExp (r'(\d+)\s+(hour|day|week|month)s?\s+ago' ,
495- caseSensitive: false );
496- final match = relativePattern.firstMatch (dateText);
497-
498- if (match != null ) {
499- final amount = int .tryParse (match.group (1 )! ) ?? 0 ;
500- final unit = match.group (2 )! .toLowerCase ();
501-
502- final now = DateTime .now ();
503- switch (unit) {
504- case 'hour' :
505- return now.subtract (Duration (hours: amount));
506- case 'day' :
507- return now.subtract (Duration (days: amount));
508- case 'week' :
509- return now.subtract (Duration (days: amount * 7 ));
510- case 'month' :
511- return DateTime (now.year, now.month - amount, now.day);
552+ // <div class="bmc">Followed by 21 people</div>
553+ final bmcEl = doc.querySelector ('.bmc' );
554+ if (bmcEl != null ) {
555+ final text = bmcEl.text.trim ();
556+ // Extract number from "Followed by 21 people"
557+ final match = RegExp (r'(\d+)' ).firstMatch (text);
558+ if (match != null ) {
559+ return int .tryParse (match.group (1 )! );
512560 }
513561 }
514-
515- return null ;
516- } catch (_) {
517- return null ;
518- }
562+ } catch (_) {}
563+ return null ;
519564 }
520565}
0 commit comments