import 'package:flutter/material.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/extensions/string/string.dart'; import 'package:lunasea/modules/sonarr.dart'; class SonarrAPIController { Future downloadRelease({ required BuildContext context, required SonarrRelease release, bool showSnackbar = true, }) async { if (context.read().enabled) { return context .read() .api! .release .add( indexerId: release.indexerId!, guid: release.guid!, ) .then((_) { if (showSnackbar) { showLunaSuccessSnackBar( title: 'sonarr.DownloadingRelease'.tr(), message: release.title.uiSafe(), ); } return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to set download release (${release.guid})', error, stack, ); if (showSnackbar) { showLunaErrorSnackBar( title: 'sonarr.FailedToDownloadRelease'.tr(), error: error, ); } return false; }); } return false; } Future toggleEpisodeMonitored({ required BuildContext context, required SonarrEpisode episode, bool showSnackbar = true, }) async { SonarrEpisode _episode = episode.clone(); _episode.monitored = !_episode.monitored!; if (context.read().enabled) { return context.read().api!.episode.setMonitored( episodeIds: [_episode.id!], monitored: _episode.monitored!, ).then((_) { context.read().setSingleEpisode(_episode); if (showSnackbar) { showLunaSuccessSnackBar( title: _episode.monitored! ? 'sonarr.Monitoring'.tr() : 'sonarr.NoLongerMonitoring'.tr(), message: _episode.title, ); } return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to set episode monitored state (${_episode.id})', error, stack, ); if (showSnackbar) { showLunaErrorSnackBar( title: _episode.monitored! ? 'sonarr.FailedToMonitorEpisode'.tr() : 'sonarr.FailedToUnmonitorEpisode'.tr(), error: error, ); } return false; }); } return false; } Future deleteEpisode({ required BuildContext context, required SonarrEpisode episode, required SonarrEpisodeFile episodeFile, bool showSnackbar = true, }) async { SonarrEpisode _episode = episode.clone(); _episode.hasFile = false; if (context.read().enabled) { return context .read() .api! .episodeFile .delete(episodeFileId: episodeFile.id!) .then((response) { context.read().setSingleEpisode(_episode); if (showSnackbar) { showLunaSuccessSnackBar( title: 'sonarr.EpisodeFileDeleted'.tr(), message: episodeFile.relativePath, ); } return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to delete episode (${episodeFile.id})', error, stack, ); if (showSnackbar) { showLunaErrorSnackBar( title: 'sonarr.FailedToDeleteEpisodeFile'.tr(), error: error, ); } return false; }); } return false; } Future episodeSearch({ required BuildContext context, required SonarrEpisode episode, bool showSnackbar = true, }) async { if (context.read().enabled) { return context .read() .api! .command .episodeSearch(episodeIds: [episode.id!]).then((response) { if (showSnackbar) { showLunaSuccessSnackBar( title: 'sonarr.SearchingForEpisode'.tr(), message: episode.title, ); } return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to search for episode: ${episode.id}', error, stack, ); if (showSnackbar) { showLunaErrorSnackBar( title: 'sonarr.FailedToSearch'.tr(), error: error, ); } return false; }); } return false; } Future toggleSeasonMonitored({ required BuildContext context, required SonarrSeriesSeason season, required int? seriesId, bool showSnackbar = true, }) async { if (context.read().enabled) { return context.read().series!.then((series) { if (series[seriesId] == null) { throw Exception('Series does not exist in catalogue'); } return series[seriesId]!.clone(); }).then((series) async { series.seasons!.forEach((seriesSeason) { if (seriesSeason.seasonNumber == season.seasonNumber) { seriesSeason.monitored = !seriesSeason.monitored!; } }); await context.read().api!.series.update(series: series); return series; }).then((series) { return context.read().setSingleSeries(series); }).then((series) { if (showSnackbar) { showLunaSuccessSnackBar( title: season.monitored! ? 'sonarr.NoLongerMonitoring'.tr() : 'sonarr.Monitoring'.tr(), message: season.seasonNumber == 0 ? 'Specials' : 'Season ${season.seasonNumber}', ); } return true; }).catchError((error, stack) { LunaLogger().error( 'Unable to toggle season monitored state: ${season.monitored.toString()} to ${(!season.monitored!).toString()}', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: season.monitored! ? 'sonarr.FailedToUnmonitorSeason'.tr() : 'sonarr.FailedToMonitorSeason'.tr(), error: error, ); return false; }); } return false; } Future toggleSeriesMonitored({ required BuildContext context, required SonarrSeries series, bool showSnackbar = true, }) async { if (context.read().enabled) { SonarrSeries seriesCopy = series.clone(); seriesCopy.monitored = !series.monitored!; return await context .read() .api! .series .update(series: seriesCopy) .then((data) async { return await context .read() .setSingleSeries(seriesCopy) .then((_) { if (showSnackbar) { showLunaSuccessSnackBar( title: seriesCopy.monitored! ? 'sonarr.Monitoring'.tr() : 'sonarr.NoLongerMonitoring'.tr(), message: seriesCopy.title, ); } return true; }); }).catchError((error, stack) { LunaLogger().error( 'Unable to toggle monitored state: ${series.monitored.toString()} to ${seriesCopy.monitored.toString()}', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: series.monitored! ? 'sonarr.FailedToUnmonitorSeries'.tr() : 'sonarr.FailedToMonitorSeries'.tr(), error: error, ); return false; }); } return false; } Future addTag({ required BuildContext context, required String label, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .tag .create(label: label) .then((tag) { showLunaSuccessSnackBar( title: 'sonarr.AddedTag'.tr(), message: tag.label, ); return true; }).catchError((error, stack) { LunaLogger().error('Failed to add tag: $label', error, stack); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToAddTag'.tr(), error: error, ); return false; }); } return false; } Future updateSeries({ required BuildContext context, required SonarrSeries series, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .series .update(series: series) .then((_) async { return await context .read() .setSingleSeries(series) .then((_) { if (showSnackbar) { showLunaSuccessSnackBar( title: 'sonarr.UpdatedSeries'.tr(), message: series.title, ); } return true; }); }).catchError((error, stack) { LunaLogger().error( 'Failed to update series: ${series.id}', error, stack, ); showLunaErrorSnackBar( title: 'sonarr.FailedToUpdateSeries'.tr(), error: error, ); return false; }); } return true; } Future backupDatabase({ required BuildContext context, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context.read().api!.command.backup().then((_) { if (showSnackbar) { showLunaSuccessSnackBar( title: 'sonarr.BackingUpDatabase'.tr(args: [LunaUI.TEXT_ELLIPSIS]), message: 'sonarr.BackingUpDatabaseDescription'.tr(), ); } return true; }).catchError((error, stack) { LunaLogger().error( 'Sonarr: Unable to backup database', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToBackupDatabase'.tr(), error: error, ); return false; }); } return false; } Future automaticSeasonSearch({ required BuildContext context, required int? seriesId, required int? seasonNumber, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .command .seasonSearch(seriesId: seriesId!, seasonNumber: seasonNumber!) .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'sonarr.SearchingForSeason'.tr(args: [LunaUI.TEXT_ELLIPSIS]), message: seasonNumber == 0 ? 'sonarr.Specials'.tr() : 'sonarr.SeasonNumber'.tr(args: [seasonNumber.toString()]), ); return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to season search ($seriesId, $seasonNumber)', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToSeasonSearch'.tr(), error: error, ); return false; }); } return false; } Future seriesSearch({ required BuildContext context, required SonarrSeries series, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .command .seriesSearch(seriesId: series.id!) .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'sonarr.SearchingForMonitoredEpisodes'.tr(), message: series.title!, ); return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to search for monitored episodes (${series.id})', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToSearchForMonitoredEpisodes'.tr(), error: error, ); return false; }); } return false; } Future runRSSSync({ required BuildContext context, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context.read().api!.command.rssSync().then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'sonarr.RunningRSSSync'.tr(args: [LunaUI.TEXT_ELLIPSIS]), message: 'sonarr.RunningRSSSyncDescription'.tr(), ); return true; }).catchError((error, stack) { LunaLogger().error( 'Unable to run RSS sync', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToRunRSSSync'.tr(), error: error, ); return false; }); } return false; } Future updateLibrary({ required BuildContext context, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .command .refreshSeries() .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'sonarr.UpdatingLibrary'.tr(args: [LunaUI.TEXT_ELLIPSIS]), message: 'sonarr.UpdatingLibraryDescription'.tr(), ); return true; }).catchError((error, stack) { LunaLogger().error( 'Unable to update library', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToUpdateLibrary'.tr(), error: error, ); return false; }); } return false; } Future missingEpisodesSearch({ required BuildContext context, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .command .missingEpisodeSearch() .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'sonarr.Searching'.tr(args: [LunaUI.TEXT_ELLIPSIS]), message: 'sonarr.SearchingDescription'.tr(), ); return true; }).catchError((error, stack) { LunaLogger().error( 'Sonarr: Unable to search for all missing episodes', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToSearch'.tr(), error: error, ); return false; }); } return false; } Future refreshSeries({ required BuildContext context, required SonarrSeries series, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .command .refreshSeries(seriesId: series.id) .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'lunasea.Refreshing'.tr(), message: series.title, ); return true; }).catchError((error, stack) { LunaLogger().error( 'Sonarr: Unable to refresh movie: ${series.id}', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToRefresh'.tr(), error: error, ); return false; }); } return false; } Future removeSeries({ required BuildContext context, required SonarrSeries series, bool showSnackbar = true, }) async { if (context.read().enabled) { return await context .read() .api! .series .delete( seriesId: series.id!, deleteFiles: SonarrDatabase.REMOVE_SERIES_DELETE_FILES.read(), addImportListExclusion: SonarrDatabase.REMOVE_SERIES_EXCLUSION_LIST.read(), ) .then((_) async { return await context .read() .removeSingleSeries(series.id!) .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: SonarrDatabase.REMOVE_SERIES_DELETE_FILES.read() ? 'sonarr.RemovedSeriesWithFiles'.tr() : 'sonarr.RemovedSeries'.tr(), message: series.title, ); return true; }); }).catchError((error, stack) { LunaLogger().error( 'Failed to remove series: ${series.id}', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToRemoveSeries'.tr(), error: error, ); return false; }); } return false; } Future addSeries({ required BuildContext context, required SonarrSeries series, required SonarrSeriesType seriesType, required bool seasonFolder, required SonarrQualityProfile qualityProfile, required SonarrRootFolder rootFolder, required SonarrSeriesMonitorType monitorType, required List tags, SonarrLanguageProfile? languageProfile, bool showSnackbar = true, }) async { if (context.read().enabled) { series.id = 0; return await context .read() .api! .series .create( series: series, seriesType: seriesType, seasonFolder: seasonFolder, qualityProfile: qualityProfile, languageProfile: languageProfile, rootFolder: rootFolder, monitorType: monitorType, tags: tags, searchForMissingEpisodes: SonarrDatabase.ADD_SERIES_SEARCH_FOR_MISSING.read(), searchForCutoffUnmetEpisodes: SonarrDatabase.ADD_SERIES_SEARCH_FOR_CUTOFF_UNMET.read(), ) .then((series) { if (showSnackbar) { showLunaSuccessSnackBar( title: 'sonarr.AddedSeries'.tr(), message: series.title, ); } return series; }).catchError((error, stack) { LunaLogger().error( 'Failed to add series (tmdbId: ${series.tvdbId})', error, stack, ); if (showSnackbar) { showLunaErrorSnackBar( title: 'sonarr.FailedToAddSeries'.tr(), error: error, ); } }); } return null; } Future removeFromQueue({ required BuildContext context, required SonarrQueueRecord queueRecord, bool showSnackbar = true, }) async { if (context.read().enabled) { return context .read() .api! .queue .delete(id: queueRecord.id!) .then((_) { if (showSnackbar) showLunaSuccessSnackBar( title: 'sonarr.RemovedFromQueue'.tr(), message: queueRecord.title, ); return true; }).catchError((error, stack) { LunaLogger().error( 'Failed to remove queue record: ${queueRecord.id}', error, stack, ); if (showSnackbar) showLunaErrorSnackBar( title: 'sonarr.FailedToRemoveFromQueue'.tr(), error: error, ); return false; }); } return false; } }