aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Data/Entities/Libraries
diff options
context:
space:
mode:
authorferferga <ferferga.fer@gmail.com>2020-10-19 17:28:07 +0200
committerferferga <ferferga.fer@gmail.com>2020-10-19 17:28:07 +0200
commit9fd01fade6ac971ba72a2e7dd54e2295f23839bc (patch)
tree20a5ff4a1621e765fc93c0e53b149edb61ff3654 /Jellyfin.Data/Entities/Libraries
parentba03ed65fe64b724b3e8b5b94b9cbe1075c61da2 (diff)
parent49ac4c4044b1777dc3a25544aead7b0b15b953e8 (diff)
Remove "download images in advance" option
Diffstat (limited to 'Jellyfin.Data/Entities/Libraries')
-rw-r--r--Jellyfin.Data/Entities/Libraries/Artwork.cs83
-rw-r--r--Jellyfin.Data/Entities/Libraries/Book.cs30
-rw-r--r--Jellyfin.Data/Entities/Libraries/BookMetadata.cs57
-rw-r--r--Jellyfin.Data/Entities/Libraries/Chapter.cs104
-rw-r--r--Jellyfin.Data/Entities/Libraries/Collection.cs57
-rw-r--r--Jellyfin.Data/Entities/Libraries/CollectionItem.cs94
-rw-r--r--Jellyfin.Data/Entities/Libraries/Company.cs69
-rw-r--r--Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs74
-rw-r--r--Jellyfin.Data/Entities/Libraries/CustomItem.cs30
-rw-r--r--Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs36
-rw-r--r--Jellyfin.Data/Entities/Libraries/Episode.cs54
-rw-r--r--Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/Genre.cs75
-rw-r--r--Jellyfin.Data/Entities/Libraries/ItemMetadata.cs167
-rw-r--r--Jellyfin.Data/Entities/Libraries/Library.cs76
-rw-r--r--Jellyfin.Data/Entities/Libraries/LibraryItem.cs63
-rw-r--r--Jellyfin.Data/Entities/Libraries/MediaFile.cs96
-rw-r--r--Jellyfin.Data/Entities/Libraries/MediaFileStream.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/MetadataProvider.cs67
-rw-r--r--Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs83
-rw-r--r--Jellyfin.Data/Entities/Libraries/Movie.cs30
-rw-r--r--Jellyfin.Data/Entities/Libraries/MovieMetadata.cs87
-rw-r--r--Jellyfin.Data/Entities/Libraries/MusicAlbum.cs31
-rw-r--r--Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs71
-rw-r--r--Jellyfin.Data/Entities/Libraries/Person.cs105
-rw-r--r--Jellyfin.Data/Entities/Libraries/PersonRole.cs100
-rw-r--r--Jellyfin.Data/Entities/Libraries/Photo.cs30
-rw-r--r--Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs36
-rw-r--r--Jellyfin.Data/Entities/Libraries/Rating.cs78
-rw-r--r--Jellyfin.Data/Entities/Libraries/RatingSource.cs92
-rw-r--r--Jellyfin.Data/Entities/Libraries/Release.cs86
-rw-r--r--Jellyfin.Data/Entities/Libraries/Season.cs55
-rw-r--r--Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs47
-rw-r--r--Jellyfin.Data/Entities/Libraries/Series.cs48
-rw-r--r--Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs93
-rw-r--r--Jellyfin.Data/Entities/Libraries/Track.cs54
-rw-r--r--Jellyfin.Data/Entities/Libraries/TrackMetadata.cs36
37 files changed, 2528 insertions, 0 deletions
diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs
new file mode 100644
index 0000000000..06cd333301
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs
@@ -0,0 +1,83 @@
+#pragma warning disable CA2227
+
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing artwork.
+ /// </summary>
+ public class Artwork : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Artwork"/> class.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="kind">The kind of art.</param>
+ /// <param name="owner">The owner.</param>
+ public Artwork(string path, ArtKind kind, IHasArtwork owner)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ Path = path;
+ Kind = kind;
+
+ owner?.Artwork.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Artwork"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Artwork()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 65535.
+ /// </remarks>
+ [Required]
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Path { get; set; }
+
+ /// <summary>
+ /// Gets or sets the kind of artwork.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public ArtKind Kind { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Book.cs b/Jellyfin.Data/Entities/Libraries/Book.cs
new file mode 100644
index 0000000000..2e63f75bd9
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Book.cs
@@ -0,0 +1,30 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a book.
+ /// </summary>
+ public class Book : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Book"/> class.
+ /// </summary>
+ public Book()
+ {
+ BookMetadata = new HashSet<BookMetadata>();
+ Releases = new HashSet<Release>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this book.
+ /// </summary>
+ public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
new file mode 100644
index 0000000000..4a3d290f01
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs
@@ -0,0 +1,57 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity containing metadata for a book.
+ /// </summary>
+ public class BookMetadata : ItemMetadata, IHasCompanies
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BookMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="book">The book.</param>
+ public BookMetadata(string title, string language, Book book) : base(title, language)
+ {
+ if (book == null)
+ {
+ throw new ArgumentNullException(nameof(book));
+ }
+
+ book.BookMetadata.Add(this);
+
+ Publishers = new HashSet<Company>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BookMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected BookMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the ISBN.
+ /// </summary>
+ public long? Isbn { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection of the publishers for this book.
+ /// </summary>
+ public virtual ICollection<Company> Publishers { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => Publishers;
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs
new file mode 100644
index 0000000000..f503de3794
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs
@@ -0,0 +1,104 @@
+#pragma warning disable CA2227
+
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a chapter.
+ /// </summary>
+ public class Chapter : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Chapter"/> class.
+ /// </summary>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="startTime">The start time for this chapter.</param>
+ /// <param name="release">The release.</param>
+ public Chapter(string language, long startTime, Release release)
+ {
+ if (string.IsNullOrEmpty(language))
+ {
+ throw new ArgumentNullException(nameof(language));
+ }
+
+ Language = language;
+ StartTime = startTime;
+
+ if (release == null)
+ {
+ throw new ArgumentNullException(nameof(release));
+ }
+
+ release.Chapters.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Chapter"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Chapter()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the language.
+ /// </summary>
+ /// <remarks>
+ /// Required, Min length = 3, Max length = 3
+ /// ISO-639-3 3-character language codes.
+ /// </remarks>
+ [Required]
+ [MinLength(3)]
+ [MaxLength(3)]
+ [StringLength(3)]
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start time.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public long StartTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the end time.
+ /// </summary>
+ public long? EndTime { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs
new file mode 100644
index 0000000000..39eded752d
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Collection.cs
@@ -0,0 +1,57 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a collection.
+ /// </summary>
+ public class Collection : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Collection"/> class.
+ /// </summary>
+ public Collection()
+ {
+ Items = new HashSet<CollectionItem>();
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing this collection's items.
+ /// </summary>
+ public virtual ICollection<CollectionItem> Items { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
new file mode 100644
index 0000000000..4467c9bbdc
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs
@@ -0,0 +1,94 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a collection item.
+ /// </summary>
+ public class CollectionItem : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionItem"/> class.
+ /// </summary>
+ /// <param name="collection">The collection.</param>
+ /// <param name="previous">The previous item.</param>
+ /// <param name="next">The next item.</param>
+ public CollectionItem(Collection collection, CollectionItem previous, CollectionItem next)
+ {
+ if (collection == null)
+ {
+ throw new ArgumentNullException(nameof(collection));
+ }
+
+ collection.Items.Add(this);
+
+ if (next != null)
+ {
+ Next = next;
+ next.Previous = this;
+ }
+
+ if (previous != null)
+ {
+ Previous = previous;
+ previous.Next = this;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionItem"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected CollectionItem()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the library item.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public virtual LibraryItem LibraryItem { get; set; }
+
+ /// <summary>
+ /// Gets or sets the next item in the collection.
+ /// </summary>
+ /// <remarks>
+ /// TODO check if this properly updated dependant and has the proper principal relationship.
+ /// </remarks>
+ public virtual CollectionItem Next { get; set; }
+
+ /// <summary>
+ /// Gets or sets the previous item in the collection.
+ /// </summary>
+ /// <remarks>
+ /// TODO check if this properly updated dependant and has the proper principal relationship.
+ /// </remarks>
+ public virtual CollectionItem Previous { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs
new file mode 100644
index 0000000000..3b6ed3309a
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Company.cs
@@ -0,0 +1,69 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a company.
+ /// </summary>
+ public class Company : IHasCompanies, IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Company"/> class.
+ /// </summary>
+ /// <param name="owner">The owner of this company.</param>
+ public Company(IHasCompanies owner)
+ {
+ owner?.Companies.Add(this);
+
+ CompanyMetadata = new HashSet<CompanyMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Company"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Company()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata.
+ /// </summary>
+ public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing this company's child companies.
+ /// </summary>
+ public virtual ICollection<Company> ChildCompanies { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => ChildCompanies;
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs
new file mode 100644
index 0000000000..8aa0486afa
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs
@@ -0,0 +1,74 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding metadata for a <see cref="Company"/>.
+ /// </summary>
+ public class CompanyMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CompanyMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="company">The company.</param>
+ public CompanyMetadata(string title, string language, Company company) : base(title, language)
+ {
+ if (company == null)
+ {
+ throw new ArgumentNullException(nameof(company));
+ }
+
+ company.CompanyMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CompanyMetadata"/> class.
+ /// </summary>
+ protected CompanyMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets the headquarters.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string Headquarters { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets the homepage.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Homepage { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CustomItem.cs b/Jellyfin.Data/Entities/Libraries/CustomItem.cs
new file mode 100644
index 0000000000..115489c786
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CustomItem.cs
@@ -0,0 +1,30 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a custom item.
+ /// </summary>
+ public class CustomItem : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomItem"/> class.
+ /// </summary>
+ public CustomItem()
+ {
+ CustomItemMetadata = new HashSet<CustomItemMetadata>();
+ Releases = new HashSet<Release>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this item.
+ /// </summary>
+ public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs
new file mode 100644
index 0000000000..f0daedfbe8
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity containing metadata for a custom item.
+ /// </summary>
+ public class CustomItemMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomItemMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="item">The item.</param>
+ public CustomItemMetadata(string title, string language, CustomItem item) : base(title, language)
+ {
+ if (item == null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+
+ item.CustomItemMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CustomItemMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected CustomItemMetadata()
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Episode.cs b/Jellyfin.Data/Entities/Libraries/Episode.cs
new file mode 100644
index 0000000000..0bdc2d7642
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Episode.cs
@@ -0,0 +1,54 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing an episode.
+ /// </summary>
+ public class Episode : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Episode"/> class.
+ /// </summary>
+ /// <param name="season">The season.</param>
+ public Episode(Season season)
+ {
+ if (season == null)
+ {
+ throw new ArgumentNullException(nameof(season));
+ }
+
+ season.Episodes.Add(this);
+
+ Releases = new HashSet<Release>();
+ EpisodeMetadata = new HashSet<EpisodeMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Episode"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Episode()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the episode number.
+ /// </summary>
+ public int? EpisodeNumber { get; set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this episode.
+ /// </summary>
+ public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs
new file mode 100644
index 0000000000..7efb840f0b
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity containing metadata for an <see cref="Episode"/>.
+ /// </summary>
+ public class EpisodeMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="episode">The episode.</param>
+ public EpisodeMetadata(string title, string language, Episode episode) : base(title, language)
+ {
+ if (episode == null)
+ {
+ throw new ArgumentNullException(nameof(episode));
+ }
+
+ episode.EpisodeMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EpisodeMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected EpisodeMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plot.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Plot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tagline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Tagline { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs
new file mode 100644
index 0000000000..2a2dbd1a5d
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Genre.cs
@@ -0,0 +1,75 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a genre.
+ /// </summary>
+ public class Genre : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Genre"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="itemMetadata">The metadata.</param>
+ public Genre(string name, ItemMetadata itemMetadata)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+
+ if (itemMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(itemMetadata));
+ }
+
+ itemMetadata.Genres.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Genre"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Genre()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Indexed, Required, Max length = 255.
+ /// </remarks>
+ [Required]
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs
new file mode 100644
index 0000000000..1d2dc0f669
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs
@@ -0,0 +1,167 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An abstract class that holds metadata.
+ /// </summary>
+ public abstract class ItemMetadata : IHasArtwork, IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ItemMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ protected ItemMetadata(string title, string language)
+ {
+ if (string.IsNullOrEmpty(title))
+ {
+ throw new ArgumentNullException(nameof(title));
+ }
+
+ if (string.IsNullOrEmpty(language))
+ {
+ throw new ArgumentNullException(nameof(language));
+ }
+
+ Title = title;
+ Language = language;
+ DateAdded = DateTime.UtcNow;
+ DateModified = DateAdded;
+
+ PersonRoles = new HashSet<PersonRole>();
+ Genres = new HashSet<Genre>();
+ Artwork = new HashSet<Artwork>();
+ Ratings = new HashSet<Rating>();
+ Sources = new HashSet<MetadataProviderId>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ItemMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to being abstract.
+ /// </remarks>
+ protected ItemMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Title { get; set; }
+
+ /// <summary>
+ /// Gets or sets the original title.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string OriginalTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sort title.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string SortTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the language.
+ /// </summary>
+ /// <remarks>
+ /// Required, Min length = 3, Max length = 3.
+ /// ISO-639-3 3-character language codes.
+ /// </remarks>
+ [Required]
+ [MinLength(3)]
+ [MaxLength(3)]
+ [StringLength(3)]
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the release date.
+ /// </summary>
+ public DateTimeOffset? ReleaseDate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date added.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateAdded { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the date modified.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateModified { get; set; }
+
+ /// <summary>
+ /// Gets or sets the row version.
+ /// </summary>
+ /// <remarks>
+ /// Required, ConcurrencyToken.
+ /// </remarks>
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the person roles for this item.
+ /// </summary>
+ public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the generes for this item.
+ /// </summary>
+ public virtual ICollection<Genre> Genres { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Artwork> Artwork { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the ratings for this item.
+ /// </summary>
+ public virtual ICollection<Rating> Ratings { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata sources for this item.
+ /// </summary>
+ public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs
new file mode 100644
index 0000000000..4f82a2e2a7
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Library.cs
@@ -0,0 +1,76 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a library.
+ /// </summary>
+ public class Library : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Library"/> class.
+ /// </summary>
+ /// <param name="name">The name of the library.</param>
+ public Library(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Library"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Library()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 128.
+ /// </remarks>
+ [Required]
+ [MaxLength(128)]
+ [StringLength(128)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the root path of the library.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public string Path { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
new file mode 100644
index 0000000000..a9167aa7fd
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs
@@ -0,0 +1,63 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a library item.
+ /// </summary>
+ public abstract class LibraryItem : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibraryItem"/> class.
+ /// </summary>
+ /// <param name="library">The library of this item.</param>
+ protected LibraryItem(Library library)
+ {
+ DateAdded = DateTime.UtcNow;
+ Library = library;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibraryItem"/> class.
+ /// </summary>
+ protected LibraryItem()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the date this library item was added.
+ /// </summary>
+ public DateTime DateAdded { get; protected set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the library of this item.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public virtual Library Library { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs
new file mode 100644
index 0000000000..9924d5728d
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs
@@ -0,0 +1,96 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a file on disk.
+ /// </summary>
+ public class MediaFile : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFile"/> class.
+ /// </summary>
+ /// <param name="path">The path relative to the LibraryRoot.</param>
+ /// <param name="kind">The file kind.</param>
+ /// <param name="release">The release.</param>
+ public MediaFile(string path, MediaFileKind kind, Release release)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ Path = path;
+ Kind = kind;
+
+ if (release == null)
+ {
+ throw new ArgumentNullException(nameof(release));
+ }
+
+ release.MediaFiles.Add(this);
+
+ MediaFileStreams = new HashSet<MediaFileStream>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFile"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MediaFile()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the path relative to the library root.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 65535.
+ /// </remarks>
+ [Required]
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Path { get; set; }
+
+ /// <summary>
+ /// Gets or sets the kind of media file.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public MediaFileKind Kind { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the streams in this file.
+ /// </summary>
+ public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
new file mode 100644
index 0000000000..5b03e260e2
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a stream in a media file.
+ /// </summary>
+ public class MediaFileStream : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFileStream"/> class.
+ /// </summary>
+ /// <param name="streamNumber">The number of this stream.</param>
+ /// <param name="mediaFile">The media file.</param>
+ public MediaFileStream(int streamNumber, MediaFile mediaFile)
+ {
+ StreamNumber = streamNumber;
+
+ if (mediaFile == null)
+ {
+ throw new ArgumentNullException(nameof(mediaFile));
+ }
+
+ mediaFile.MediaFileStreams.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaFileStream"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MediaFileStream()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the stream number.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public int StreamNumber { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
new file mode 100644
index 0000000000..a18a612bcd
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a metadata provider.
+ /// </summary>
+ public class MetadataProvider : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProvider"/> class.
+ /// </summary>
+ /// <param name="name">The name of the metadata provider.</param>
+ public MetadataProvider(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProvider"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MetadataProvider()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
new file mode 100644
index 0000000000..fcfb35bfac
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
@@ -0,0 +1,83 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a unique identifier for a metadata provider.
+ /// </summary>
+ public class MetadataProviderId : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProviderId"/> class.
+ /// </summary>
+ /// <param name="providerId">The provider id.</param>
+ /// <param name="itemMetadata">The metadata entity.</param>
+ public MetadataProviderId(string providerId, ItemMetadata itemMetadata)
+ {
+ if (string.IsNullOrEmpty(providerId))
+ {
+ throw new ArgumentNullException(nameof(providerId));
+ }
+
+ ProviderId = providerId;
+
+ if (itemMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(itemMetadata));
+ }
+
+ itemMetadata.Sources.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MetadataProviderId"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MetadataProviderId()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the provider id.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 255.
+ /// </remarks>
+ [Required]
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string ProviderId { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the metadata provider.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public virtual MetadataProvider MetadataProvider { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Movie.cs b/Jellyfin.Data/Entities/Libraries/Movie.cs
new file mode 100644
index 0000000000..08db904fa8
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Movie.cs
@@ -0,0 +1,30 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a movie.
+ /// </summary>
+ public class Movie : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Movie"/> class.
+ /// </summary>
+ public Movie()
+ {
+ Releases = new HashSet<Release>();
+ MovieMetadata = new HashSet<MovieMetadata>();
+ }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata for this movie.
+ /// </summary>
+ public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
new file mode 100644
index 0000000000..aa1501a5cc
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
@@ -0,0 +1,87 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding the metadata for a movie.
+ /// </summary>
+ public class MovieMetadata : ItemMetadata, IHasCompanies
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the movie.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="movie">The movie.</param>
+ public MovieMetadata(string title, string language, Movie movie) : base(title, language)
+ {
+ Studios = new HashSet<Company>();
+
+ movie.MovieMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MovieMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tagline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Tagline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plot.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Plot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets the studios that produced this movie.
+ /// </summary>
+ public virtual ICollection<Company> Studios { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => Studios;
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
new file mode 100644
index 0000000000..06aff6f457
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
@@ -0,0 +1,31 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a music album.
+ /// </summary>
+ public class MusicAlbum : LibraryItem
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbum"/> class.
+ /// </summary>
+ public MusicAlbum()
+ {
+ MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>();
+ Tracks = new HashSet<Track>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the album metadata.
+ /// </summary>
+ public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the tracks.
+ /// </summary>
+ public virtual ICollection<Track> Tracks { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
new file mode 100644
index 0000000000..05c0b0374b
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
@@ -0,0 +1,71 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding the metadata for a music album.
+ /// </summary>
+ public class MusicAlbumMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbumMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the album.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="album">The music album.</param>
+ public MusicAlbumMetadata(string title, string language, MusicAlbum album) : base(title, language)
+ {
+ Labels = new HashSet<Company>();
+
+ album.MusicAlbumMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbumMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected MusicAlbumMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the barcode.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string Barcode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the label number.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(255)]
+ [StringLength(255)]
+ public string LabelNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the labels.
+ /// </summary>
+ public virtual ICollection<Company> Labels { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs
new file mode 100644
index 0000000000..af4c87b73c
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Person.cs
@@ -0,0 +1,105 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a person.
+ /// </summary>
+ public class Person : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Person"/> class.
+ /// </summary>
+ /// <param name="name">The name of the person.</param>
+ public Person(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+ DateAdded = DateTime.UtcNow;
+ DateModified = DateAdded;
+
+ Sources = new HashSet<MetadataProviderId>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Person"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Person()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the source id.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 255.
+ /// </remarks>
+ [MaxLength(256)]
+ [StringLength(256)]
+ public string SourceId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date added.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateAdded { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the date modified.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public DateTime DateModified { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a list of metadata sources for this person.
+ /// </summary>
+ public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs
new file mode 100644
index 0000000000..cd38ee83d0
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs
@@ -0,0 +1,100 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Enums;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a person's role in media.
+ /// </summary>
+ public class PersonRole : IHasArtwork, IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PersonRole"/> class.
+ /// </summary>
+ /// <param name="type">The role type.</param>
+ /// <param name="itemMetadata">The metadata.</param>
+ public PersonRole(PersonRoleType type, ItemMetadata itemMetadata)
+ {
+ Type = type;
+
+ if (itemMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(itemMetadata));
+ }
+
+ itemMetadata.PersonRoles.Add(this);
+
+ Sources = new HashSet<MetadataProviderId>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PersonRole"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected PersonRole()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name of the person's role.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Role { get; set; }
+
+ /// <summary>
+ /// Gets or sets the person's role type.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public PersonRoleType Type { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the person.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ [Required]
+ public virtual Person Person { get; set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Artwork> Artwork { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the metadata sources for this person role.
+ /// </summary>
+ public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Photo.cs b/Jellyfin.Data/Entities/Libraries/Photo.cs
new file mode 100644
index 0000000000..25562ec96f
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Photo.cs
@@ -0,0 +1,30 @@
+#pragma warning disable CA2227
+
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a photo.
+ /// </summary>
+ public class Photo : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Photo"/> class.
+ /// </summary>
+ public Photo()
+ {
+ PhotoMetadata = new HashSet<PhotoMetadata>();
+ Releases = new HashSet<Release>();
+ }
+
+ /// <summary>
+ /// Gets or sets a collection containing the photo metadata.
+ /// </summary>
+ public virtual ICollection<PhotoMetadata> PhotoMetadata { get; protected set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs
new file mode 100644
index 0000000000..ffc790b574
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity that holds metadata for a photo.
+ /// </summary>
+ public class PhotoMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the photo.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="photo">The photo.</param>
+ public PhotoMetadata(string title, string language, Photo photo) : base(title, language)
+ {
+ if (photo == null)
+ {
+ throw new ArgumentNullException(nameof(photo));
+ }
+
+ photo.PhotoMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected PhotoMetadata()
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs
new file mode 100644
index 0000000000..98226cd802
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Rating.cs
@@ -0,0 +1,78 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a rating for an entity.
+ /// </summary>
+ public class Rating : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Rating"/> class.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <param name="itemMetadata">The metadata.</param>
+ public Rating(double value, ItemMetadata itemMetadata)
+ {
+ Value = value;
+
+ if (itemMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(itemMetadata));
+ }
+
+ itemMetadata.Ratings.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Rating"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Rating()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the value.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public double Value { get; set; }
+
+ /// <summary>
+ /// Gets or sets the number of votes.
+ /// </summary>
+ public int? Votes { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rating type.
+ /// If this is <c>null</c> it's the internal user rating.
+ /// </summary>
+ public virtual RatingSource RatingType { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs
new file mode 100644
index 0000000000..549f418042
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs
@@ -0,0 +1,92 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// This is the entity to store review ratings, not age ratings.
+ /// </summary>
+ public class RatingSource : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RatingSource"/> class.
+ /// </summary>
+ /// <param name="minimumValue">The minimum value.</param>
+ /// <param name="maximumValue">The maximum value.</param>
+ /// <param name="rating">The rating.</param>
+ public RatingSource(double minimumValue, double maximumValue, Rating rating)
+ {
+ MinimumValue = minimumValue;
+ MaximumValue = maximumValue;
+
+ if (rating == null)
+ {
+ throw new ArgumentNullException(nameof(rating));
+ }
+
+ rating.RatingType = this;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RatingSource"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected RatingSource()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the minimum value.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public double MinimumValue { get; set; }
+
+ /// <summary>
+ /// Gets or sets the maximum value.
+ /// </summary>
+ /// <remarks>
+ /// Required.
+ /// </remarks>
+ public double MaximumValue { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the metadata source.
+ /// </summary>
+ public virtual MetadataProviderId Source { get; set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs
new file mode 100644
index 0000000000..b633e08fb3
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Release.cs
@@ -0,0 +1,86 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a release for a library item, eg. Director's cut vs. standard.
+ /// </summary>
+ public class Release : IHasConcurrencyToken
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Release"/> class.
+ /// </summary>
+ /// <param name="name">The name of this release.</param>
+ /// <param name="owner">The owner of this release.</param>
+ public Release(string name, IHasReleases owner)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Name = name;
+
+ owner?.Releases.Add(this);
+
+ MediaFiles = new HashSet<MediaFile>();
+ Chapters = new HashSet<Chapter>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Release"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Release()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <remarks>
+ /// Identity, Indexed, Required.
+ /// </remarks>
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int Id { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <remarks>
+ /// Required, Max length = 1024.
+ /// </remarks>
+ [Required]
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Name { get; set; }
+
+ /// <inheritdoc />
+ [ConcurrencyCheck]
+ public uint RowVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the media files for this release.
+ /// </summary>
+ public virtual ICollection<MediaFile> MediaFiles { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the chapters for this release.
+ /// </summary>
+ public virtual ICollection<Chapter> Chapters { get; protected set; }
+
+ /// <inheritdoc />
+ public void OnSavingChanges()
+ {
+ RowVersion++;
+ }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Season.cs b/Jellyfin.Data/Entities/Libraries/Season.cs
new file mode 100644
index 0000000000..eb6674dbc3
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Season.cs
@@ -0,0 +1,55 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a season.
+ /// </summary>
+ public class Season : LibraryItem
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Season"/> class.
+ /// </summary>
+ /// <param name="series">The series.</param>
+ public Season(Series series)
+ {
+ if (series == null)
+ {
+ throw new ArgumentNullException(nameof(series));
+ }
+
+ series.Seasons.Add(this);
+
+ Episodes = new HashSet<Episode>();
+ SeasonMetadata = new HashSet<SeasonMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Season"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Season()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the season number.
+ /// </summary>
+ public int? SeasonNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the season metadata.
+ /// </summary>
+ public virtual ICollection<SeasonMetadata> SeasonMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the number of episodes.
+ /// </summary>
+ public virtual ICollection<Episode> Episodes { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs
new file mode 100644
index 0000000000..7ce79756b2
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs
@@ -0,0 +1,47 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity that holds metadata for seasons.
+ /// </summary>
+ public class SeasonMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="season">The season.</param>
+ public SeasonMetadata(string title, string language, Season season) : base(title, language)
+ {
+ if (season == null)
+ {
+ throw new ArgumentNullException(nameof(season));
+ }
+
+ season.SeasonMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeasonMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected SeasonMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Series.cs b/Jellyfin.Data/Entities/Libraries/Series.cs
new file mode 100644
index 0000000000..8c8317d14b
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Series.cs
@@ -0,0 +1,48 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a a series.
+ /// </summary>
+ public class Series : LibraryItem
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Series"/> class.
+ /// </summary>
+ public Series()
+ {
+ DateAdded = DateTime.UtcNow;
+ Seasons = new HashSet<Season>();
+ SeriesMetadata = new HashSet<SeriesMetadata>();
+ }
+
+ /// <summary>
+ /// Gets or sets the days of week.
+ /// </summary>
+ public DayOfWeek? AirsDayOfWeek { get; set; }
+
+ /// <summary>
+ /// Gets or sets the time the show airs, ignore the date portion.
+ /// </summary>
+ public DateTimeOffset? AirsTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date the series first aired.
+ /// </summary>
+ public DateTime? FirstAired { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the series metadata.
+ /// </summary>
+ public virtual ICollection<SeriesMetadata> SeriesMetadata { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the seasons.
+ /// </summary>
+ public virtual ICollection<Season> Seasons { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
new file mode 100644
index 0000000000..877dbfc69c
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
@@ -0,0 +1,93 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing series metadata.
+ /// </summary>
+ public class SeriesMetadata : ItemMetadata, IHasCompanies
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="series">The series.</param>
+ public SeriesMetadata(string title, string language, Series series) : base(title, language)
+ {
+ if (series == null)
+ {
+ throw new ArgumentNullException(nameof(series));
+ }
+
+ series.SeriesMetadata.Add(this);
+
+ Networks = new HashSet<Company>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected SeriesMetadata()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the outline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Outline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the plot.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 65535.
+ /// </remarks>
+ [MaxLength(65535)]
+ [StringLength(65535)]
+ public string Plot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tagline.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 1024.
+ /// </remarks>
+ [MaxLength(1024)]
+ [StringLength(1024)]
+ public string Tagline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country code.
+ /// </summary>
+ /// <remarks>
+ /// Max length = 2.
+ /// </remarks>
+ [MaxLength(2)]
+ [StringLength(2)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the networks.
+ /// </summary>
+ public virtual ICollection<Company> Networks { get; protected set; }
+
+ /// <inheritdoc />
+ [NotMapped]
+ public ICollection<Company> Companies => Networks;
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/Track.cs b/Jellyfin.Data/Entities/Libraries/Track.cs
new file mode 100644
index 0000000000..782bfb5ce7
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/Track.cs
@@ -0,0 +1,54 @@
+#pragma warning disable CA2227
+
+using System;
+using System.Collections.Generic;
+using Jellyfin.Data.Interfaces;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity representing a track.
+ /// </summary>
+ public class Track : LibraryItem, IHasReleases
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Track"/> class.
+ /// </summary>
+ /// <param name="album">The album.</param>
+ public Track(MusicAlbum album)
+ {
+ if (album == null)
+ {
+ throw new ArgumentNullException(nameof(album));
+ }
+
+ album.Tracks.Add(this);
+
+ Releases = new HashSet<Release>();
+ TrackMetadata = new HashSet<TrackMetadata>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Track"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected Track()
+ {
+ }
+
+ /// <summary>
+ /// Gets or sets the track number.
+ /// </summary>
+ public int? TrackNumber { get; set; }
+
+ /// <inheritdoc />
+ public virtual ICollection<Release> Releases { get; protected set; }
+
+ /// <summary>
+ /// Gets or sets a collection containing the track metadata.
+ /// </summary>
+ public virtual ICollection<TrackMetadata> TrackMetadata { get; protected set; }
+ }
+}
diff --git a/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs
new file mode 100644
index 0000000000..321f93bf2e
--- /dev/null
+++ b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Jellyfin.Data.Entities.Libraries
+{
+ /// <summary>
+ /// An entity holding metadata for a track.
+ /// </summary>
+ public class TrackMetadata : ItemMetadata
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackMetadata"/> class.
+ /// </summary>
+ /// <param name="title">The title or name of the object.</param>
+ /// <param name="language">ISO-639-3 3-character language codes.</param>
+ /// <param name="track">The track.</param>
+ public TrackMetadata(string title, string language, Track track) : base(title, language)
+ {
+ if (track == null)
+ {
+ throw new ArgumentNullException(nameof(track));
+ }
+
+ track.TrackMetadata.Add(this);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackMetadata"/> class.
+ /// </summary>
+ /// <remarks>
+ /// Default constructor. Protected due to required properties, but present because EF needs it.
+ /// </remarks>
+ protected TrackMetadata()
+ {
+ }
+ }
+}