private List <ISyncItem> ApplyDownloadStrategy(string stateKey, IPodcastInfo podcastInfo, List <ISyncItem> episodesFound) { switch (podcastInfo.Feed.DownloadStrategy.Value) { case PodcastEpisodeDownloadStrategy.All: return(episodesFound); case PodcastEpisodeDownloadStrategy.HighTide: var state = _stateProvider.GetState(stateKey); var newEpisodes = (from episode in episodesFound where episode.Published > state.DownloadHighTide select episode); var filteredEpisodes = new List <ISyncItem>(1); filteredEpisodes.AddRange(newEpisodes); return(filteredEpisodes); case PodcastEpisodeDownloadStrategy.Latest: episodesFound.Sort((e1, e2) => e2.Published.CompareTo(e1.Published)); var latestEpisodes = new List <ISyncItem>(1); latestEpisodes.AddRange(episodesFound.Take(1)); return(latestEpisodes); default: throw new EnumOutOfRangeException(); } }
/// <summary> /// Find folders that will be empty and that can be purged /// </summary> /// <param name="rootFolder">the root folder for all downloads</param> /// <param name="podcastInfo">info on the podcast to download</param> /// <param name="filesThatWillBeDeleted">files that will be removed and do not count when considering an empty folder</param> /// <returns></returns> public IList <IDirectoryInfo> FindEmptyFoldersToDelete(string rootFolder, IPodcastInfo podcastInfo, IList <IFileInfo> filesThatWillBeDeleted) { List <IDirectoryInfo> foldersToDelete = new List <IDirectoryInfo>(10); if (!podcastInfo.DeleteEmptyFolder.Value) { return(foldersToDelete); } var feedDownloadsFolder = Path.Combine(rootFolder, podcastInfo.Folder); IDirectoryInfo directoryInfo = _directoryInfoProvider.GetDirectoryInfo(feedDownloadsFolder); if (podcastInfo.Feed != null && IsSubFolderBasedNaming(podcastInfo.Feed.NamingStyle.Value)) { CheckIfSubFoldersCanBeDeleted(directoryInfo, filesThatWillBeDeleted, foldersToDelete); } else { if (FolderCanBeDeleted(directoryInfo, filesThatWillBeDeleted)) { foldersToDelete.Add(directoryInfo); } } return(foldersToDelete); }
private static DateTime GetWhenDownloadWasPublished(IPodcastInfo podcastInfo, IFileInfo file) { switch (podcastInfo.Feed.NamingStyle.Value) { case PodcastEpisodeNamingStyle.UrlFileNameAndPublishDateTime: case PodcastEpisodeNamingStyle.UrlFileNameFeedTitleAndPublishDateTime: case PodcastEpisodeNamingStyle.EpisodeTitleAndPublishDateTime: case PodcastEpisodeNamingStyle.UrlFileNameFeedTitleAndPublishDateTimeInfolder: try { return(ConvertFilenameToPublishedDate(Path.GetFileNameWithoutExtension(file.FullName))); } catch (Exception) { return(file.CreationTime); } case PodcastEpisodeNamingStyle.EpisodeTitle: case PodcastEpisodeNamingStyle.UrlFileName: return(file.CreationTime); default: throw new EnumOutOfRangeException("NamingStyle"); } }
private void DownloadFile1() { ReadOnlyControlFile controlFile = new ReadOnlyControlFile(_inputfilename); IPodcastInfo info = GetPodcastInfo(controlFile, 0); DisplayMessage(string.Format("Reading a feed: {0}", info.Feed.Address)); IList <ISyncItem> allEpisodes = GetAllEpisodesInFeed(controlFile, info); if (allEpisodes.Count < 1) { DisplayMessage("No episodes in the feed - dont forget the state.xml file is being used", DisplayLevel.Warning); return; } IList <ISyncItem> firstEpisode = new List <ISyncItem>(1); firstEpisode.Add(allEpisodes.First()); DisplayMessage(string.Format("Downloading Eposode: {0}", firstEpisode.First().EpisodeTitle)); ISyncItemToEpisodeDownloaderTaskConverter converter = _iocContainer.Resolve <ISyncItemToEpisodeDownloaderTaskConverter>(); IEpisodeDownloader[] downloadTasks = converter.ConvertItemsToTasks(firstEpisode, StatusUpdate, ProgressUpdate); // run them in a task pool ITaskPool taskPool = _iocContainer.Resolve <ITaskPool>(); taskPool.RunAllTasks(1, downloadTasks); DisplayMessage(string.Format("Download Complete", allEpisodes.Count)); }
private IPodcastInfo GetPodcastInfo(ReadOnlyControlFile controlFile, int index) { IEnumerable <IPodcastInfo> podcasts = controlFile.GetPodcasts(); IPodcastInfo info = podcasts.ElementAt(index); return(info); }
/// <summary> /// find old downloads that can be deleted /// </summary> /// <param name="rootFolder">the root folder for all downloads</param> /// <param name="podcastInfo">info on the podcast to download</param> /// <returns>list of episodes to be deleted for the supplied podcastInfo</returns> public IList <IFileInfo> FindEpisodesToPurge(string rootFolder, IPodcastInfo podcastInfo) { List <IFileInfo> episodesToDelete = new List <IFileInfo>(10); if (podcastInfo.Feed == null) { // it is optional to have a feed return(episodesToDelete); } var feedDownloadsFolder = Path.Combine(rootFolder, podcastInfo.Folder); var oldestEpisodeToKeep = DateTime.MinValue; if (podcastInfo.Feed.DeleteDownloadsDaysOld.Value < int.MaxValue) { oldestEpisodeToKeep = _timeProvider.UtcNow.AddDays(-podcastInfo.Feed.DeleteDownloadsDaysOld.Value); } if (IsSubFolderBasedNaming(podcastInfo.Feed.NamingStyle.Value)) { ScanSubFoldersForOldFiles(feedDownloadsFolder, oldestEpisodeToKeep, episodesToDelete, podcastInfo); } else { ScanFolderForOldFiles(feedDownloadsFolder, oldestEpisodeToKeep, episodesToDelete, podcastInfo); } return(episodesToDelete); }
protected void SetupFoundEpisodes(IPodcastInfo podcastInfo, List <ISyncItem> episodes) { A.CallTo(() => MockPodcastEpisodeFinder.FindEpisodesToDownload( SOURCE_ROOT, RETRY_TIME, podcastInfo, DIAGS) ).Returns(episodes); }
private void ReadFeed1() { ReadOnlyControlFile controlFile = new ReadOnlyControlFile(_inputfilename); IPodcastInfo info = GetPodcastInfo(controlFile, 0); DisplayMessage(string.Format("Reading a feed: {0}", info.Feed.Address)); IList <ISyncItem> allEpisodes = GetAllEpisodesInFeed(controlFile, info); DisplayMessage(string.Format("Eposodes in feed: {0}", allEpisodes.Count)); foreach (ISyncItem item in allEpisodes) { DisplayMessage(string.Format("Eposode: {0}", item.EpisodeTitle)); } }
private string GetDownloadPathname(string rootFolder, IPodcastInfo podcastInfo, IPodcastFeedItem podcastFeedItem) { var proposedFilename = podcastFeedItem.FileName; switch (podcastInfo.Feed.NamingStyle.Value) { case PodcastEpisodeNamingStyle.UrlFileNameAndPublishDateTime: proposedFilename = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", podcastFeedItem.Published.ToString("yyyy_MM_dd_HHmm", CultureInfo.InvariantCulture), proposedFilename); break; case PodcastEpisodeNamingStyle.UrlFileNameFeedTitleAndPublishDateTime: proposedFilename = string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}", podcastFeedItem.Published.ToString("yyyy_MM_dd_HHmm", CultureInfo.InvariantCulture), podcastInfo.Folder, proposedFilename); break; case PodcastEpisodeNamingStyle.UrlFileNameFeedTitleAndPublishDateTimeInfolder: proposedFilename = string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}_{3}_{4}", podcastFeedItem.Published.ToString("yyyy_MM", CultureInfo.InvariantCulture), _pathUtilities.GetPathSeparator(), podcastFeedItem.Published.ToString("yyyy_MM_dd_HHmm", CultureInfo.InvariantCulture), podcastInfo.Folder, proposedFilename); break; case PodcastEpisodeNamingStyle.EpisodeTitle: proposedFilename = podcastFeedItem.TitleAsFileName; break; case PodcastEpisodeNamingStyle.EpisodeTitleAndPublishDateTime: proposedFilename = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", podcastFeedItem.Published.ToString("yyyy_MM_dd_HHmm", CultureInfo.InvariantCulture), podcastFeedItem.TitleAsFileName); break; case PodcastEpisodeNamingStyle.UrlFileName: break; default: throw new EnumOutOfRangeException("NamingStyle"); } return(Path.Combine(Path.Combine(rootFolder, podcastInfo.Folder), proposedFilename)); }
public virtual void CancelEdit() { _podcast = _backupPodcastInfo; _backupPodcastInfo = null; }
public virtual void AcceptEdit() { _backupPodcastInfo = null; }
public virtual void StartEditing() { _backupPodcastInfo = _podcast.Clone() as PodcastInfo; }
public PodcastViewModel(IPodcastInfo podcast) { _podcast = podcast; }
private void OnStatusVerbose(string message, IPodcastInfo podcastInfo) { OnStatusUpdate(new StatusUpdateEventArgs(StatusUpdateLevel.Verbose, message, false, podcastInfo)); }
private void ScanFolderForOldFiles(string folderToScan, DateTime oldestEpisodeToKeep, List <IFileInfo> episodesToDelete, IPodcastInfo podcastInfo) { IDirectoryInfo directoryInfo = _directoryInfoProvider.GetDirectoryInfo(folderToScan); IFileInfo[] files; try { files = directoryInfo.GetFiles(podcastInfo.Pattern.Value); } catch (DirectoryNotFoundException) { // if the folder is not there then there is nothing to do return; } foreach (IFileInfo file in files) { var extension = Path.GetExtension(file.FullName); if (extension != null && extension.ToUpperInvariant() == ".XML") { // do not delete the state file continue; } if (GetWhenDownloadWasPublished(podcastInfo, file) < oldestEpisodeToKeep) { episodesToDelete.Add(file); } } }
private void ScanSubFoldersForOldFiles(string folderToScan, DateTime oldestEpisodeToKeep, List <IFileInfo> episodesToDelete, IPodcastInfo podcastInfo) { IDirectoryInfo directoryInfo = _directoryInfoProvider.GetDirectoryInfo(folderToScan); IDirectoryInfo[] subFolders; try { subFolders = directoryInfo.GetDirectories("*.*"); } catch (DirectoryNotFoundException) { // if the folder is not there then there is nothing to do return; } foreach (IDirectoryInfo subFolder in subFolders) { ScanFolderForOldFiles(subFolder.FullName, oldestEpisodeToKeep, episodesToDelete, podcastInfo); } }
private void OnStatusError(string message, IPodcastInfo podcastInfo) { OnStatusUpdate(new StatusUpdateEventArgs(StatusUpdateLevel.Error, message, false, podcastInfo)); }
private IList <ISyncItem> GetAllEpisodesInFeed(ReadOnlyControlFile controlFile, IPodcastInfo info) { List <ISyncItem> allEpisodes = new List <ISyncItem>(20); IEpisodeFinder podcastEpisodeFinder = _iocContainer.Resolve <IEpisodeFinder>(); IList <ISyncItem> episodesInThisFeed = podcastEpisodeFinder.FindEpisodesToDownload( controlFile.GetSourceRoot(), controlFile.GetRetryWaitInSeconds(), info, controlFile.GetDiagnosticRetainTemporaryFiles() ); allEpisodes.AddRange(episodesInThisFeed); return(allEpisodes); }
/// <summary> /// replace any token elements in a command line and return a full command ready to be executed /// </summary> /// <param name="tokenisedCommand">tokenised command</param> /// <param name="rootFolder">the root folder for all downloads</param> /// <param name="destinationPathname">the full pathname for the download</param> /// <param name="podcast">the podcast, used as a source of tokens</param> /// <returns>a full command line, NULL if there is no command to execute</returns> public IExternalCommand ReplaceTokensInCommand(ITokenisedCommand tokenisedCommand, string rootFolder, string destinationPathname, IPodcastInfo podcast) { if (tokenisedCommand == null) { return(null); } var command = new ExternalCommand(); command.Command = ReplaceTokensInString(tokenisedCommand.Command.Value, rootFolder, destinationPathname, podcast); command.Arguments = ReplaceTokensInString(tokenisedCommand.Arguments.Value, rootFolder, destinationPathname, podcast); command.WorkingDirectory = ReplaceTokensInString(tokenisedCommand.WorkingDirectory.Value, rootFolder, destinationPathname, podcast); return(command); }
public IList <ISyncItem> FindEpisodesToDownload(string rootFolder, int retryWaitTimeInSeconds, IPodcastInfo podcastInfo, bool retainFeedStream) { List <ISyncItem> episodesToDownload = new List <ISyncItem>(10); if (podcastInfo.Feed == null) { // it is optional to have a feed return(episodesToDownload); } var stateKey = Path.Combine(rootFolder, podcastInfo.Folder); string feedSaveFile = null; if (retainFeedStream) { CreateFolderIfNeeded(stateKey); feedSaveFile = Path.Combine(Path.Combine(rootFolder, podcastInfo.Folder), "last_download_feed.xml"); } using (var webClient = _webClientFactory.CreateWebClient()) { var downloader = new Downloader(webClient, _feedFactory); try { var feed = downloader.DownloadFeed(podcastInfo.Feed.Format.Value, podcastInfo.Feed.Address, feedSaveFile); feed.StatusUpdate += StatusUpdate; var episodes = feed.Episodes; var oldestEpisodeToAccept = DateTime.MinValue; if (podcastInfo.Feed.MaximumDaysOld.Value < int.MaxValue) { oldestEpisodeToAccept = _timeProvider.UtcNow.AddDays(-podcastInfo.Feed.MaximumDaysOld.Value); } foreach (IPodcastFeedItem podcastFeedItem in episodes) { if (podcastFeedItem.Published > oldestEpisodeToAccept) { var destinationPath = GetDownloadPathname(rootFolder, podcastInfo, podcastFeedItem); if (!_fileUtilities.FileExists(destinationPath)) { var downloadItem = new SyncItem() { Id = Guid.NewGuid(), StateKey = stateKey, RetryWaitTimeInSeconds = retryWaitTimeInSeconds, Published = podcastFeedItem.Published, EpisodeUrl = podcastFeedItem.Address, DestinationPath = destinationPath, EpisodeTitle = string.Format(CultureInfo.InvariantCulture, "{0} {1}", podcastInfo.Folder, podcastFeedItem.EpisodeTitle), PostDownloadCommand = _commandGenerator.ReplaceTokensInCommand(podcastInfo.PostDownloadCommand, rootFolder, destinationPath, podcastInfo), }; episodesToDownload.Add(downloadItem); } else { OnStatusVerbose(string.Format(CultureInfo.InvariantCulture, "Episode already downloaded: {0}", podcastFeedItem.EpisodeTitle), podcastInfo); } } else { OnStatusVerbose(string.Format(CultureInfo.InvariantCulture, "Episode too old: {0}", podcastFeedItem.EpisodeTitle), podcastInfo); } } } catch (Exception e) { OnStatusError(string.Format(CultureInfo.InvariantCulture, "Error processing feed {0}: {1}", podcastInfo.Feed.Address, e.Message), podcastInfo); } } var filteredEpisodes = ApplyDownloadStrategy(stateKey, podcastInfo, episodesToDownload); foreach (var filteredEpisode in filteredEpisodes) { OnStatusMessageUpdate(string.Format(CultureInfo.InvariantCulture, "Queued: {0}", filteredEpisode.EpisodeTitle), podcastInfo); } return(filteredEpisodes); }
protected override void When() { CreatedPodcast = PodcastFactory.CreatePodcast(_controlFile); }
private string ReplaceTokensInString(string input, string rootFolder, string destinationPathname, IPodcastInfo podcast) { if (input == null) { return(null); } string returnValue = input.Replace("{downloadfullpath}", destinationPathname); returnValue = returnValue.Replace("{downloadroot}", rootFolder); returnValue = returnValue.Replace("{downloadfolder}", podcast.Folder); returnValue = returnValue.Replace("{exefolder}", GetExeFolder()); return(returnValue); }