/// <summary> /// Determine if the supplied file data points to a music album /// </summary> /// <param name="path">The path.</param> /// <param name="directoryService">The directory service.</param> /// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns> public static bool IsMusicAlbum(string path, IDirectoryService directoryService) { // If list contains at least 2 audio files or at least one and no video files consider it to contain music var foundAudio = 0; foreach (var file in directoryService.GetFiles(path)) { var fullName = file.FullName; if (EntityResolutionHelper.IsAudioFile(fullName)) { // Don't resolve these into audio files if (string.Equals(Path.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(fullName)) { continue; } foundAudio++; } if (foundAudio >= 2) { return(true); } if (EntityResolutionHelper.IsVideoFile(fullName)) { return(false); } } // or a single audio file and no video files return(foundAudio > 0); }
/// <summary> /// Determine if the supplied list contains what we should consider music /// </summary> /// <param name="list">The list.</param> /// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns> public static bool ContainsMusic(IEnumerable <WIN32_FIND_DATA> list) { // If list contains at least 2 audio files or at least one and no video files consider it to contain music var foundAudio = 0; var foundVideo = 0; foreach (var file in list) { if (AudioResolver.IsAudioFile(file)) { foundAudio++; } if (foundAudio >= 2) { return(true); } if (EntityResolutionHelper.IsVideoFile(file.Path)) { foundVideo++; } } // or a single audio file and no video files if (foundAudio > 0 && foundVideo == 0) { return(true); } return(false); }
/// <summary> /// Determine if the supplied list contains what we should consider music /// </summary> /// <param name="list">The list.</param> /// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns> public static bool ContainsMusic(IEnumerable <FileSystemInfo> list) { // If list contains at least 2 audio files or at least one and no video files consider it to contain music var foundAudio = 0; foreach (var file in list) { var fullName = file.FullName; if (EntityResolutionHelper.IsAudioFile(fullName)) { foundAudio++; } if (foundAudio >= 2) { return(true); } if (EntityResolutionHelper.IsVideoFile(fullName)) { return(false); } if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) { return(false); } } // or a single audio file and no video files return(foundAudio > 0); }
/// <summary> /// Determines whether [is series folder] [the specified path]. /// </summary> /// <param name="path">The path.</param> /// <param name="considerSeasonlessEntries">if set to <c>true</c> [consider seasonless entries].</param> /// <param name="fileSystemChildren">The file system children.</param> /// <param name="directoryService">The directory service.</param> /// <param name="fileSystem">The file system.</param> /// <returns><c>true</c> if [is series folder] [the specified path]; otherwise, <c>false</c>.</returns> public static bool IsSeriesFolder(string path, bool considerSeasonlessEntries, IEnumerable <FileSystemInfo> fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem, ILogger logger) { // A folder with more than 3 non-season folders in will not becounted as a series var nonSeriesFolders = 0; foreach (var child in fileSystemChildren) { var attributes = child.Attributes; if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) { //logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName); continue; } // Can't enforce this because files saved by Bitcasa are always marked System //if ((attributes & FileAttributes.System) == FileAttributes.System) //{ // logger.Debug("Igoring series subfolder marked system: {0}", child.FullName); // continue; //} if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) { if (IsSeasonFolder(child.FullName, directoryService, fileSystem)) { logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); return(true); } if (IsBadFolder(child.Name)) { logger.Debug("Invalid folder under series: {0}", child.FullName); nonSeriesFolders++; } if (nonSeriesFolders >= 3) { logger.Debug("{0} not a series due to 3 or more invalid folders.", path); return(false); } } else { var fullName = child.FullName; if (EntityResolutionHelper.IsVideoFile(fullName) || EntityResolutionHelper.IsVideoPlaceHolder(fullName)) { if (GetEpisodeNumberFromFile(fullName, considerSeasonlessEntries).HasValue) { return(true); } } } } logger.Debug("{0} is not a series folder.", path); return(false); }
/// <summary> /// Determine if the supplied list contains what we should consider music /// </summary> /// <param name="list">The list.</param> /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param> /// <param name="allowSubfolders">if set to <c>true</c> [allow subfolders].</param> /// <param name="directoryService">The directory service.</param> /// <param name="logger">The logger.</param> /// <param name="fileSystem">The file system.</param> /// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns> private static bool ContainsMusic(IEnumerable <FileSystemInfo> list, bool isMusicMediaFolder, bool allowSubfolders, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem) { // If list contains at least 2 audio files or at least one and no video files consider it to contain music var foundAudio = 0; var discSubfolderCount = 0; foreach (var fileSystemInfo in list) { if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { if (isMusicMediaFolder && allowSubfolders && IsAlbumSubfolder(fileSystemInfo, true, directoryService, logger, fileSystem)) { discSubfolderCount++; } if (!IsAdditionalSubfolderAllowed(fileSystemInfo)) { return(false); } } var fullName = fileSystemInfo.FullName; if (EntityResolutionHelper.IsAudioFile(fullName)) { // Don't resolve these into audio files if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename)) { continue; } foundAudio++; } else if (EntityResolutionHelper.IsVideoFile(fullName)) { return(false); } else if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) { return(false); } if (foundAudio >= 2) { return(true); } } // or a single audio file and no video files return(foundAudio > 0 || discSubfolderCount > 0); }
/// <summary> /// Downloads the trailer for item. /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> public async Task DownloadTrailerForItem(BaseItem item, CancellationToken cancellationToken) { var url = await GetTrailerUrl(item, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(url)) { return; } var responseInfo = await _httpClient.GetTempFileResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, Progress = new Progress <double>(), UserAgent = GetUserAgent(url) }); var extension = responseInfo.ContentType.Split('/').Last(); if (string.Equals("quicktime", extension, StringComparison.OrdinalIgnoreCase)) { extension = "mov"; } var savePath = Directory.Exists(item.Path) ? Path.Combine(item.Path, Path.GetFileNameWithoutExtension(item.Path) + "-trailer." + extension) : Path.Combine(Path.GetDirectoryName(item.Path), Path.GetFileNameWithoutExtension(item.Path) + "-trailer." + extension); if (!EntityResolutionHelper.IsVideoFile(savePath)) { _logger.Warn("Unrecognized video extension {0}", savePath); return; } _directoryWatchers.TemporarilyIgnore(savePath); _logger.Info("Moving {0} to {1}", responseInfo.TempFilePath, savePath); try { var parentPath = Path.GetDirectoryName(savePath); if (!Directory.Exists(parentPath)) { Directory.CreateDirectory(parentPath); } File.Move(responseInfo.TempFilePath, savePath); } finally { _directoryWatchers.RemoveTempIgnore(savePath); } await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); }
/// <summary> /// Determines whether [is series folder] [the specified path]. /// </summary> /// <param name="path">The path.</param> /// <param name="fileSystemChildren">The file system children.</param> /// <returns><c>true</c> if [is series folder] [the specified path]; otherwise, <c>false</c>.</returns> public static bool IsSeriesFolder(string path, IEnumerable <FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) { // A folder with more than 3 non-season folders in will not becounted as a series var nonSeriesFolders = 0; foreach (var child in fileSystemChildren) { var attributes = child.Attributes; if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) { continue; } if ((attributes & FileAttributes.System) == FileAttributes.System) { continue; } if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) { if (IsSeasonFolder(child.FullName, directoryService)) { return(true); } nonSeriesFolders++; if (nonSeriesFolders >= 3) { return(false); } } else { var fullName = child.FullName; if (EntityResolutionHelper.IsVideoFile(fullName) || EntityResolutionHelper.IsVideoPlaceHolder(fullName)) { if (GetEpisodeNumberFromFile(fullName, false).HasValue) { return(true); } } } } return(false); }
private List <string> GetOtherDuplicatePaths(string targetPath, Series series, int seasonNumber, int episodeNumber, int?endingEpisodeNumber) { var episodePaths = series.RecursiveChildren .OfType <Episode>() .Where(i => { var locationType = i.LocationType; // Must be file system based and match exactly if (locationType != LocationType.Remote && locationType != LocationType.Virtual && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber && i.IndexNumber.HasValue && i.IndexNumber.Value == episodeNumber) { if (endingEpisodeNumber.HasValue || i.IndexNumberEnd.HasValue) { return(endingEpisodeNumber.HasValue && i.IndexNumberEnd.HasValue && endingEpisodeNumber.Value == i.IndexNumberEnd.Value); } return(true); } return(false); }) .Select(i => i.Path) .ToList(); var folder = Path.GetDirectoryName(targetPath); var targetFileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath); try { var filesOfOtherExtensions = Directory.EnumerateFiles(folder, "*", SearchOption.TopDirectoryOnly) .Where(i => EntityResolutionHelper.IsVideoFile(i) && string.Equals(Path.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)); episodePaths.AddRange(filesOfOtherExtensions); } catch (DirectoryNotFoundException) { // No big deal. Maybe the season folder doesn't already exist. } return(episodePaths.Where(i => !string.Equals(i, targetPath, StringComparison.OrdinalIgnoreCase)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList()); }
public IEnumerable <MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item) { var filenamePrefix = item.Id.ToString("N"); var parentPath = Path.Combine(ChannelDownloadPath, item.ChannelId); try { var files = new DirectoryInfo(parentPath).EnumerateFiles("*", SearchOption.TopDirectoryOnly); if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { files = files.Where(i => EntityResolutionHelper.IsVideoFile(i.FullName)); } else { files = files.Where(i => EntityResolutionHelper.IsAudioFile(i.FullName)); } var file = files .FirstOrDefault(i => i.Name.StartsWith(filenamePrefix, StringComparison.OrdinalIgnoreCase)); if (file != null) { var cachedItem = _libraryManager.ResolvePath(file); if (cachedItem != null) { var hasMediaSources = _libraryManager.GetItemById(cachedItem.Id) as IHasMediaSources; if (hasMediaSources != null) { var source = hasMediaSources.GetMediaSources(true).FirstOrDefault(); if (source != null) { source.Type = MediaSourceType.Cache; return(new[] { source }); } } } } } catch (DirectoryNotFoundException) { } return(new List <MediaSourceInfo>()); }
/// <summary> /// Loads the additional parts. /// </summary> /// <returns>IEnumerable{Video}.</returns> private IEnumerable <Video> LoadAlternateVersionsWithinSameDirectory(IEnumerable <FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) { IEnumerable <FileSystemInfo> files; // Only support this for video files. For folder rips, they'll have to use the linking feature if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso) { var path = Path; var filenamePrefix = System.IO.Path.GetFileName(System.IO.Path.GetDirectoryName(path)); files = fileSystemChildren.Where(i => { if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { return(false); } return(!string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && i.Name.StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase)); }); } else { files = new List <FileSystemInfo>(); } return(LibraryManager.ResolvePaths <Video>(files, directoryService, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version var dbItem = LibraryManager.GetItemById(video.Id) as Video; if (dbItem != null) { video = dbItem; } video.PrimaryVersionId = Id; return video; // Sort them so that the list can be easily compared for changes }).OrderBy(i => i.Path).ToList()); }
/// <summary> /// Determines whether [is series folder] [the specified path]. /// </summary> /// <param name="path">The path.</param> /// <param name="fileSystemChildren">The file system children.</param> /// <returns><c>true</c> if [is series folder] [the specified path]; otherwise, <c>false</c>.</returns> public static bool IsSeriesFolder(string path, IEnumerable <WIN32_FIND_DATA> fileSystemChildren) { // A folder with more than 3 non-season folders in will not becounted as a series var nonSeriesFolders = 0; foreach (var child in fileSystemChildren) { if (child.IsHidden || child.IsSystemFile) { continue; } if (child.IsDirectory) { if (IsSeasonFolder(child.Path)) { return(true); } nonSeriesFolders++; if (nonSeriesFolders >= 3) { return(false); } } else { if (EntityResolutionHelper.IsVideoFile(child.Path) && !string.IsNullOrEmpty(EpisodeNumberFromFile(child.Path, false))) { return(true); } } } return(false); }
/// <summary> /// Determine if the supplied file data points to a music album /// </summary> /// <param name="path">The path.</param> /// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns> public static bool IsMusicAlbum(string path) { // If list contains at least 2 audio files or at least one and no video files consider it to contain music var foundAudio = 0; foreach (var fullName in Directory.EnumerateFiles(path)) { if (EntityResolutionHelper.IsAudioFile(fullName)) { foundAudio++; } if (foundAudio >= 2) { return(true); } if (EntityResolutionHelper.IsVideoFile(fullName)) { return(false); } } // or a single audio file and no video files return(foundAudio > 0); }
/// <summary> /// Loads the additional parts. /// </summary> /// <returns>IEnumerable{Video}.</returns> private IEnumerable <Video> LoadAdditionalParts(IEnumerable <FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) { IEnumerable <FileSystemInfo> files; var path = Path; if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd) { files = fileSystemChildren.Where(i => { if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { return(!string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFolder(i.FullName)); } return(false); }); } else { files = fileSystemChildren.Where(i => { if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { return(false); } return(!string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name)); }); } return(LibraryManager.ResolvePaths <Video>(files, directoryService, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version var dbItem = LibraryManager.GetItemById(video.Id) as Video; if (dbItem != null) { video = dbItem; } return video; // Sort them so that the list can be easily compared for changes }).OrderBy(i => i.Path).ToList()); }
/// <summary> /// Loads the additional parts. /// </summary> /// <returns>IEnumerable{Video}.</returns> private IEnumerable <Video> LoadAdditionalParts() { IEnumerable <FileSystemInfo> files; if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd) { files = new DirectoryInfo(System.IO.Path.GetDirectoryName(Path)) .EnumerateDirectories() .Where(i => !string.Equals(i.FullName, Path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFile(i.Name)); } else { files = ResolveArgs.FileSystemChildren.Where(i => { if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { return(false); } return(!string.Equals(i.FullName, Path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name)); }); } return(LibraryManager.ResolvePaths <Video>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version var dbItem = LibraryManager.RetrieveItem(video.Id) as Video; if (dbItem != null) { dbItem.ResolveArgs = video.ResolveArgs; video = dbItem; } return video; }).ToList()); }
public async Task Organize(TvFileOrganizationOptions options, CancellationToken cancellationToken, IProgress <double> progress) { var minFileBytes = options.MinFileSizeMb * 1024 * 1024; var watchLocations = options.WatchLocations.ToList(); var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize) .OrderBy(_fileSystem.GetCreationTimeUtc) .Where(i => EntityResolutionHelper.IsVideoFile(i.FullName) && i.Length >= minFileBytes) .ToList(); progress.Report(10); var scanLibrary = false; if (eligibleFiles.Count > 0) { var numComplete = 0; foreach (var file in eligibleFiles) { var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false); if (result.Status == FileSortingStatus.Success) { scanLibrary = true; } numComplete++; double percent = numComplete; percent /= eligibleFiles.Count; progress.Report(10 + (89 * percent)); } } cancellationToken.ThrowIfCancellationRequested(); progress.Report(99); foreach (var path in watchLocations) { var deleteExtensions = options.LeftOverFileExtensionsToDelete .Select(i => i.Trim().TrimStart('.')) .Where(i => !string.IsNullOrEmpty(i)) .Select(i => "." + i) .ToList(); if (deleteExtensions.Count > 0) { DeleteLeftOverFiles(path, deleteExtensions); } if (options.DeleteEmptyFolders) { foreach (var subfolder in GetDirectories(path).ToList()) { DeleteEmptyFolders(subfolder); } } } if (scanLibrary) { await _libraryManager.ValidateMediaLibrary(new Progress <double>(), CancellationToken.None) .ConfigureAwait(false); } progress.Report(100); }