86 lines
2.3 KiB
Dart
86 lines
2.3 KiB
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
import 'package:worldhopper/models/opds_link.dart';
|
|
import 'package:worldhopper/models/opds_stream_link.dart';
|
|
|
|
part 'opds_entry.freezed.dart';
|
|
part 'opds_entry.g.dart';
|
|
|
|
/// Represents an entry in an OPDS feed (a publication or collection)
|
|
@freezed
|
|
class OPDSEntry with _$OPDSEntry {
|
|
const factory OPDSEntry({
|
|
/// Unique identifier for the entry
|
|
required String id,
|
|
|
|
/// Entry title
|
|
required String title,
|
|
|
|
/// Entry summary/description
|
|
String? summary,
|
|
|
|
/// Entry content (HTML description)
|
|
String? content,
|
|
|
|
/// Publication date
|
|
DateTime? published,
|
|
|
|
/// Last update date
|
|
DateTime? updated,
|
|
|
|
/// Authors
|
|
@Default([]) List<String> authors,
|
|
|
|
/// Categories/tags
|
|
@Default([]) List<String> categories,
|
|
|
|
/// All links associated with this entry
|
|
@Default([]) List<OPDSLink> links,
|
|
|
|
/// PSE stream link (if available)
|
|
OPDSStreamLink? streamLink,
|
|
|
|
/// Cover image URL
|
|
String? coverUrl,
|
|
|
|
/// Thumbnail image URL
|
|
String? thumbnailUrl,
|
|
}) = _OPDSEntry;
|
|
|
|
const OPDSEntry._();
|
|
|
|
/// Create OPDSEntry from JSON
|
|
factory OPDSEntry.fromJson(Map<String, dynamic> json) =>
|
|
_$OPDSEntryFromJson(json);
|
|
|
|
/// Check if this entry has a stream link (can be read page by page)
|
|
bool get hasStreamLink => streamLink != null;
|
|
|
|
/// Check if this entry is a navigation entry (leads to another feed)
|
|
bool get isNavigation => links.any(
|
|
(link) => link.rel == 'subsection' || link.rel.contains('navigation'),
|
|
);
|
|
|
|
/// Get the acquisition link (for download or reading)
|
|
OPDSLink? get acquisitionLink => links.firstWhere(
|
|
(link) =>
|
|
link.rel.contains('acquisition') ||
|
|
link.rel.contains('open-access'),
|
|
orElse: () => links.first,
|
|
);
|
|
|
|
/// Get the self link
|
|
OPDSLink? get selfLink => links.cast<OPDSLink?>().firstWhere(
|
|
(link) => link?.rel == 'self',
|
|
orElse: () => null,
|
|
);
|
|
|
|
/// Check if this entry is an EPUB publication
|
|
bool get isEpub {
|
|
final link = acquisitionLink;
|
|
if (link == null) return false;
|
|
return link.type?.toLowerCase().contains('epub') ?? false;
|
|
}
|
|
|
|
/// Check if this entry is an image stream (comic/manga)
|
|
bool get isImageStream => hasStreamLink;
|
|
}
|