/// <summary> /// Imports the resource with the given <paramref name="mediaItemAccessor"/>. /// </summary> /// <remarks> /// This method will be called for file resources as well as for directory resources because some metadata extractors /// extract their metadata from directories. /// </remarks> /// <param name="mediaItemAccessor">File or directory resource to be imported.</param> /// <param name="parentDirectoryId">Media item id of the parent directory, if present, else <see cref="Guid.Empty"/>.</param> /// <param name="metadataExtractors">Collection of metadata extractors to apply to the given resoure.</param> /// <param name="resultHandler">Callback to notify the import results.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> /// <returns><c>true</c>, if metadata could be extracted from the given <paramref name="mediaItemAccessor"/>, else /// <c>false</c>.</returns> protected bool ImportResource(ImportJob importJob, IResourceAccessor mediaItemAccessor, Guid parentDirectoryId, ICollection <IMetadataExtractor> metadataExtractors, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { const bool forceQuickMode = false; // Allow extractions with probably longer runtime. ResourcePath path = mediaItemAccessor.CanonicalLocalResourcePath; ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportStatus, path); IDictionary <Guid, IList <MediaItemAspect> > aspects = mediaAccessor.ExtractMetadataAsync(mediaItemAccessor, metadataExtractors, forceQuickMode).Result; if (aspects == null) { // No metadata could be extracted return(false); } using (CancellationTokenSource cancelToken = new CancellationTokenSource()) { try { resultHandler.UpdateMediaItemAsync(parentDirectoryId, path, MediaItemAspect.GetAspects(aspects), importJob.JobType == ImportJobType.Refresh, importJob.BasePath); resultHandler.DeleteUnderPathAsync(path); } catch { cancelToken.Cancel(); throw; } } return(true); }
/// <summary> /// Imports or refreshes a single file without a parent directory with the specified <paramref name="fileAccessor"/>. /// </summary> /// <param name="importJob">The import job being processed.</param> /// <param name="fileAccessor">Resource accessor for the file to import.</param> /// <param name="metadataExtractors">Metadata extractors to apply on the resource.</param> /// <param name="mediaBrowsing">Callback interface to the media library for the refresh import type.</param> /// <param name="resultHandler">Callback to notify the import result.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> protected void ImportSingleFile(ImportJob importJob, IResourceAccessor fileAccessor, ICollection <IMetadataExtractor> metadataExtractors, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { ResourcePath currentFilePath = fileAccessor.CanonicalLocalResourcePath; try { if (importJob.JobType == ImportJobType.Refresh) { MediaItem mediaItem = mediaBrowsing.LoadLocalItem(currentFilePath, IMPORTER_MIA_ID_ENUMERATION, EMPTY_MIA_ID_ENUMERATION); MediaItemAspect importerAspect; if (mediaItem != null && mediaItem.Aspects.TryGetValue(ImporterAspect.ASPECT_ID, out importerAspect) && (DateTime)importerAspect[ImporterAspect.ATTR_LAST_IMPORT_DATE] > fileAccessor.LastChanged) { return; } } ImportResource(fileAccessor, Guid.Empty, metadataExtractors, resultHandler, mediaAccessor); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Problem while importing resource '{0}'", e, currentFilePath); importJob.State = ImportJobState.Erroneous; } }
protected Guid GetOrAddDirectory(ImportJob importJob, IFileSystemResourceAccessor directoryAccessor, Guid parentDirectoryId, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler) { ResourcePath directoryPath = directoryAccessor.CanonicalLocalResourcePath; MediaItem directoryItem = mediaBrowsing.LoadLocalItemAsync(directoryPath, EMPTY_MIA_ID_ENUMERATION, DIRECTORY_MIA_ID_ENUMERATION).Result; if (directoryItem != null) { SingleMediaItemAspect da; if (!MediaItemAspect.TryGetAspect(directoryItem.Aspects, DirectoryAspect.Metadata, out da)) { // This is the case if the path was formerly imported as a non-directory media item; we cannot reuse it resultHandler.DeleteMediaItemAsync(directoryPath); directoryItem = null; } } if (directoryItem == null) { // Add directory item to ML MediaItemAspect mia = new SingleMediaItemAspect(MediaAspect.Metadata); mia.SetAttribute(MediaAspect.ATTR_TITLE, directoryAccessor.ResourceName); mia.SetAttribute(MediaAspect.ATTR_SORT_TITLE, directoryAccessor.ResourceName); mia.SetAttribute(MediaAspect.ATTR_ISVIRTUAL, false); mia.SetAttribute(MediaAspect.ATTR_RECORDINGTIME, DateTime.MinValue); mia.SetAttribute(MediaAspect.ATTR_RATING, 0); mia.SetAttribute(MediaAspect.ATTR_COMMENT, null); mia.SetAttribute(MediaAspect.ATTR_LASTPLAYED, DateTime.MinValue); MediaItemAspect da = new SingleMediaItemAspect(DirectoryAspect.Metadata); IList <MediaItemAspect> aspects = new List <MediaItemAspect>(new[] { mia, da, }); return(resultHandler.UpdateMediaItemAsync(parentDirectoryId, directoryPath, aspects, importJob.JobType == ImportJobType.Refresh, importJob.BasePath).Result); } return(directoryItem.MediaItemId); }
public void ScheduleRefresh(ResourcePath path, IEnumerable <string> mediaCategories, bool includeSubDirectories) { ICollection <ImportJob> importJobs; lock (_syncObj) importJobs = new List <ImportJob>(_importJobs); ICollection <ImportJob> removeImportJobs = new List <ImportJob>(); foreach (ImportJob checkJob in importJobs) { if (checkJob.BasePath.IsSameOrParentOf(path)) { // The new job is included in an already existing job - re-schedule existing job path = checkJob.BasePath; checkJob.Cancel(); removeImportJobs.Add(checkJob); } if (path.IsParentOf(checkJob.BasePath)) { // The new job will include the old job checkJob.Cancel(); removeImportJobs.Add(checkJob); } } lock (_syncObj) foreach (ImportJob removeJob in removeImportJobs) { _importJobs.Remove(removeJob); } ICollection <Guid> metadataExtractorIds = GetMetadataExtractorIdsForMediaCategories(mediaCategories); ImportJob job = new ImportJob(ImportJobType.Refresh, path, metadataExtractorIds, includeSubDirectories); EnqueueImportJob(job); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportScheduled, path, ImportJobType.Refresh); }
protected void EnqueueImportJob(ImportJob job) { lock (_syncObj) { job.State = ImportJobState.Scheduled; _importJobs.Insert(0, job); _importJobsReadyAvailableEvent.Set(); } }
public override bool Equals(object obj) { if (!(obj is ImportJob)) { return(false); } ImportJob other = (ImportJob)obj; return(_jobInfo.Equals(other._jobInfo)); }
protected ImportJob DequeueImportJob() { lock (_syncObj) { ImportJob job = PeekImportJob(); if (job != null) { _importJobs.RemoveAt(_importJobs.Count - 1); } return(job); } }
/// <summary> /// Imports or refreshes a single file without a parent directory with the specified <paramref name="resourceAccessor"/>. /// </summary> /// <param name="importJob">The import job being processed.</param> /// <param name="resourceAccessor">Resource accessor for the file to import.</param> /// <param name="metadataExtractors">Metadata extractors to apply on the resource.</param> /// <param name="mediaBrowsing">Callback interface to the media library for the refresh import type.</param> /// <param name="resultHandler">Callback to notify the import result.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> protected void ImportSingleFile(ImportJob importJob, IResourceAccessor resourceAccessor, ICollection <IMetadataExtractor> metadataExtractors, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { ResourcePath currentFilePath = resourceAccessor.CanonicalLocalResourcePath; try { ImportResource(importJob, resourceAccessor, Guid.Empty, metadataExtractors, resultHandler, mediaAccessor); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Problem while importing resource '{0}'", e, currentFilePath); importJob.State = ImportJobState.Erroneous; } }
public void ScheduleImport(ResourcePath path, IEnumerable <string> mediaCategories, bool includeSubDirectories) { ICollection <ImportJob> importJobs; lock (_syncObj) importJobs = new List <ImportJob>(_importJobs); if (importJobs.Any(checkJob => checkJob.JobType == ImportJobType.Import && checkJob.BasePath.IsSameOrParentOf(path))) { // Path is already being scheduled as Import job // => the new job is already included in an already existing job return; } CancelJobsForPath(path); ICollection <Guid> metadataExtractorIds = GetMetadataExtractorIdsForMediaCategories(mediaCategories); ImportJob job = new ImportJob(ImportJobType.Import, path, metadataExtractorIds, includeSubDirectories); EnqueueImportJob(job); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportScheduled, path, ImportJobType.Import); }
/// <summary> /// Imports or refreshes a single file without a parent directory with the specified <paramref name="fileAccessor"/>. /// </summary> /// <param name="importJob">The import job being processed.</param> /// <param name="fileAccessor">Resource accessor for the file to import.</param> /// <param name="metadataExtractors">Metadata extractors to apply on the resource.</param> /// <param name="mediaBrowsing">Callback interface to the media library for the refresh import type.</param> /// <param name="resultHandler">Callback to notify the import result.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> protected void ImportSingleFile(ImportJob importJob, IResourceAccessor fileAccessor, ICollection<IMetadataExtractor> metadataExtractors, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { ResourcePath currentFilePath = fileAccessor.CanonicalLocalResourcePath; try { if (importJob.JobType == ImportJobType.Refresh) { MediaItem mediaItem = mediaBrowsing.LoadLocalItem(currentFilePath, IMPORTER_MIA_ID_ENUMERATION, EMPTY_MIA_ID_ENUMERATION); MediaItemAspect importerAspect; if (mediaItem != null && mediaItem.Aspects.TryGetValue(ImporterAspect.ASPECT_ID, out importerAspect) && (DateTime) importerAspect[ImporterAspect.ATTR_LAST_IMPORT_DATE] > fileAccessor.LastChanged) return; } ImportResource(fileAccessor, Guid.Empty, metadataExtractors, resultHandler, mediaAccessor); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Problem while importing resource '{0}'", e, currentFilePath); importJob.State = ImportJobState.Erroneous; } }
/// <summary> /// Executes the given <paramref name="importJob"/>. /// </summary> /// <remarks> /// This method automatically terminates if it encounters that this importer worker was suspended or that the /// given <paramref name="importJob"/> was cancelled. /// </remarks> /// <param name="importJob">Import job to be executed. The state variables of this parameter will be updated by /// this method.</param> protected void Process(ImportJob importJob) { ImportJobState state = importJob.State; if (state == ImportJobState.Finished || state == ImportJobState.Cancelled || state == ImportJobState.Erroneous) { return; } // Preparation IMediaAccessor mediaAccessor = ServiceRegistration.Get <IMediaAccessor>(); IImportResultHandler resultHandler; IMediaBrowsing mediaBrowsing; lock (_syncObj) { resultHandler = _importResultHandler; mediaBrowsing = _mediaBrowsingCallback; } if (mediaBrowsing == null || resultHandler == null) { // Can be the case if this importer worker was asynchronously suspended return; } try { try { ICollection <IMetadataExtractor> metadataExtractors = new List <IMetadataExtractor>(); foreach (Guid metadataExtractorId in importJob.MetadataExtractorIds) { IMetadataExtractor extractor; if (!mediaAccessor.LocalMetadataExtractors.TryGetValue(metadataExtractorId, out extractor)) { continue; } metadataExtractors.Add(extractor); } // Prepare import if (state == ImportJobState.Scheduled) { ServiceRegistration.Get <ILogger>().Info("ImporterWorker: Starting import job '{0}'", importJob); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportStarted, importJob.BasePath); IResourceAccessor ra; if (!importJob.BasePath.TryCreateLocalResourceAccessor(out ra)) { throw new ArgumentException(string.Format("Unable to access resource path '{0}'", importJob.BasePath)); } using (ra) { IFileSystemResourceAccessor fsra = ra as IFileSystemResourceAccessor; if (fsra != null) { // Prepare complex import process importJob.PendingResources.Add(new PendingImportResource(Guid.Empty, (IFileSystemResourceAccessor)fsra.Clone())); importJob.State = ImportJobState.Active; } else { // Simple resource import ImportSingleFile(importJob, ra, metadataExtractors, mediaBrowsing, resultHandler, mediaAccessor); lock (importJob.SyncObj) if (importJob.State == ImportJobState.Active) { importJob.State = ImportJobState.Finished; } return; } } } else { ServiceRegistration.Get <ILogger>().Info("ImporterWorker: Resuming import job '{0}' ({1} items pending)", importJob, importJob.PendingResources.Count); } // Actual import process while (importJob.HasPendingResources) { Thread.Sleep(0); CheckImportStillRunning(importJob.State); PendingImportResource pendingImportResource; lock (importJob.SyncObj) pendingImportResource = importJob.PendingResources.FirstOrDefault(); if (pendingImportResource.IsValid) { IFileSystemResourceAccessor fsra = pendingImportResource.ResourceAccessor; int numPending = importJob.PendingResources.Count; string moreResources = numPending > 1 ? string.Format(" ({0} more resources pending)", numPending) : string.Empty; ServiceRegistration.Get <ILogger>().Info("ImporterWorker: Importing '{0}'{1}", fsra.ResourcePathName, moreResources); if (fsra.IsFile && fsra.Exists) { ImportResource(importJob, fsra, pendingImportResource.ParentDirectory, metadataExtractors, resultHandler, mediaAccessor); } else if (!fsra.IsFile) { CheckImportStillRunning(importJob.State); Guid?currentDirectoryId = ImportDirectory(importJob, pendingImportResource.ParentDirectory, fsra, metadataExtractors, mediaBrowsing, resultHandler, mediaAccessor); CheckImportStillRunning(importJob.State); if (currentDirectoryId.HasValue && importJob.IncludeSubDirectories) { // Add subdirectories in front of work queue lock (importJob.SyncObj) { ICollection <IFileSystemResourceAccessor> directories = FileSystemResourceNavigator.GetChildDirectories(fsra, false); if (directories != null) { foreach (IFileSystemResourceAccessor childDirectory in directories) { importJob.PendingResources.Insert(0, new PendingImportResource(currentDirectoryId.Value, childDirectory)); } } } } } else { ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Cannot import resource '{0}': It's neither a file nor a directory", fsra.CanonicalLocalResourcePath.Serialize()); } } lock (importJob.SyncObj) importJob.PendingResources.Remove(pendingImportResource); pendingImportResource.Dispose(); } lock (importJob.SyncObj) if (importJob.State == ImportJobState.Active) { importJob.State = ImportJobState.Finished; } ServiceRegistration.Get <ILogger>().Info("ImporterWorker: Finished import job '{0}'", importJob); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportCompleted, importJob.BasePath); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Problem processing '{0}'", e, importJob); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportCompleted, importJob.BasePath); importJob.State = ImportJobState.Erroneous; } } catch (ImportSuspendedException) { ServiceRegistration.Get <ILogger>().Info("ImporterWorker: Suspending import job '{0}' ({1} items pending - will be continued next time)", importJob, importJob.PendingResources.Count); } catch (ImportAbortException) { ServiceRegistration.Get <ILogger>().Info("ImporterWorker: Aborting import job '{0}' ({1} items pending)", importJob, importJob.PendingResources.Count); } }
/// <summary> /// Imports or refreshes the directory with the specified <paramref name="directoryAccessor"/>. Sub directories will not /// be processed in this method. /// </summary> /// <param name="importJob">The import job being processed.</param> /// <param name="parentDirectoryId">Media item id of the parent directory, if present, else <see cref="Guid.Empty"/>.</param> /// <param name="directoryAccessor">Resource accessor for the directory to import.</param> /// <param name="metadataExtractors">Metadata extractors to apply on the resources.</param> /// <param name="mediaBrowsing">Callback interface to the media library for the refresh import type.</param> /// <param name="resultHandler">Callback to notify the import result.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> /// <returns>Id of the directory's media item or <c>null</c>, if the given <paramref name="directoryAccessor"/> /// was imported as a media item or if an error occured. If <c>null</c> is returned, the directory import should be /// considered to be finished.</returns> protected Guid?ImportDirectory(ImportJob importJob, Guid parentDirectoryId, IFileSystemResourceAccessor directoryAccessor, ICollection <IMetadataExtractor> metadataExtractors, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { ResourcePath currentDirectoryPath = directoryAccessor.CanonicalLocalResourcePath; try { ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportStatus, currentDirectoryPath); if (ImportResource(importJob, directoryAccessor, parentDirectoryId, metadataExtractors, resultHandler, mediaAccessor)) { // The directory could be imported as a media item. // If the directory itself was identified as a normal media item, don't import its children. // Necessary for DVD directories, for example. return(null); } Guid directoryId = GetOrAddDirectory(importJob, directoryAccessor, parentDirectoryId, mediaBrowsing, resultHandler); IDictionary <string, MediaItem> path2Item = new Dictionary <string, MediaItem>(); if (importJob.JobType == ImportJobType.Refresh) { foreach (MediaItem mediaItem in mediaBrowsing.BrowseAsync(directoryId, IMPORTER_PROVIDER_MIA_ID_ENUMERATION, EMPTY_MIA_ID_ENUMERATION, null, false).Result) { IList <MultipleMediaItemAspect> providerResourceAspects; if (MediaItemAspect.TryGetAspects(mediaItem.Aspects, ProviderResourceAspect.Metadata, out providerResourceAspects)) { path2Item[providerResourceAspects[0].GetAttributeValue <string>(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH)] = mediaItem; } } } CheckImportStillRunning(importJob.State); ICollection <IFileSystemResourceAccessor> files = FileSystemResourceNavigator.GetFiles(directoryAccessor, false); if (files != null) { foreach (IFileSystemResourceAccessor fileAccessor in files) { using (fileAccessor) { // Add & update files ResourcePath currentFilePath = fileAccessor.CanonicalLocalResourcePath; string serializedFilePath = currentFilePath.Serialize(); try { SingleMediaItemAspect importerAspect; MediaItem mediaItem; if (importJob.JobType == ImportJobType.Refresh && path2Item.TryGetValue(serializedFilePath, out mediaItem) && MediaItemAspect.TryGetAspect(mediaItem.Aspects, ImporterAspect.Metadata, out importerAspect) && importerAspect.GetAttributeValue <DateTime>(ImporterAspect.ATTR_LAST_IMPORT_DATE) > fileAccessor.LastChanged) { // We can skip this file; it was imported after the last change time of the item path2Item.Remove(serializedFilePath); continue; } if (ImportResource(importJob, fileAccessor, directoryId, metadataExtractors, resultHandler, mediaAccessor)) { path2Item.Remove(serializedFilePath); } } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Problem while importing resource '{0}'", e, serializedFilePath); importJob.State = ImportJobState.Erroneous; } CheckImportStillRunning(importJob.State); } } } if (importJob.JobType == ImportJobType.Refresh) { // Remove remaining (= non-present) files foreach (string pathStr in path2Item.Keys) { ResourcePath path = ResourcePath.Deserialize(pathStr); try { IResourceAccessor ra; if (!path.TryCreateLocalResourceAccessor(out ra)) { throw new IllegalCallException("Unable to access resource path '{0}'", importJob.BasePath); } using (ra) { IFileSystemResourceAccessor fsra = ra as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) { // Don't touch directories because they will be imported in a different call of ImportDirectory continue; } } } catch (IllegalCallException) { // This happens if the resource doesn't exist any more - we also catch missing directories here } // Delete all remaining items resultHandler.DeleteMediaItemAsync(path); CheckImportStillRunning(importJob.State); } } return(directoryId); } catch (ImportSuspendedException) { throw; } catch (ImportAbortException) { throw; } catch (UnauthorizedAccessException e) { // If the access to the file or folder was denied, simply continue with the others ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Problem accessing resource '{0}', continueing with one", e, currentDirectoryPath); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get <ILogger>().Warn("ImporterWorker: Problem while importing directory '{0}'", e, currentDirectoryPath); importJob.State = ImportJobState.Erroneous; } return(null); }
protected void EnqueueImportJob(ImportJob job) { lock (_syncObj) { job.State = ImportJobState.Scheduled; _importJobs.Insert(0, job); _importJobsReadyAvailableEvent.Set(); } }
protected void RemoveImportJob(ImportJob job) { lock (_syncObj) _importJobs.Remove(job); job.Dispose(); }
protected void RemoveImportJob(ImportJob job) { lock (_syncObj) _importJobs.Remove(job); job.Dispose(); }
/// <summary> /// Imports or refreshes a single file without a parent directory with the specified <paramref name="resourceAccessor"/>. /// </summary> /// <param name="importJob">The import job being processed.</param> /// <param name="resourceAccessor">Resource accessor for the file to import.</param> /// <param name="metadataExtractors">Metadata extractors to apply on the resource.</param> /// <param name="mediaBrowsing">Callback interface to the media library for the refresh import type.</param> /// <param name="resultHandler">Callback to notify the import result.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> protected void ImportSingleFile(ImportJob importJob, IResourceAccessor resourceAccessor, ICollection<IMetadataExtractor> metadataExtractors, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { ResourcePath currentFilePath = resourceAccessor.CanonicalLocalResourcePath; try { ImportResource(resourceAccessor, Guid.Empty, metadataExtractors, resultHandler, mediaAccessor); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Problem while importing resource '{0}'", e, currentFilePath); importJob.State = ImportJobState.Erroneous; } }
public void ScheduleRefresh(ResourcePath path, IEnumerable<string> mediaCategories, bool includeSubDirectories) { ICollection<ImportJob> importJobs; lock (_syncObj) importJobs = new List<ImportJob>(_importJobs); ICollection<ImportJob> removeImportJobs = new List<ImportJob>(); foreach (ImportJob checkJob in importJobs) { if (checkJob.BasePath.IsSameOrParentOf(path)) { // The new job is included in an already existing job - re-schedule existing job path = checkJob.BasePath; checkJob.Cancel(); removeImportJobs.Add(checkJob); } if (path.IsParentOf(checkJob.BasePath)) { // The new job will include the old job checkJob.Cancel(); removeImportJobs.Add(checkJob); } } lock (_syncObj) foreach (ImportJob removeJob in removeImportJobs) _importJobs.Remove(removeJob); ICollection<Guid> metadataExtractorIds = GetMetadataExtractorIdsForMediaCategories(mediaCategories); ImportJob job = new ImportJob(ImportJobType.Refresh, path, metadataExtractorIds, includeSubDirectories); EnqueueImportJob(job); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportScheduled, path, ImportJobType.Refresh); }
public void ScheduleImport(ResourcePath path, IEnumerable<string> mediaCategories, bool includeSubDirectories) { ICollection<ImportJob> importJobs; lock (_syncObj) importJobs = new List<ImportJob>(_importJobs); if (importJobs.Any(checkJob => checkJob.JobType == ImportJobType.Import && checkJob.BasePath.IsSameOrParentOf(path))) // Path is already being scheduled as Import job // => the new job is already included in an already existing job return; CancelJobsForPath(path); ICollection<Guid> metadataExtractorIds = GetMetadataExtractorIdsForMediaCategories(mediaCategories); ImportJob job = new ImportJob(ImportJobType.Import, path, metadataExtractorIds, includeSubDirectories); EnqueueImportJob(job); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportScheduled, path, ImportJobType.Import); }
/// <summary> /// Executes the given <paramref name="importJob"/>. /// </summary> /// <remarks> /// This method automatically terminates if it encounters that this importer worker was suspended or that the /// given <paramref name="importJob"/> was cancelled. /// </remarks> /// <param name="importJob">Import job to be executed. The state variables of this parameter will be updated by /// this method.</param> protected void Process(ImportJob importJob) { ImportJobState state = importJob.State; if (state == ImportJobState.Finished || state == ImportJobState.Cancelled || state == ImportJobState.Erroneous) return; // Preparation IMediaAccessor mediaAccessor = ServiceRegistration.Get<IMediaAccessor>(); IImportResultHandler resultHandler; IMediaBrowsing mediaBrowsing; lock (_syncObj) { resultHandler = _importResultHandler; mediaBrowsing = _mediaBrowsingCallback; } if (mediaBrowsing == null || resultHandler == null) // Can be the case if this importer worker was asynchronously suspended return; try { try { ICollection<IMetadataExtractor> metadataExtractors = new List<IMetadataExtractor>(); foreach (Guid metadataExtractorId in importJob.MetadataExtractorIds) { IMetadataExtractor extractor; if (!mediaAccessor.LocalMetadataExtractors.TryGetValue(metadataExtractorId, out extractor)) continue; metadataExtractors.Add(extractor); } // Prepare import if (state == ImportJobState.Scheduled) { ServiceRegistration.Get<ILogger>().Info("ImporterWorker: Starting import job '{0}'", importJob); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportStarted, importJob.BasePath); IResourceAccessor ra; if (!importJob.BasePath.TryCreateLocalResourceAccessor(out ra)) throw new ArgumentException(string.Format("Unable to access resource path '{0}'", importJob.BasePath)); using (ra) { IFileSystemResourceAccessor fsra = ra as IFileSystemResourceAccessor; if (fsra != null) { // Prepare complex import process importJob.PendingResources.Add(new PendingImportResource(Guid.Empty, (IFileSystemResourceAccessor) fsra.Clone())); importJob.State = ImportJobState.Active; } else { // Simple resource import ImportSingleFile(importJob, ra, metadataExtractors, mediaBrowsing, resultHandler, mediaAccessor); lock (importJob.SyncObj) if (importJob.State == ImportJobState.Active) importJob.State = ImportJobState.Finished; return; } } } else ServiceRegistration.Get<ILogger>().Info("ImporterWorker: Resuming import job '{0}' ({1} items pending)", importJob, importJob.PendingResources.Count); // Actual import process while (importJob.HasPendingResources) { Thread.Sleep(0); CheckImportStillRunning(importJob.State); PendingImportResource pendingImportResource; lock (importJob.SyncObj) pendingImportResource = importJob.PendingResources.FirstOrDefault(); if (pendingImportResource.IsValid) { IFileSystemResourceAccessor fsra = pendingImportResource.ResourceAccessor; int numPending = importJob.PendingResources.Count; string moreResources = numPending > 1 ? string.Format(" ({0} more resources pending)", numPending) : string.Empty; ServiceRegistration.Get<ILogger>().Info("ImporterWorker: Importing '{0}'{1}", fsra.ResourcePathName, moreResources); if (fsra.IsFile && fsra.Exists) ImportResource(fsra, pendingImportResource.ParentDirectory, metadataExtractors, resultHandler, mediaAccessor); else if (!fsra.IsFile) { CheckImportStillRunning(importJob.State); Guid? currentDirectoryId = ImportDirectory(importJob, pendingImportResource.ParentDirectory, fsra, metadataExtractors, mediaBrowsing, resultHandler, mediaAccessor); CheckImportStillRunning(importJob.State); if (currentDirectoryId.HasValue && importJob.IncludeSubDirectories) // Add subdirectories in front of work queue lock (importJob.SyncObj) { ICollection<IFileSystemResourceAccessor> directories = FileSystemResourceNavigator.GetChildDirectories(fsra, false); if (directories != null) foreach (IFileSystemResourceAccessor childDirectory in directories) importJob.PendingResources.Insert(0, new PendingImportResource(currentDirectoryId.Value, childDirectory)); } } else ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Cannot import resource '{0}': It's neither a file nor a directory", fsra.CanonicalLocalResourcePath.Serialize()); } lock (importJob.SyncObj) importJob.PendingResources.Remove(pendingImportResource); pendingImportResource.Dispose(); } lock (importJob.SyncObj) if (importJob.State == ImportJobState.Active) importJob.State = ImportJobState.Finished; ServiceRegistration.Get<ILogger>().Info("ImporterWorker: Finished import job '{0}'", importJob); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportCompleted, importJob.BasePath); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Problem processing '{0}'", e, importJob); ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportCompleted, importJob.BasePath); importJob.State = ImportJobState.Erroneous; } } catch (ImportSuspendedException) { ServiceRegistration.Get<ILogger>().Info("ImporterWorker: Suspending import job '{0}' ({1} items pending - will be continued next time)", importJob, importJob.PendingResources.Count); } catch (ImportAbortException) { ServiceRegistration.Get<ILogger>().Info("ImporterWorker: Aborting import job '{0}' ({1} items pending)", importJob, importJob.PendingResources.Count); } }
/// <summary> /// Imports or refreshes the directory with the specified <paramref name="directoryAccessor"/>. Sub directories will not /// be processed in this method. /// </summary> /// <param name="importJob">The import job being processed.</param> /// <param name="parentDirectoryId">Media item id of the parent directory, if present, else <see cref="Guid.Empty"/>.</param> /// <param name="directoryAccessor">Resource accessor for the directory to import.</param> /// <param name="metadataExtractors">Metadata extractors to apply on the resources.</param> /// <param name="mediaBrowsing">Callback interface to the media library for the refresh import type.</param> /// <param name="resultHandler">Callback to notify the import result.</param> /// <param name="mediaAccessor">Convenience reference to the media accessor.</param> /// <returns>Id of the directory's media item or <c>null</c>, if the given <paramref name="directoryAccessor"/> /// was imported as a media item or if an error occured. If <c>null</c> is returned, the directory import should be /// considered to be finished.</returns> protected Guid? ImportDirectory(ImportJob importJob, Guid parentDirectoryId, IFileSystemResourceAccessor directoryAccessor, ICollection<IMetadataExtractor> metadataExtractors, IMediaBrowsing mediaBrowsing, IImportResultHandler resultHandler, IMediaAccessor mediaAccessor) { ResourcePath currentDirectoryPath = directoryAccessor.CanonicalLocalResourcePath; try { ImporterWorkerMessaging.SendImportMessage(ImporterWorkerMessaging.MessageType.ImportStatus, currentDirectoryPath); if (ImportResource(directoryAccessor, parentDirectoryId, metadataExtractors, resultHandler, mediaAccessor)) // The directory could be imported as a media item. // If the directory itself was identified as a normal media item, don't import its children. // Necessary for DVD directories, for example. return null; Guid directoryId = GetOrAddDirectory(directoryAccessor, parentDirectoryId, mediaBrowsing, resultHandler); IDictionary<string, MediaItem> path2Item = new Dictionary<string, MediaItem>(); if (importJob.JobType == ImportJobType.Refresh) { foreach (MediaItem mediaItem in mediaBrowsing.Browse(directoryId, IMPORTER_PROVIDER_MIA_ID_ENUMERATION, EMPTY_MIA_ID_ENUMERATION)) { MediaItemAspect providerResourceAspect; if (mediaItem.Aspects.TryGetValue(ProviderResourceAspect.ASPECT_ID, out providerResourceAspect)) path2Item[providerResourceAspect.GetAttributeValue<string>(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH)] = mediaItem; } } CheckImportStillRunning(importJob.State); ICollection<IFileSystemResourceAccessor> files = FileSystemResourceNavigator.GetFiles(directoryAccessor, false); if (files != null) foreach (IFileSystemResourceAccessor fileAccessor in files) using (fileAccessor) { // Add & update files ResourcePath currentFilePath = fileAccessor.CanonicalLocalResourcePath; string serializedFilePath = currentFilePath.Serialize(); try { MediaItemAspect importerAspect; MediaItem mediaItem; if (importJob.JobType == ImportJobType.Refresh && path2Item.TryGetValue(serializedFilePath, out mediaItem) && mediaItem.Aspects.TryGetValue(ImporterAspect.ASPECT_ID, out importerAspect) && importerAspect.GetAttributeValue<DateTime>(ImporterAspect.ATTR_LAST_IMPORT_DATE) > fileAccessor.LastChanged) { // We can skip this file; it was imported after the last change time of the item path2Item.Remove(serializedFilePath); continue; } if (ImportResource(fileAccessor, directoryId, metadataExtractors, resultHandler, mediaAccessor)) path2Item.Remove(serializedFilePath); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Problem while importing resource '{0}'", e, serializedFilePath); importJob.State = ImportJobState.Erroneous; } CheckImportStillRunning(importJob.State); } if (importJob.JobType == ImportJobType.Refresh) { // Remove remaining (= non-present) files foreach (string pathStr in path2Item.Keys) { ResourcePath path = ResourcePath.Deserialize(pathStr); try { IResourceAccessor ra; if (!path.TryCreateLocalResourceAccessor(out ra)) throw new IllegalCallException("Unable to access resource path '{0}'", importJob.BasePath); using (ra) { IFileSystemResourceAccessor fsra = ra as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) // Don't touch directories because they will be imported in a different call of ImportDirectory continue; } } catch (IllegalCallException) { // This happens if the resource doesn't exist any more - we also catch missing directories here } // Delete all remaining items resultHandler.DeleteMediaItem(path); CheckImportStillRunning(importJob.State); } } return directoryId; } catch (ImportSuspendedException) { throw; } catch (ImportAbortException) { throw; } catch (UnauthorizedAccessException e) { // If the access to the file or folder was denied, simply continue with the others ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Problem accessing resource '{0}', continueing with one", e, currentDirectoryPath); } catch (Exception e) { CheckSuspended(e); // Throw ImportAbortException if suspended - will skip warning and tagging job as erroneous ServiceRegistration.Get<ILogger>().Warn("ImporterWorker: Problem while importing directory '{0}'", e, currentDirectoryPath); importJob.State = ImportJobState.Erroneous; } return null; }