mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-10-31 10:37:13 -04:00 
			
		
		
		
	Making edit routes kinda work
This commit is contained in:
		
							parent
							
								
									5229e319b1
								
							
						
					
					
						commit
						41cabdd1b5
					
				| @ -43,7 +43,7 @@ namespace Kyoo.Controllers | ||||
| 		Task<Season> GetSeason(string showSlug, int seasonNumber); | ||||
| 		Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber); | ||||
| 		Task<Episode> GetMovieEpisode(string movieSlug); | ||||
| 		Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknow); | ||||
| 		Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknown); | ||||
| 		Task<Genre> GetGenre(string slug); | ||||
| 		Task<Studio> GetStudio(string slug); | ||||
| 		Task<People> GetPeople(string slug); | ||||
|  | ||||
| @ -95,20 +95,7 @@ namespace Kyoo.Controllers | ||||
| 		 | ||||
| 		 | ||||
| 		Task<T> Create([NotNull] T obj); | ||||
| 		Task<T> CreateIfNotExists([NotNull] T obj); | ||||
| 		async Task<T> CreateIfNotExists([NotNull] T obj, bool silentFail) | ||||
| 		{ | ||||
| 			try | ||||
| 			{ | ||||
| 				return await CreateIfNotExists(obj); | ||||
| 			} | ||||
| 			catch | ||||
| 			{ | ||||
| 				if (!silentFail) | ||||
| 					throw; | ||||
| 				return null; | ||||
| 			} | ||||
| 		} | ||||
| 		Task<T> CreateIfNotExists([NotNull] T obj, bool silentFail = false); | ||||
| 		Task<T> Edit([NotNull] T edited, bool resetOld); | ||||
| 		 | ||||
| 		Task Delete(int id); | ||||
| @ -147,7 +134,7 @@ namespace Kyoo.Controllers | ||||
| 
 | ||||
| 	public interface ITrackRepository : IRepository<Track> | ||||
| 	{ | ||||
| 		Task<Track> Get(string slug, StreamType type = StreamType.Unknow); | ||||
| 		Task<Track> Get(string slug, StreamType type = StreamType.Unknown); | ||||
| 	} | ||||
| 	 | ||||
| 	public interface ILibraryRepository : IRepository<Library> { } | ||||
|  | ||||
| @ -110,7 +110,7 @@ namespace Kyoo.Controllers | ||||
| 			return EpisodeRepository.Get(showID, seasonNumber, episodeNumber); | ||||
| 		} | ||||
| 
 | ||||
| 		public Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknow) | ||||
| 		public Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknown) | ||||
| 		{ | ||||
| 			return TrackRepository.Get(slug, type); | ||||
| 		} | ||||
|  | ||||
							
								
								
									
										6
									
								
								Kyoo.Common/Models/Attributes/EditableRelation.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Kyoo.Common/Models/Attributes/EditableRelation.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Kyoo.Models.Attributes | ||||
| { | ||||
| 	public class EditableRelation : Attribute { } | ||||
| } | ||||
| @ -23,7 +23,7 @@ namespace Kyoo.Models | ||||
| 		public int Runtime { get; set; } //This runtime variable should be in minutes | ||||
| 
 | ||||
| 		[JsonIgnore] public string Poster { get; set; } | ||||
| 		public virtual IEnumerable<MetadataID> ExternalIDs { get; set; } | ||||
| 		[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; } | ||||
| 
 | ||||
| 		[JsonIgnore] public virtual IEnumerable<Track> Tracks { get; set; } | ||||
| 
 | ||||
|  | ||||
| @ -10,7 +10,7 @@ namespace Kyoo.Models | ||||
| 		public string Name { get; set; } | ||||
| 		public IEnumerable<string> Paths { get; set; } | ||||
| 
 | ||||
| 		public virtual IEnumerable<ProviderID> Providers { get; set; } | ||||
| 		[EditableRelation] public virtual IEnumerable<ProviderID> Providers { get; set; } | ||||
| 
 | ||||
