@ -93,17 +93,29 @@ namespace Emby.Server.Implementations.Updates
public IEnumerable < InstallationInfo > CompletedInstallations = > _completedInstallationsInternal ;
/// <inheritdoc />
public async Task < I ReadOnly List< PackageInfo > > GetPackages ( string manifest , CancellationToken cancellationToken = default )
public async Task < I List< PackageInfo > > GetPackages ( string manifestName , string manifest , CancellationToken cancellationToken = default )
{
try
{
using var response = await _httpClientFactory . CreateClient ( NamedClient . Default )
. GetAsync ( manifest , cancellationToken ) . ConfigureAwait ( false ) ;
. GetAsync ( new Uri ( manifest ) , cancellationToken ) . ConfigureAwait ( false ) ;
await using var stream = await response . Content . ReadAsStreamAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
try
{
return await _jsonSerializer . DeserializeFromStreamAsync < IReadOnlyList < PackageInfo > > ( stream ) . ConfigureAwait ( false ) ;
var package = await _jsonSerializer . DeserializeFromStreamAsync < IList < PackageInfo > > ( stream ) . ConfigureAwait ( false ) ;
// Store the repository and repository url with each version, as they may be spread apart.
foreach ( var entry in package )
{
foreach ( var ver in entry . versions )
{
ver . repositoryName = manifestName ;
ver . repositoryUrl = manifest ;
}
}
return package ;
}
catch ( SerializationException ex )
{
@ -123,17 +135,69 @@ namespace Emby.Server.Implementations.Updates
}
}
private static void MergeSort ( IList < VersionInfo > source , IList < VersionInfo > dest )
{
int sLength = source . Count - 1 ;
int dLength = dest . Count ;
int s = 0 , d = 0 ;
var sourceVersion = source [ 0 ] . VersionNumber ;
var destVersion = dest [ 0 ] . VersionNumber ;
while ( d < dLength )
{
if ( sourceVersion . CompareTo ( destVersion ) > = 0 )
{
if ( s < sLength )
{
sourceVersion = source [ + + s ] . VersionNumber ;
}
else
{
// Append all of destination to the end of source.
while ( d < dLength )
{
source . Add ( dest [ d + + ] ) ;
}
break ;
}
}
else
{
source . Insert ( s + + , dest [ d + + ] ) ;
if ( d > = dLength )
{
break ;
}
sLength + + ;
destVersion = dest [ d ] . VersionNumber ;
}
}
}
/// <inheritdoc />
public async Task < IReadOnlyList < PackageInfo > > GetAvailablePackages ( CancellationToken cancellationToken = default )
{
var result = new List < PackageInfo > ( ) ;
foreach ( RepositoryInfo repository in _config . Configuration . PluginRepositories )
{
foreach ( var package in await GetPackages ( repository . Url , cancellationToken ) . ConfigureAwait ( true ) )
if ( repository . Enabled )
{
package . repositoryName = repository . Name ;
package . repositoryUrl = repository . Url ;
result . Add ( package ) ;
// Where repositories have the same content, the details of the first is taken.
foreach ( var package in await GetPackages ( repository . Name , repository . Url , cancellationToken ) . ConfigureAwait ( true ) )
{
var existing = FilterPackages ( result , package . name , Guid . Parse ( package . guid ) ) . FirstOrDefault ( ) ;
if ( existing ! = null )
{
// Assumption is both lists are ordered, so slot these into the correct place.
MergeSort ( existing . versions , package . versions ) ;
}
else
{
result . Add ( package ) ;
}
}
}
}
@ -144,7 +208,8 @@ namespace Emby.Server.Implementations.Updates
public IEnumerable < PackageInfo > FilterPackages (
IEnumerable < PackageInfo > availablePackages ,
string name = null ,
Guid guid = default )
Guid guid = default ,
Version specificVersion = null )
{
if ( name ! = null )
{
@ -156,6 +221,11 @@ namespace Emby.Server.Implementations.Updates
availablePackages = availablePackages . Where ( x = > Guid . Parse ( x . guid ) = = guid ) ;
}
if ( specificVersion ! = null )
{
availablePackages = availablePackages . Where ( x = > x . versions . Where ( y = > y . VersionNumber . Equals ( specificVersion ) ) . Any ( ) ) ;
}
return availablePackages ;
}
@ -167,7 +237,7 @@ namespace Emby.Server.Implementations.Updates
Version minVersion = null ,
Version specificVersion = null )
{
var package = FilterPackages ( availablePackages , name , guid ). FirstOrDefault ( ) ;
var package = FilterPackages ( availablePackages , name , guid , specificVersion ). FirstOrDefault ( ) ;
// Package not found in repository
if ( package = = null )
@ -181,21 +251,21 @@ namespace Emby.Server.Implementations.Updates
if ( specificVersion ! = null )
{
availableVersions = availableVersions . Where ( x = > new Version ( x . version ) = = specificVersion ) ;
availableVersions = availableVersions . Where ( x = > x . VersionNumber . Equals ( specificVersion ) ) ;
}
else if ( minVersion ! = null )
{
availableVersions = availableVersions . Where ( x = > new Version ( x . version ) > = minVersion ) ;
availableVersions = availableVersions . Where ( x = > x . VersionNumber > = minVersion ) ;
}
foreach ( var v in availableVersions . OrderByDescending ( x = > x . version ) )
foreach ( var v in availableVersions . OrderByDescending ( x = > x . VersionNumber ) )
{
yield return new InstallationInfo
{
Changelog = v . changelog ,
Guid = new Guid ( package . guid ) ,
Name = package . name ,
Version = new Version ( v . version ) ,
Version = v . VersionNumber ,
SourceUrl = v . sourceUrl ,
Checksum = v . checksum
} ;
@ -322,7 +392,7 @@ namespace Emby.Server.Implementations.Updates
private async Task PerformPackageInstallation ( InstallationInfo package , CancellationToken cancellationToken )
{
var extension = Path . GetExtension ( package . SourceUrl );
var extension = Path . GetExtension ( package . SourceUrl .ToString ( ) );
if ( ! string . Equals ( extension , ".zip" , StringComparison . OrdinalIgnoreCase ) )
{
_logger . LogError ( "Only zip packages are supported. {SourceUrl} is not a zip archive." , package . SourceUrl ) ;