MovieResolver.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using MediaBrowser.Common.Extensions;
  2. using MediaBrowser.Controller;
  3. using MediaBrowser.Controller.Entities;
  4. using MediaBrowser.Controller.Entities.Movies;
  5. using MediaBrowser.Controller.Library;
  6. using MediaBrowser.Controller.Resolvers;
  7. using MediaBrowser.Model.Entities;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
  13. {
  14. /// <summary>
  15. /// Class MovieResolver
  16. /// </summary>
  17. public class MovieResolver : BaseVideoResolver<Video>
  18. {
  19. private readonly IServerApplicationPaths _applicationPaths;
  20. private readonly ILibraryManager _libraryManager;
  21. public MovieResolver(IServerApplicationPaths appPaths, ILibraryManager libraryManager)
  22. {
  23. _applicationPaths = appPaths;
  24. _libraryManager = libraryManager;
  25. }
  26. /// <summary>
  27. /// Gets the priority.
  28. /// </summary>
  29. /// <value>The priority.</value>
  30. public override ResolverPriority Priority
  31. {
  32. get
  33. {
  34. // Give plugins a chance to catch iso's first
  35. // Also since we have to loop through child files looking for videos,
  36. // see if we can avoid some of that by letting other resolvers claim folders first
  37. return ResolverPriority.Second;
  38. }
  39. }
  40. /// <summary>
  41. /// Resolves the specified args.
  42. /// </summary>
  43. /// <param name="args">The args.</param>
  44. /// <returns>Video.</returns>
  45. protected override Video Resolve(ItemResolveArgs args)
  46. {
  47. // Avoid expensive tests against VF's and all their children by not allowing this
  48. if (args.Parent != null)
  49. {
  50. if (args.Parent.IsRoot)
  51. {
  52. return null;
  53. }
  54. // If the parent is not a boxset, the only other allowed parent type is Folder
  55. if (!(args.Parent is BoxSet))
  56. {
  57. if (args.Parent.GetType() != typeof(Folder))
  58. {
  59. return null;
  60. }
  61. }
  62. }
  63. // Since the looping is expensive, this is an optimization to help us avoid it
  64. if (args.Path.IndexOf("[tvdbid", StringComparison.OrdinalIgnoreCase) != -1)
  65. {
  66. return null;
  67. }
  68. var isDirectory = args.IsDirectory;
  69. if (isDirectory)
  70. {
  71. // Since the looping is expensive, this is an optimization to help us avoid it
  72. if (args.ContainsMetaFileByName("series.xml"))
  73. {
  74. return null;
  75. }
  76. }
  77. var collectionType = args.GetCollectionType();
  78. // Find movies with their own folders
  79. if (isDirectory)
  80. {
  81. if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1 ||
  82. string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
  83. {
  84. return FindMovie<Trailer>(args.Path, args.FileSystemChildren);
  85. }
  86. if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
  87. string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
  88. {
  89. return FindMovie<MusicVideo>(args.Path, args.FileSystemChildren);
  90. }
  91. if (args.Path.IndexOf("[adultvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
  92. string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
  93. {
  94. return FindMovie<AdultVideo>(args.Path, args.FileSystemChildren);
  95. }
  96. if (string.IsNullOrEmpty(collectionType) ||
  97. string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
  98. string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
  99. {
  100. return FindMovie<Movie>(args.Path, args.FileSystemChildren);
  101. }
  102. return null;
  103. }
  104. var filename = Path.GetFileName(args.Path);
  105. // Don't misidentify xbmc trailers as a movie
  106. if (filename.IndexOf(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) != -1)
  107. {
  108. return null;
  109. }
  110. // Find movies that are mixed in the same folder
  111. if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1 ||
  112. string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
  113. {
  114. return ResolveVideo<Trailer>(args);
  115. }
  116. Video item = null;
  117. if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
  118. string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
  119. {
  120. item = ResolveVideo<MusicVideo>(args);
  121. }
  122. if (args.Path.IndexOf("[adultvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
  123. string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
  124. {
  125. item = ResolveVideo<AdultVideo>(args);
  126. }
  127. // To find a movie file, the collection type must be movies or boxsets
  128. // Otherwise we'll consider it a plain video and let the video resolver handle it
  129. if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
  130. string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
  131. {
  132. item = ResolveVideo<Movie>(args);
  133. }
  134. if (item != null)
  135. {
  136. item.IsInMixedFolder = true;
  137. }
  138. return item;
  139. }
  140. /// <summary>
  141. /// Sets the initial item values.
  142. /// </summary>
  143. /// <param name="item">The item.</param>
  144. /// <param name="args">The args.</param>
  145. protected override void SetInitialItemValues(Video item, ItemResolveArgs args)
  146. {
  147. base.SetInitialItemValues(item, args);
  148. SetProviderIdFromPath(item);
  149. }
  150. /// <summary>
  151. /// Sets the provider id from path.
  152. /// </summary>
  153. /// <param name="item">The item.</param>
  154. private void SetProviderIdFromPath(Video item)
  155. {
  156. //we need to only look at the name of this actual item (not parents)
  157. var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(Path.GetDirectoryName(item.Path));
  158. var id = justName.GetAttributeValue("tmdbid");
  159. if (!string.IsNullOrEmpty(id))
  160. {
  161. item.SetProviderId(MetadataProviders.Tmdb, id);
  162. }
  163. }
  164. /// <summary>
  165. /// Finds a movie based on a child file system entries
  166. /// </summary>
  167. /// <typeparam name="T"></typeparam>
  168. /// <param name="path">The path.</param>
  169. /// <param name="fileSystemEntries">The file system entries.</param>
  170. /// <returns>Movie.</returns>
  171. private T FindMovie<T>(string path, IEnumerable<FileSystemInfo> fileSystemEntries)
  172. where T : Video, new()
  173. {
  174. var movies = new List<T>();
  175. var multiDiscFolders = new List<FileSystemInfo>();
  176. // Loop through each child file/folder and see if we find a video
  177. foreach (var child in fileSystemEntries)
  178. {
  179. var filename = child.Name;
  180. if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
  181. {
  182. if (IsDvdDirectory(filename))
  183. {
  184. return new T
  185. {
  186. Path = path,
  187. VideoType = VideoType.Dvd
  188. };
  189. }
  190. if (IsBluRayDirectory(filename))
  191. {
  192. return new T
  193. {
  194. Path = path,
  195. VideoType = VideoType.BluRay
  196. };
  197. }
  198. if (EntityResolutionHelper.IsMultiPartFile(filename))
  199. {
  200. multiDiscFolders.Add(child);
  201. }
  202. continue;
  203. }
  204. // Don't misidentify xbmc trailers as a movie
  205. if (filename.IndexOf(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) != -1)
  206. {
  207. continue;
  208. }
  209. var childArgs = new ItemResolveArgs(_applicationPaths, _libraryManager)
  210. {
  211. FileInfo = child,
  212. Path = child.FullName
  213. };
  214. var item = ResolveVideo<T>(childArgs);
  215. if (item != null)
  216. {
  217. item.IsInMixedFolder = false;
  218. movies.Add(item);
  219. }
  220. }
  221. if (movies.Count > 1)
  222. {
  223. return GetMultiFileMovie(movies);
  224. }
  225. if (movies.Count == 1)
  226. {
  227. return movies[0];
  228. }
  229. if (multiDiscFolders.Count > 0)
  230. {
  231. return GetMultiDiscMovie<T>(multiDiscFolders);
  232. }
  233. return null;
  234. }
  235. /// <summary>
  236. /// Gets the multi disc movie.
  237. /// </summary>
  238. /// <typeparam name="T"></typeparam>
  239. /// <param name="folders">The folders.</param>
  240. /// <returns>``0.</returns>
  241. private T GetMultiDiscMovie<T>(List<FileSystemInfo> folders)
  242. where T : Video, new()
  243. {
  244. var videoType = VideoType.BluRay;
  245. var folderPaths = folders.Select(i => i.FullName).Where(i =>
  246. {
  247. var subfolders = Directory.GetDirectories(i).Select(Path.GetFileName).ToList();
  248. if (subfolders.Any(IsDvdDirectory))
  249. {
  250. videoType = VideoType.Dvd;
  251. return true;
  252. }
  253. if (subfolders.Any(IsBluRayDirectory))
  254. {
  255. videoType = VideoType.BluRay;
  256. return true;
  257. }
  258. return false;
  259. }).OrderBy(i => i).ToList();
  260. if (folderPaths.Count == 0)
  261. {
  262. return null;
  263. }
  264. return new T
  265. {
  266. Path = folderPaths[0],
  267. IsMultiPart = true,
  268. VideoType = videoType
  269. };
  270. }
  271. /// <summary>
  272. /// Gets the multi file movie.
  273. /// </summary>
  274. /// <typeparam name="T"></typeparam>
  275. /// <param name="movies">The movies.</param>
  276. /// <returns>``0.</returns>
  277. private T GetMultiFileMovie<T>(List<T> movies)
  278. where T : Video, new()
  279. {
  280. var multiPartMovies = movies.OrderBy(i => i.Path)
  281. .Where(i => EntityResolutionHelper.IsMultiPartFile(i.Path))
  282. .ToList();
  283. // They must all be part of the sequence
  284. if (multiPartMovies.Count != movies.Count)
  285. {
  286. return null;
  287. }
  288. var firstPart = multiPartMovies[0];
  289. firstPart.IsMultiPart = true;
  290. return firstPart;
  291. }
  292. /// <summary>
  293. /// Determines whether [is DVD directory] [the specified directory name].
  294. /// </summary>
  295. /// <param name="directoryName">Name of the directory.</param>
  296. /// <returns><c>true</c> if [is DVD directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
  297. private bool IsDvdDirectory(string directoryName)
  298. {
  299. return string.Equals(directoryName, "video_ts", StringComparison.OrdinalIgnoreCase);
  300. }
  301. /// <summary>
  302. /// Determines whether [is blu ray directory] [the specified directory name].
  303. /// </summary>
  304. /// <param name="directoryName">Name of the directory.</param>
  305. /// <returns><c>true</c> if [is blu ray directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
  306. private bool IsBluRayDirectory(string directoryName)
  307. {
  308. return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase);
  309. }
  310. }
  311. }