| 		[JsonIgnore] public virtual IEnumerable<Show> Shows { get; set; } | ||||
| 		[JsonIgnore] public virtual IEnumerable<Collection> Collections { get; set; } | ||||
|  | ||||
| @ -9,9 +9,9 @@ namespace Kyoo.Models | ||||
| 		public string Slug { get; set; } | ||||
| 		public string Name { get; set; } | ||||
| 		public string Poster { get; set; } | ||||
| 		public virtual IEnumerable<MetadataID> ExternalIDs { get; set; } | ||||
| 		[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; } | ||||
| 		 | ||||
| 		[JsonReadOnly] public virtual IEnumerable<PeopleRole> Roles { get; set; } | ||||
| 		[EditableRelation] [JsonReadOnly] public virtual IEnumerable<PeopleRole> Roles { get; set; } | ||||
| 		 | ||||
| 		public People() {} | ||||
| 
 | ||||
|  | ||||
| @ -16,7 +16,7 @@ namespace Kyoo.Models | ||||
| 		public int? Year { get; set; } | ||||
| 
 | ||||
| 		[JsonIgnore] public string Poster { get; set; } | ||||
| 		public virtual IEnumerable<MetadataID> ExternalIDs { get; set; } | ||||
| 		[EditableRelation] public virtual IEnumerable<MetadataID> ExternalIDs { get; set; } | ||||
| 
 | ||||
