Files
lunasea/lib/modules/lidarr/core/api/api.dart
Jagandeep Brar 5cf33b3abd [Release/TestFlight] v4.1.0+40100001 (#266)
NEW
- [Networking] Strict TLS/SSL validation is now disabled globally
- [Routing] Hold the AppBar back button to pop back to the home page of the module
- [Settings/Sonarr] Toggle to enable Sonarr v3 features
- [Sonarr] Complete rewrite of Sonarr
- [Sonarr] State is now held across module switches
- [Sonarr/Overview] View tags applied to series
- [Sonarr/Catalogue] (v3 only) Ability to set and update language profile
- [Sonarr/Catalogue] Ability to view all, only monitored, or only unmonitored series
- [Sonarr/Releases] (v3 only) Ability to interactively search for season packs
- [Sonarr/Releases] Ability to view all, only approved, or only rejected releases

TWEAKS
- [Settings] Removed all toggles for strict TLS
- [Sonarr] Many tweaks to Sonarr's design
- [Sonarr/Add] (v3 only) Tapping an already-added series will take you to the series page
- [Sonarr/Add] Automatically navigate to newly added series
- [Sonarr/Series] Toggling monitored state of series has now been moved to the edit prompt

FIXES
- [Images] Images will now load for invalid/self-signed certificates
- [Tautulli/Activity] Fix grey screen for music activity
- [TextField] Fix TextField actions (cut, copy, paste, etc.) not showing
- [Timestamps] Fix 12:xx AM being shown as 00:xx AM
- Additional small bug fixes
2020-10-13 09:43:28 -05:00

737 lines
32 KiB
Dart

import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrAPI extends API {
final Map<String, dynamic> _values;
final Dio _dio;
LidarrAPI._internal(this._values, this._dio);
factory LidarrAPI.from(ProfileHiveObject profile) {
Map<String, dynamic> _headers = Map<String, dynamic>.from(profile.getLidarr()['headers']);
Dio _client = Dio(
BaseOptions(
baseUrl: (profile.getLidarr()['host'] as String).endsWith('/')
? '${profile.getLidarr()['host']}api/v1/'
: '${profile.getLidarr()['host']}/api/v1/',
queryParameters: {
if(profile.getLidarr()['key'] != '') 'apikey': profile.getLidarr()['key'],
},
headers: _headers,
followRedirects: true,
maxRedirects: 5,
),
);
return LidarrAPI._internal(
profile.getLidarr(),
_client,
);
}
void logWarning(String methodName, String text) => LunaLogger.warning(
'package:lunasea/core/api/lidarr/api.dart',
methodName,
'Lidarr: $text',
);
void logError(String methodName, String text, Object error, StackTrace trace, {
bool uploadToSentry = true,
}) => LunaLogger.error(
'package:lunasea/core/api/lidarr/api.dart',
methodName,
'Lidarr: $text',
error,
trace,
uploadToSentry: uploadToSentry,
);
bool get enabled => _values['enabled'];
String get host => _values['host'];
String get key => _values['key'];
Future<bool> testConnection() async {
try {
Response response = await _dio.get('system/status');
if(response.statusCode == 200) return true;
} catch (error, stack) {
logError('testConnection', 'Connection test failed', error, stack, uploadToSentry: false);
}
return false;
}
Future<List<LidarrCatalogueData>> getAllArtists() async {
try {
Map<int, LidarrQualityProfile> _qualities = await getQualityProfiles().catchError((error) { return Future.error(error); });
Map<int, LidarrMetadataProfile> _metadatas = await getMetadataProfiles().catchError((error) { return Future.error(error); });
Response response = await _dio.get('artist');
List<LidarrCatalogueData> entries = [];
for(var entry in response.data) {
entries.add(LidarrCatalogueData(
title: entry['artistName'] ?? 'Unknown Artist',
sortTitle: entry['sortName'] ?? 'Unknown Artist',
overview: entry['overview'] ?? 'No summary is available.',
path: entry['path'] ?? 'Unknown Path',
artistID: entry['id'] ?? 0,
artistType: entry['artistType'] ?? 'Unknown Artist Type',
monitored: entry['monitored'] ?? false,
statistics: entry['statistics'] ?? {},
qualityProfile: entry['qualityProfileId'] ?? 0,
metadataProfile: entry['metadataProfileId'] ?? 0,
quality: entry['qualityProfileId'] != null ? _qualities[entry['qualityProfileId']].name ?? 'Unknown Quality Profile' : '',
metadata: entry['metadataProfileId'] != null ? _metadatas[entry['metadataProfileId']].name ?? 'Unknown Metadata Profile' : '',
genres: entry['genres'] ?? [],
links: entry['links'] ?? [],
albumFolders: entry['albumFolder'] ?? false,
foreignArtistID: entry['foreignArtistId'] ?? '',
sizeOnDisk: entry['statistics'] != null ? entry['statistics']['sizeOnDisk'] ?? 0 : 0,
added: entry['added'] ?? '',
));
}
return entries;
} on DioError catch (error, stack) {
logError('getAllArtists', 'Failed to fetch artists', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getAllArtists', 'Failed to fetch artists', error, stack);
return Future.error(error);
}
}
Future<List<String>> getAllArtistIDs() async {
try {
Response response = await _dio.get('artist');
List<String> _entries = [];
for(var entry in response.data) {
_entries.add(entry['foreignArtistId'] ?? '');
}
return _entries;
} on DioError catch (error, stack) {
logError('getAllArtistIDs', 'Failed to fetch artist IDs', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getAllArtistIDs', 'Failed to fetch artist IDs', error, stack);
return Future.error(error);
}
}
Future<bool> refreshArtist(int artistID) async {
try {
await _dio.post(
'command',
data: json.encode({
'name': 'RefreshArtist',
'artistId': artistID,
}),
);
return true;
} on DioError catch (error, stack) {
logError('refreshArtist', 'Failed to refresh artist ($artistID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('refreshArtist', 'Failed to refresh artist ($artistID)', error, stack);
return Future.error(error);
}
}
Future<LidarrCatalogueData> getArtist(int artistID) async {
try {
Map<int, LidarrQualityProfile> _qualities = await getQualityProfiles().catchError((error) { return Future.error(error); });
Map<int, LidarrMetadataProfile> _metadatas = await getMetadataProfiles().catchError((error) { return Future.error(error); });
Response response = await _dio.get('artist/$artistID');
return LidarrCatalogueData(
title: response.data['artistName'] ?? 'Unknown Artist',
sortTitle: response.data['sortName'] ?? 'Unknown Artist',
overview: response.data['overview'] ?? 'No summary is available.',
artistType: response.data['artistType'] ?? 'Unknown Artist Type',
path: response.data['path'] ?? 'Unknown Path',
artistID: response.data['id'] ?? 0,
added: response.data['added'] ?? '',
monitored: response.data['monitored'] ?? false,
statistics: response.data['statistics'] ?? {},
qualityProfile: response.data['qualityProfileId'] ?? 0,
metadataProfile: response.data['metadataProfileId'] ?? 0,
quality: response.data['qualityProfileId'] != null ? _qualities[response.data['qualityProfileId']].name ?? 'Unknown Quality Profile' : '',
metadata: response.data['metadataProfileId'] != null ? _metadatas[response.data['metadataProfileId']].name ?? 'Unknown Metadata Profile' : '',
genres: response.data['genres'] ?? [],
links: response.data['links'] ?? [],
albumFolders: response.data['albumFolder'] ?? false,
foreignArtistID: response.data['foreignArtistId'] ?? '',
sizeOnDisk: response.data['statistics'] != null ? response.data['statistics']['sizeOnDisk'] ?? 0 : 0,
);
} on DioError catch (error, stack) {
logError('getArtist', 'Failed to fetch artist ($artistID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getArtist', 'Failed to fetch artist ($artistID)', error, stack);
return Future.error(error);
}
}
Future<bool> removeArtist(int artistID, { bool deleteFiles = false }) async {
try {
await _dio.delete(
'artist/$artistID',
queryParameters: {
'deleteFiles': deleteFiles,
},
);
return true;
} on DioError catch (error, stack) {
logError('removeArtist', 'Failed to remove artist ($artistID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('removeArtist', 'Failed to remove artist ($artistID)', error, stack);
return Future.error(error);
}
}
Future<bool> editArtist(int artistID, LidarrQualityProfile qualityProfile, LidarrMetadataProfile metadataProfile, String path, bool monitored, bool albumFolders) async {
try {
Response response = await _dio.get('artist/$artistID');
Map artist = response.data;
artist['monitored'] = monitored;
artist['albumFolder'] = albumFolders;
artist['path'] = path;
artist['profileId'] = qualityProfile.id;
artist['qualityProfileId'] = qualityProfile.id;
artist['metadataProfileId'] = metadataProfile.id;
response = await _dio.put(
'artist',
data: json.encode(artist),
);
return true;
} on DioError catch (error, stack) {
logError('editArtist', 'Failed to edit artist ($artistID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('editArtist', 'Failed to edit artist ($artistID)', error, stack);
return Future.error(error);
}
}
Future<List<LidarrAlbumData>> getArtistAlbums(int artistID) async {
try {
Response response = await _dio.get(
'album',
queryParameters: {
'artistId': artistID,
}
);
List<LidarrAlbumData> entries = [];
for(var entry in response.data) {
entries.add(LidarrAlbumData(
albumID: entry['id'] ?? -1,
title: entry['title'] ?? 'Unknown Album Title',
monitored: entry['monitored'] ?? false,
trackCount: entry['statistics'] == null ? 0 : entry['statistics']['totalTrackCount'] ?? 0,
percentageTracks: entry['statistics'] == null ? 0 : entry['statistics']['percentOfTracks'] ?? 0,
releaseDate: entry['releaseDate'] ?? '',
));
}
entries.sort((a, b) {
if(a.releaseDateObject == null) return 1;
if(b.releaseDateObject == null) return -1;
return b.releaseDateObject.compareTo(a.releaseDateObject);
});
return entries;
} on DioError catch (error, stack) {
logError('getArtistAlbums', 'Failed to fetch albums ($artistID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getArtistAlbums', 'Failed to fetch albums ($artistID)', error, stack);
return Future.error(error);
}
}
Future<LidarrAlbumData> getAlbum(int albumID) async {
try {
Response response = await _dio.get(
'album',
queryParameters: {
'albumIds': albumID,
},
);
return LidarrAlbumData(
albumID: response.data[0]['id'] ?? -1,
title: response.data[0]['title'] ?? 'Unknown Album Title',
monitored: response.data[0]['monitored'] ?? false,
trackCount: response.data[0]['statistics'] == null ? 0 : response.data[0]['statistics']['totalTrackCount'] ?? 0,
percentageTracks: response.data[0]['statistics'] == null ? 0 : response.data[0]['statistics']['percentOfTracks'] ?? 0,
releaseDate: response.data[0]['releaseDate'] ?? '',
);
} on DioError catch (error, stack) {
logError('getAlbum', 'Failed to fetch album ($albumID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getAlbum', 'Failed to fetch album ($albumID)', error, stack);
return Future.error(error);
}
}
Future<List<LidarrTrackData>> getAlbumTracks(int albumID) async {
try {
Response response = await _dio.get(
'track',
queryParameters: {
'albumId': albumID,
},
);
List<LidarrTrackData> entries = [];
for(var entry in response.data) {
entries.add(LidarrTrackData(
trackID: entry['id'] ?? -1,
title: entry['title'] ?? 'Unknown Track Title',
trackNumber: entry['trackNumber'] ?? '',
duration: entry['duration'] ?? 0,
explicit: entry['explicit'] ?? false,
hasFile: entry['hasFile'] ?? false,
));
}
return entries;
} on DioError catch (error, stack) {
logError('getAlbumTracks', 'Failed to fetch album tracks ($albumID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getAlbumTracks', 'Failed to fetch album tracks ($albumID)', error, stack);
return Future.error(error);
}
}
Future<Map<int, LidarrQualityProfile>> getQualityProfiles() async {
try {
Response response = await _dio.get('qualityprofile');
var _entries = new Map<int, LidarrQualityProfile>();
for(var entry in response.data) {
_entries[entry['id']] = LidarrQualityProfile(
id: entry['id'] ?? -1,
name: entry['name'] ?? 'Unknown Quality Profile',
);
}
return _entries;
} on DioError catch (error, stack) {
logError('getQualityProfiles', 'Failed to fetch quality profiles', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getQualityProfiles', 'Failed to fetch quality profiles', error, stack);
return Future.error(error);
}
}
Future<Map<int, LidarrMetadataProfile>> getMetadataProfiles() async {
try {
Response response = await _dio.get('metadataprofile');
var _entries = new Map<int, LidarrMetadataProfile>();
for(var entry in response.data) {
_entries[entry['id']] = LidarrMetadataProfile(
id: entry['id'] ?? -1,
name: entry['name'] ?? 'Unknown Metadata Profile',
);
}
return _entries;
} on DioError catch (error, stack) {
logError('getMetadataProfiles', 'Failed to fetch metadata profiles', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getMetadataProfiles', 'Failed to fetch metadata profiles', error, stack);
return Future.error(error);
}
}
Future<List<LidarrRootFolder>> getRootFolders() async {
try {
Response response = await _dio.get('rootfolder');
List<LidarrRootFolder> _entries = [];
for(var entry in response.data) {
_entries.add(LidarrRootFolder(
id: entry['id'] ?? -1,
path: entry['path'] ?? 'Unknown Root Folder',
freeSpace: entry['freeSpace'] ?? 0,
));
}
return _entries;
} on DioError catch (error, stack) {
logError('getRootFolders', 'Failed to fetch root folders', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getRootFolders', 'Failed to fetch root folders', error, stack);
return Future.error(error);
}
}
Future<List<LidarrHistoryData>> getHistory({ String sortKey = 'date', String sortDir = 'desc', int pageSize = 250}) async {
try {
Response response = await _dio.get(
'history',
queryParameters: {
'sortKey': sortKey,
'pageSize': pageSize,
'sortDir': sortDir,
}
);
List<LidarrHistoryData> _entries = [];
for(var entry in response.data['records']) {
switch(entry['eventType']) {
case 'grabbed': {
_entries.add(LidarrHistoryDataGrabbed(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
indexer: entry['data']['indexer'] ?? 'Unknown Indexer',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
case 'trackFileImported': {
_entries.add(LidarrHistoryDataTrackFileImported(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
quality: entry['quality']['quality']['name'] ?? 'Unknown Quality',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
case 'albumImportIncomplete': {
_entries.add(LidarrHistoryDataAlbumImportIncomplete(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
case 'downloadImported': {
_entries.add(LidarrHistoryDataDownloadImported(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
quality: entry['quality']['quality']['name'] ?? 'Unknown Quality',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
case 'trackFileDeleted': {
_entries.add(LidarrHistoryDataTrackFileDeleted(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
reason: entry['data']['reason'] ?? 'Unknown Reason',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
case 'trackFileRenamed': {
_entries.add(LidarrHistoryDataTrackFileRenamed(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
default: {
_entries.add(LidarrHistoryDataGeneric(
title: entry['sourceTitle'] ?? 'Unknown Title',
timestamp: entry['date'] ?? '',
eventType: entry['eventType'] ?? 'Unknown Event Type',
artistID: entry['artistId'] ?? -1,
albumID: entry['albumId'] ?? -1,
));
break;
}
}
}
return _entries;
} on DioError catch (error, stack) {
logError('getHistory', 'Failed to fetch history', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getHistory', 'Failed to fetch history', error, stack);
return Future.error(error);
}
}
Future<List<LidarrMissingData>> getMissing({ int pageSize = 250, String sortDir = 'descending', String sortKey = 'releaseDate', bool monitored = true}) async {
try {
Response response = await _dio.get(
'wanted/missing',
queryParameters: {
'pageSize': pageSize,
'sortDirection': sortDir,
'sortKey': sortKey,
'monitored': monitored,
}
);
List<LidarrMissingData> entries = [];
for(var entry in response.data['records']) {
entries.add(LidarrMissingData(
title: entry['title'] ?? 'Unknown Title',
artistTitle: entry['artist']['artistName'] ?? 'Unknown Artist Title',
artistID: entry['artistId'] ?? -1,
albumID: entry['id'] ?? -1,
releaseDate: entry['releaseDate'] ?? '',
monitored: entry['monitored'] ?? false,
));
}
return entries;
} on DioError catch (error, stack) {
logError('getMissing', 'Failed to fetch missing albums', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getMissing', 'Failed to fetch missing albums', error, stack);
return Future.error(error);
}
}
Future<bool> searchAlbums(List<int> albums) async {
try {
await _dio.post(
'command',
data: json.encode({
'name': 'AlbumSearch',
'albumIds': albums,
}),
);
return true;
} on DioError catch (error, stack) {
logError('searchAlbums', 'Failed to search for albums (${albums.toString()})', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('searchAlbums', 'Failed to search for albums (${albums.toString()})', error, stack);
return Future.error(error);
}
}
Future<bool> searchAllMissing() async {
try {
await _dio.post(
'command',
data: json.encode({
'name': 'MissingAlbumSearch',
'filterKey': 'monitored',
'filterValue': true,
}),
);
return true;
} on DioError catch (error, stack) {
logError('searchAllMissing', 'Failed to search for all missing albums', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('searchAllMissing', 'Failed to search for all missing albums', error, stack);
return Future.error(error);
}
}
Future<bool> updateLibrary() async {
try {
await _dio.post(
'command',
data: json.encode({
'name': 'RefreshArtist',
}),
);
return true;
} on DioError catch (error, stack) {
logError('updateLibrary', 'Failed to update library', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('updateLibrary', 'Failed to update library', error, stack);
return Future.error(error);
}
}
Future<bool> triggerRssSync() async {
try {
await _dio.post(
'command',
data: json.encode({
'name': 'RssSync',
}),
);
return true;
} on DioError catch (error, stack) {
logError('triggerRssSync', 'Failed to trigger RSS sync', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('triggerRssSync', 'Failed to trigger RSS sync', error, stack);
return Future.error(error);
}
}
Future<bool> triggerBackup() async {
try {
await _dio.post(
'command',
data: json.encode({
'name': 'Backup',
}),
);
return true;
} on DioError catch (error, stack) {
logError('triggerBackup', 'Failed to backup database', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('triggerBackup', 'Failed to backup database', error, stack);
return Future.error(error);
}
}
Future<bool> toggleArtistMonitored(int artistID, bool status) async {
try {
Response response = await _dio.get('artist/$artistID');
Map body = response.data;
body['monitored'] = status;
response = await _dio.put(
'artist',
data: json.encode(body),
);
return true;
} on DioError catch (error, stack) {
logError('toggleArtistMonitored', 'Failed to toggle artist monitored status ($artistID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('toggleArtistMonitored', 'Failed to toggle artist monitored status ($artistID)', error, stack);
return Future.error(error);
}
}
Future<bool> toggleAlbumMonitored(int albumID, bool status) async {
try {
Response response = await _dio.get(
'album',
queryParameters: {
'albumIds': albumID,
}
);
Map body = response.data[0];
body['monitored'] = status;
response = await _dio.put(
'album',
data: json.encode(body),
);
return true;
} on DioError catch (error, stack) {
logError('toggleAlbumMonitored', 'Failed to toggle album monitored status ($albumID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('toggleAlbumMonitored', 'Failed to toggle album monitored status ($albumID)', error, stack);
return Future.error(error);
}
}
Future<List<LidarrSearchData>> searchArtists(String search) async {
if(search == '') return [];
try {
Response response = await _dio.get(
'artist/lookup',
queryParameters: {
'term': search,
}
);
List<LidarrSearchData> entries = [];
for(var entry in response.data) {
entries.add(LidarrSearchData(
title: entry['artistName'] ?? 'Unknown Artist Name',
foreignArtistId: entry['foreignArtistId'] ?? '',
overview: entry['overview'] == null || entry['overview'] == '' ? 'No summary is available.' : entry['overview'],
tadbId: entry['tadbId'] ?? 0,
artistType: entry['artistType'] ?? 'Unknown Artist Type',
links: entry['links'] ?? [],
images: entry['images'] ?? [],
));
}
return entries;
} on DioError catch (error, stack) {
logError('searchArtists', 'Failed to search ($search)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('searchArtists', 'Failed to search ($search)', error, stack);
return Future.error(error);
}
}
Future<int> addArtist(LidarrSearchData entry, LidarrQualityProfile quality, LidarrRootFolder rootFolder, LidarrMetadataProfile metadata, bool monitored, bool albumFolders, {bool search = false}) async {
try {
Response response = await _dio.post(
'artist',
data: json.encode({
'ArtistName': entry.title,
'foreignArtistId': entry.foreignArtistId,
'qualityProfileId': quality.id,
'metadataProfileId': metadata.id,
'rootFolderPath': rootFolder.path,
'monitored': monitored,
'albumFolder': albumFolders,
'addOptions': {
'searchForMissingAlbums': search,
},
}),
);
return response.data['id'];
} on DioError catch (error, stack) {
logError('addArtist', 'Failed to add artist (${entry.title})', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('addArtist', 'Failed to add artist (${entry.title})', error, stack);
return Future.error(error);
}
}
Future<List<LidarrReleaseData>> getReleases(int albumID) async {
try {
Response response = await _dio.get(
'release',
queryParameters: {
'albumId': albumID,
},
);
List<LidarrReleaseData> entries = [];
for(var entry in response.data) {
entries.add(LidarrReleaseData(
title: entry['title'] ?? 'Unknown Title',
guid: entry['guid'] ?? '',
quality: entry['quality']['quality']['name'] ?? 'Unknown',
protocol: entry['protocol'] ?? 'Unknown Protocol',
indexer: entry['indexer'] ?? 'Unknown Indexer',
infoUrl: entry['infoUrl'] ?? '',
approved: entry['approved'] ?? false,
releaseWeight: entry['releaseWeight'] ?? 0,
size: entry['size'] ?? 0,
indexerId: entry['indexerId'] ?? 0,
ageHours: entry['ageHours'] ?? 0,
rejections: entry['rejections'] ?? [],
seeders: entry['seeders'] ?? 0,
leechers: entry['leechers'] ?? 0,
));
}
return entries;
} on DioError catch (error, stack) {
logError('getReleases', 'Failed to fetch releases ($albumID)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('getReleases', 'Failed to fetch releases ($albumID)', error, stack);
return Future.error(error);
}
}
Future<bool> downloadRelease(String guid, int indexerId) async {
try {
await _dio.post(
'release',
data: json.encode({
'guid': guid,
'indexerId': indexerId,
}),
);
return true;
} on DioError catch (error, stack) {
logError('downloadRelease', 'Failed to download release ($guid)', error, stack, uploadToSentry: false);
return Future.error(error);
} catch (error, stack) {
logError('downloadRelease', 'Failed to download release ($guid)', error, stack);
return Future.error(error);
}
}
}