| 		[JsonIgnore] public virtual Show Show { get; set; } | ||||
| 		[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; } | ||||
|  | ||||
| @ -28,9 +28,9 @@ namespace Kyoo.Models | ||||
| 		 | ||||
| 		 | ||||
| 		[JsonIgnore] public int? StudioID { get; set; } | ||||
| 		[JsonReadOnly] public virtual Studio Studio { get; set; } | ||||
| 		[JsonReadOnly] public virtual IEnumerable<Genre> Genres { get; set; } | ||||
| 		[JsonReadOnly] public virtual IEnumerable<PeopleRole> People { get; set; } | ||||
| 		[EditableRelation] [JsonReadOnly] public virtual Studio Studio { get; set; } | ||||
| 		[EditableRelation] [JsonReadOnly] public virtual IEnumerable<Genre> Genres { get; set; } | ||||
| 		[EditableRelation] [JsonReadOnly] public virtual IEnumerable<PeopleRole> People { get; set; } | ||||
| 		[JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; } | ||||
| 		[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; } | ||||
| 		[JsonIgnore] public virtual IEnumerable<Library> Libraries { get; set; } | ||||
|  | ||||
| @ -8,7 +8,7 @@ namespace Kyoo.Models | ||||
| { | ||||
| 	public enum StreamType | ||||
| 	{ | ||||
| 		Unknow = 0, | ||||
| 		Unknown = 0, | ||||
| 		Video = 1, | ||||
| 		Audio = 2, | ||||
| 		Subtitle = 3, | ||||
|  | ||||
| @ -102,9 +102,8 @@ namespace Kyoo | ||||
| 			 | ||||
| 			Type type = typeof(T); | ||||
| 			IEnumerable<PropertyInfo> properties = type.GetProperties() | ||||
| 				.Where(x => x.CanRead  | ||||
| 				            && x.CanWrite  | ||||
| 				            && Attribute.GetCustomAttribute(x, typeof(NotMergableAttribute)) == null); | ||||
| 				.Where(x => x.CanRead && x.CanWrite  | ||||
| 				                      && Attribute.GetCustomAttribute(x, typeof(NotMergableAttribute)) == null); | ||||
| 			 | ||||
| 			foreach (PropertyInfo property in properties) | ||||
| 			{ | ||||
| @ -114,11 +113,7 @@ namespace Kyoo | ||||
| 					: null; | ||||
| 
 | ||||
| 				if (value?.Equals(defaultValue) == false) | ||||
| 				{ | ||||
| 					if (value is IEnumerable enumerable && !(value is string)) | ||||
| 						value = RunGenericMethod(typeof(Enumerable), "ToList", GetEnumerableType(enumerable), value); | ||||
| 					property.SetValue(first, value); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (first is IOnMerge merge) | ||||
|  | ||||
| @ -13,7 +13,19 @@ using Newtonsoft.Json.Serialization; | ||||
| 
 | ||||
| namespace Kyoo.Controllers | ||||
| { | ||||
| 	public class JsonPropertySelector : CamelCasePropertyNamesContractResolver | ||||
| 	public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver | ||||
| 	{ | ||||
| 		protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | ||||
| 		{ | ||||
| 			JsonProperty property = base.CreateProperty(member, memberSerialization); | ||||
| 			 | ||||
| 			property.ShouldSerialize = i => member.GetCustomAttribute<JsonReadOnly>(true) == null; | ||||
| 			property.ShouldDeserialize = i => member.GetCustomAttribute<JsonIgnore>(true) == null; | ||||
| 			return property; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public class JsonPropertySelector : JsonPropertyIgnorer | ||||
| 	{ | ||||
| 		private readonly Dictionary<Type, HashSet<string>> _ignored; | ||||
| 		private readonly Dictionary<Type, HashSet<string>> _forceSerialize; | ||||
| @ -60,20 +72,9 @@ namespace Kyoo.Controllers | ||||
| 			JsonProperty property = base.CreateProperty(member, memberSerialization); | ||||
| 
 | ||||
| 			if (IsSerializationForced(property.DeclaringType, property.PropertyName)) | ||||
| 			{ | ||||
| 				property.ShouldSerialize = i => true; | ||||
| 				property.Ignored = false; | ||||
| 			} | ||||
| 			else if (IsIgnored(property.DeclaringType, property.PropertyName)) | ||||
| 			{ | ||||
| 				property.ShouldSerialize = i => false; | ||||
| 				property.Ignored = true; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				property.ShouldSerialize = i => member.GetCustomAttribute<JsonReadOnly>(true) == null; | ||||
| 				property.ShouldDeserialize = i => member.GetCustomAttribute<JsonIgnore>(true) == null; | ||||
| 			} | ||||
| 			return property; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -8,6 +8,7 @@ using System.Threading.Tasks; | ||||
| using JetBrains.Annotations; | ||||
| using Kyoo.CommonApi; | ||||
| using Kyoo.Models; | ||||
| using Kyoo.Models.Attributes; | ||||
| using Kyoo.Models.Exceptions; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.EntityFrameworkCore.ChangeTracking; | ||||
| @ -118,25 +119,32 @@ namespace Kyoo.Controllers | ||||
| 			return obj; | ||||
| 		} | ||||
| 
 | ||||
| 		public virtual async Task<T> CreateIfNotExists(T obj) | ||||
| 		public virtual async Task<T> CreateIfNotExists(T obj, bool silentFail = false) | ||||
| 		{ | ||||
| 			if (obj == null) | ||||
| 				throw new ArgumentNullException(nameof(obj)); | ||||
| 
 | ||||
| 			T old = await Get(obj.Slug); | ||||
| 			if (old != null) | ||||
| 				return old; | ||||
| 			try | ||||
| 			{ | ||||
| 				if (obj == null) | ||||
| 					throw new ArgumentNullException(nameof(obj)); | ||||
| 
 | ||||
| 				T old = await Get(obj.Slug); | ||||
| 				if (old != null) | ||||
| 					return old; | ||||
| 				 | ||||
| 				return await Create(obj); | ||||
| 			} | ||||
| 			catch (DuplicatedItemException) | ||||
| 			{ | ||||
| 				old = await Get(obj.Slug); | ||||
| 				T old = await Get(obj!.Slug); | ||||
| 				if (old == null) | ||||
| 					throw new SystemException("Unknown database state."); | ||||
| 				return old; | ||||
| 			} | ||||
| 			catch | ||||
| 			{ | ||||
| 				if (silentFail) | ||||
| 					return default; | ||||
| 				throw; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		public virtual async Task<T> Edit(T edited, bool resetOld) | ||||
| @ -152,14 +160,17 @@ namespace Kyoo.Controllers | ||||
| 				if (old == null) | ||||
| 					throw new ItemNotFound($"No resource found with the ID {edited.ID}."); | ||||
| 
 | ||||
| 				IEnumerable<NavigationEntry> relations = Database.Entry(old).Collections | ||||
| 					.Concat(Database.Entry(old).Navigations); | ||||
| 				foreach (NavigationEntry navigation in relations) | ||||
| 					if (navigation.Metadata.PropertyInfo.GetCustomAttribute<EditableRelation>() != null) | ||||
| 						await navigation.LoadAsync(); | ||||
| 				 | ||||
| 				if (resetOld) | ||||
| 					Utility.Nullify(old); | ||||
| 				Utility.Complete(old, edited); | ||||
| 				// TODO Validation set values & setting values trigger a change round in the OEM. A change round should only be triggered if the item is actually different. | ||||
| 				await Validate(old); | ||||
| 				// TODO should fix this, new links & deleted links should be kept. | ||||
| 				// TODO The changetracker has trash values now & values can't be listed before the validation (exception is thrown) | ||||
| 				foreach (EntityEntry x in Database.ChangeTracker.Entries().Where(x => x.Entity != old)) | ||||
| 					x.State = EntityState.Detached; | ||||
| 				await Database.SaveChangesAsync(); | ||||
| 				return old; | ||||
| 			} | ||||
| @ -168,7 +179,7 @@ namespace Kyoo.Controllers | ||||
| 				Database.ChangeTracker.LazyLoadingEnabled = true; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 
 | ||||
| 		protected virtual Task Validate(T resource) | ||||
| 		{ | ||||
| 			if (string.IsNullOrEmpty(resource.Slug)) | ||||
| @ -298,13 +309,13 @@ namespace Kyoo.Controllers | ||||
| 				.Then(x => item.ID = x.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		Task<T> IRepository<T>.CreateIfNotExists(T item) | ||||
| 		Task<T> IRepository<T>.CreateIfNotExists(T item, bool silentFail) | ||||
| 		{ | ||||
| 			TInternal obj = item as TInternal ?? new TInternal(); | ||||
| 			if (!(item is TInternal)) | ||||
| 				Utility.Assign(obj, item); | ||||
| 			 | ||||
| 			return CreateIfNotExists(obj).Cast<T>() | ||||
| 			return CreateIfNotExists(obj, silentFail).Cast<T>() | ||||
| 				.Then(x => item.ID = x.ID); | ||||
| 		} | ||||
| 
 | ||||
|  | ||||
| @ -100,7 +100,13 @@ namespace Kyoo.Controllers | ||||
| 		} | ||||
| 
 | ||||
| 		public override Task<LibraryItem> Create(LibraryItem obj) => throw new InvalidOperationException(); | ||||
| 		public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj) => throw new InvalidOperationException(); | ||||
| 
 | ||||
| 		public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj, bool silentFail = false) | ||||
| 		{ | ||||
| 			if (silentFail) | ||||
| 				return Task.FromResult<LibraryItem>(default); | ||||
| 			throw new InvalidOperationException(); | ||||
| 		} | ||||
| 		public override Task<LibraryItem> Edit(LibraryItem obj, bool reset) => throw new InvalidOperationException(); | ||||
| 		protected override Task Validate(LibraryItem resource) => throw new InvalidOperationException(); | ||||
| 		public override Task Delete(int id) => throw new InvalidOperationException(); | ||||
|  | ||||
| @ -75,7 +75,8 @@ namespace Kyoo.Controllers | ||||
| 			query = $"%{query}%"; | ||||
| 			return await _database.Shows | ||||
| 				.Where(x => EF.Functions.ILike(x.Title, query)  | ||||
| 				            /*|| x.Aliases.Any(y => EF.Functions.ILike(y, query))*/) // NOT TRANSLATABLE. | ||||
| 				            || EF.Functions.ILike(x.Slug, query)  | ||||
| 							/*|| x.Aliases.Any(y => EF.Functions.ILike(y, query))*/) // NOT TRANSLATABLE. | ||||
| 				.Take(20) | ||||
| 				.ToListAsync<Show>(); | ||||
| 		} | ||||
|  | ||||
| @ -36,7 +36,7 @@ namespace Kyoo.Controllers | ||||
| 			await _database.DisposeAsync(); | ||||
| 		} | ||||
| 
 | ||||
| 		public Task<Track> Get(string slug, StreamType type = StreamType.Unknow) | ||||
| 		public Task<Track> Get(string slug, StreamType type = StreamType.Unknown) | ||||
| 		{ | ||||
| 			Match match = Regex.Match(slug, | ||||
| 				@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?"); | ||||
| @ -57,7 +57,7 @@ namespace Kyoo.Controllers | ||||
| 			string language = match.Groups["language"].Value; | ||||
| 			bool forced = match.Groups["forced"].Success; | ||||
| 
 | ||||
| 			if (type == StreamType.Unknow) | ||||
| 			if (type == StreamType.Unknown) | ||||
| 			{ | ||||
| 				return _database.Tracks.FirstOrDefaultAsync(x => x.Episode.Show.Slug == showSlug | ||||
| 			        	                                         && x.Episode.SeasonNumber == seasonNumber | ||||
|  | ||||
| @ -55,7 +55,7 @@ namespace Kyoo.Controllers | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					await Task.Delay(10, cancellationToken); | ||||
| 					await Task.Delay(1000, cancellationToken); | ||||
| 					QueueScheduledTasks(); | ||||
| 				} | ||||
| 			} | ||||
| @ -63,8 +63,8 @@ namespace Kyoo.Controllers | ||||
| 
 | ||||
| 		private void QueueScheduledTasks() | ||||
| 		{ | ||||
| 			List<string> tasksToQueue = _tasks.Where(x => x.scheduledDate <= DateTime.Now) | ||||
| 				.Select(x => x.task.Slug).ToList(); | ||||
| 			IEnumerable<string> tasksToQueue = _tasks.Where(x => x.scheduledDate <= DateTime.Now) | ||||
| 				.Select(x => x.task.Slug); | ||||
| 			foreach (string task in tasksToQueue) | ||||
| 				StartTask(task); | ||||
| 		} | ||||
|  | ||||
| @ -41,7 +41,7 @@ namespace Kyoo.Controllers.TranscoderLink | ||||
| 				for (int i = 0; i < arrayLength; i++) | ||||
| 				{ | ||||
| 					Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr); | ||||
| 					if (stream!.Type != StreamType.Unknow) | ||||
| 					if (stream!.Type != StreamType.Unknown) | ||||
| 					{ | ||||
| 						tracks[j] = new Track(stream); | ||||
| 						j++; | ||||
|  | ||||
| @ -6,7 +6,7 @@ namespace Kyoo.Models | ||||
| { | ||||
| 	public class LibraryDE : Library | ||||
| 	{ | ||||
| 		[JsonIgnore] [NotMergable] public virtual ICollection<ProviderLink> ProviderLinks { get; set; } | ||||
| 		[EditableRelation] [JsonIgnore] [NotMergable] public virtual ICollection<ProviderLink> ProviderLinks { get; set; } | ||||
| 		[ExpressionRewrite(nameof(ProviderLinks), nameof(ProviderLink.Provider))] | ||||
| 		public override IEnumerable<ProviderID> Providers | ||||
| 		{ | ||||
|  | ||||
| @ -6,7 +6,7 @@ namespace Kyoo.Models | ||||
| { | ||||
| 	public class ShowDE : Show | ||||
| 	{ | ||||
| 		[JsonReadOnly] [NotMergable] public virtual ICollection<GenreLink> GenreLinks { get; set; } | ||||
| 		[EditableRelation] [JsonReadOnly] [NotMergable] public virtual ICollection<GenreLink> GenreLinks { get; set; } | ||||
| 		[ExpressionRewrite(nameof(GenreLinks), nameof(GenreLink.Genre))] | ||||
| 		public override IEnumerable<Genre> Genres | ||||
| 		{ | ||||
|  | ||||
| @ -41,7 +41,7 @@ namespace Kyoo | ||||
| 			}); | ||||
| 
 | ||||
| 			services.AddControllers() | ||||
| 				.AddNewtonsoftJson(x => x.SerializerSettings.ContractResolver = new JsonPropertySelector()); | ||||
| 				.AddNewtonsoftJson(x => x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer()); | ||||
| 			services.AddHttpClient(); | ||||
| 
 | ||||
| 			services.AddDbContext<DatabaseContext>(options => | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user