mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-04-23 21:47:14 -04:00
Merge branch 'dev' into reformat
# Conflicts: # Emby.Server.Implementations/ApplicationHost.cs # Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs # Emby.Server.Implementations/LiveTv/LiveTvManager.cs # Emby.Server.Implementations/Security/MBLicenseFile.cs # Emby.Server.Implementations/Security/PluginSecurityManager.cs # Emby.Server.Implementations/Security/RegRecord.cs # MediaBrowser.Api/PluginService.cs # MediaBrowser.Api/System/SystemService.cs # MediaBrowser.Common/Security/IRequiresRegistration.cs # MediaBrowser.Common/Security/ISecurityManager.cs # MediaBrowser.Common/Security/PaymentRequiredException.cs # MediaBrowser.Model/Entities/MBRegistrationRecord.cs # MediaBrowser.Model/Entities/PluginSecurityInfo.cs # deployment/win-generic/build-jellyfin.ps1
This commit is contained in:
commit
49b61f238e
95 changed files with 1137 additions and 892 deletions
|
@ -3,3 +3,6 @@
|
|||
Dockerfile
|
||||
CONTRIBUTORS.md
|
||||
README.md
|
||||
deployment/*/dist
|
||||
deployment/*/pkg-dist
|
||||
deployment/collect-dist/
|
||||
|
|
35
.gitignore
vendored
35
.gitignore
vendored
|
@ -1,5 +1,3 @@
|
|||
!*
|
||||
|
||||
.directory
|
||||
|
||||
#################
|
||||
|
@ -49,6 +47,8 @@ ProgramData-UI*/
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
.vs/
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
|
@ -204,7 +204,6 @@ $RECYCLE.BIN/
|
|||
# Mac crap
|
||||
.DS_Store
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
@ -234,23 +233,33 @@ pip-log.txt
|
|||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
/.vs
|
||||
|
||||
##########
|
||||
# Rider
|
||||
##########
|
||||
.idea/
|
||||
|
||||
##########
|
||||
# Visual Studio Code
|
||||
##########
|
||||
.vscode/
|
||||
|
||||
#########################
|
||||
# Debian build artifacts
|
||||
# Build artifacts
|
||||
#########################
|
||||
|
||||
debian/.debhelper/
|
||||
debian/*.debhelper
|
||||
debian/debhelper-build-stamp
|
||||
debian/files
|
||||
debian/jellyfin.substvars
|
||||
debian/jellyfin/
|
||||
|
||||
# Artifacts for debian-x64
|
||||
deployment/debian-package-x64/pkg-src/.debhelper/
|
||||
deployment/debian-package-x64/pkg-src/*.debhelper
|
||||
deployment/debian-package-x64/pkg-src/debhelper-build-stamp
|
||||
deployment/debian-package-x64/pkg-src/files
|
||||
deployment/debian-package-x64/pkg-src/jellyfin.substvars
|
||||
deployment/debian-package-x64/pkg-src/jellyfin/
|
||||
# Don't ignore the debian/bin folder
|
||||
!debian/bin/
|
||||
!deployment/debian-package-x64/pkg-src/bin/
|
||||
|
||||
deployment/**/dist/
|
||||
deployment/**/pkg-dist/
|
||||
deployment/**/pkg-dist-tmp/
|
||||
deployment/collect-dist/
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
- [sparky8251](https://github.com/sparky8251)
|
||||
- [LeoVerto](https://github.com/LeoVerto)
|
||||
- [grafixeyehero](https://github.com/grafixeyehero)
|
||||
- [cvium](https://github.com/cvium)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
|
|
@ -26,6 +26,6 @@ COPY --from=builder /jellyfin /jellyfin
|
|||
COPY --from=ffmpeg /ffmpeg-bin/* /usr/bin/
|
||||
EXPOSE 8096
|
||||
VOLUME /config /media
|
||||
RUN apt update \
|
||||
&& apt install -y libfontconfig1 # needed for Skia
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y libfontconfig1 --no-install-recommends # needed for Skia
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
||||
|
|
|
@ -3,6 +3,7 @@ ARG DOTNET_VERSION=3.0
|
|||
FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder
|
||||
WORKDIR /repo
|
||||
COPY . .
|
||||
#TODO Remove or update the sed line when we update dotnet version.
|
||||
RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
||||
&& find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
|
||||
&& dotnet clean \
|
||||
|
@ -14,7 +15,7 @@ RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
|||
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime
|
||||
COPY --from=builder /jellyfin /jellyfin
|
||||
EXPOSE 8096
|
||||
RUN apt update \
|
||||
&& apt install -y ffmpeg
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ffmpeg
|
||||
VOLUME /config /media
|
||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
|
@ -54,7 +54,11 @@ using MediaBrowser.Common.Events;
|
|||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
<<<<<<< HEAD
|
||||
using MediaBrowser.Common.Security;
|
||||
=======
|
||||
using MediaBrowser.Model.Extensions;
|
||||
>>>>>>> dev
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
|
@ -340,11 +344,6 @@ namespace Emby.Server.Implementations
|
|||
/// </summary>
|
||||
/// <value>The installation manager.</value>
|
||||
protected IInstallationManager InstallationManager { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the security manager.
|
||||
/// </summary>
|
||||
/// <value>The security manager.</value>
|
||||
protected ISecurityManager SecurityManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the zip client.
|
||||
|
@ -804,10 +803,7 @@ namespace Emby.Server.Implementations
|
|||
SocketFactory = new SocketFactory(LoggerFactory.CreateLogger("SocketFactory"));
|
||||
RegisterSingleInstance(SocketFactory);
|
||||
|
||||
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LoggerFactory, FileSystemManager, CryptographyProvider);
|
||||
RegisterSingleInstance(SecurityManager);
|
||||
|
||||
InstallationManager = new InstallationManager(LoggerFactory.CreateLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
|
||||
InstallationManager = new InstallationManager(LoggerFactory.CreateLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
|
||||
RegisterSingleInstance(InstallationManager);
|
||||
|
||||
ZipClient = new ZipClient(FileSystemManager);
|
||||
|
@ -922,7 +918,7 @@ namespace Emby.Server.Implementations
|
|||
PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LoggerFactory.CreateLogger("PlaylistManager"), UserManager, ProviderManager);
|
||||
RegisterSingleInstance(PlaylistManager);
|
||||
|
||||
LiveTvManager = new LiveTvManager(this, HttpClient, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager, () => ChannelManager);
|
||||
LiveTvManager = new LiveTvManager(this, HttpClient, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, () => ChannelManager);
|
||||
RegisterSingleInstance(LiveTvManager);
|
||||
|
||||
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
|
||||
|
|
|
@ -12,7 +12,6 @@ using Emby.Server.Implementations.Net;
|
|||
using Emby.Server.Implementations.Services;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Net;
|
||||
|
@ -85,7 +84,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
{typeof (FileNotFoundException), 404},
|
||||
//{typeof (DirectoryNotFoundException), 404},
|
||||
{typeof (SecurityException), 401},
|
||||
{typeof (PaymentRequiredException), 402},
|
||||
{typeof (ArgumentException), 400}
|
||||
};
|
||||
|
||||
|
|
|
@ -451,18 +451,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
|
||||
public ChannelInfo GetChannelByNumber(string number)
|
||||
{
|
||||
ChannelInfo result = null;
|
||||
|
||||
ChannelsByNumber.TryGetValue(number, out result);
|
||||
ChannelsByNumber.TryGetValue(number, out var result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ChannelInfo GetChannelByName(string name)
|
||||
{
|
||||
ChannelInfo result = null;
|
||||
|
||||
ChannelsByName.TryGetValue(name, out result);
|
||||
ChannelsByName.TryGetValue(name, out var result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1178,14 +1174,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
return;
|
||||
}
|
||||
|
||||
var registration = await _liveTvManager.GetRegistrationInfo("dvr").ConfigureAwait(false);
|
||||
if (!registration.IsValid)
|
||||
{
|
||||
_logger.LogWarning("Emby Premiere required to use Emby DVR.");
|
||||
OnTimerOutOfDate(timer);
|
||||
return;
|
||||
}
|
||||
|
||||
var activeRecordingInfo = new ActiveRecordingInfo
|
||||
{
|
||||
CancellationTokenSource = new CancellationTokenSource(),
|
||||
|
@ -2372,41 +2360,57 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
var allTimers = GetTimersForSeries(seriesTimer)
|
||||
.ToList();
|
||||
|
||||
var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
|
||||
|
||||
var enabledTimersForSeries = new List<TimerInfo>();
|
||||
|
||||
if (registration.IsValid)
|
||||
foreach (var timer in allTimers)
|
||||
{
|
||||
foreach (var timer in allTimers)
|
||||
var existingTimer = _timerProvider.GetTimer(timer.Id);
|
||||
|
||||
if (existingTimer == null)
|
||||
{
|
||||
var existingTimer = _timerProvider.GetTimer(timer.Id);
|
||||
existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId)
|
||||
? null
|
||||
: _timerProvider.GetTimerByProgramId(timer.ProgramId);
|
||||
}
|
||||
|
||||
if (existingTimer == null)
|
||||
if (existingTimer == null)
|
||||
{
|
||||
if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
|
||||
{
|
||||
existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId)
|
||||
? null
|
||||
: _timerProvider.GetTimerByProgramId(timer.ProgramId);
|
||||
}
|
||||
|
||||
if (existingTimer == null)
|
||||
{
|
||||
if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
|
||||
{
|
||||
timer.Status = RecordingStatus.Cancelled;
|
||||
}
|
||||
else
|
||||
{
|
||||
enabledTimersForSeries.Add(timer);
|
||||
}
|
||||
_timerProvider.Add(timer);
|
||||
|
||||
if (TimerCreated != null)
|
||||
{
|
||||
TimerCreated(this, new GenericEventArgs<TimerInfo>(timer));
|
||||
}
|
||||
timer.Status = RecordingStatus.Cancelled;
|
||||
}
|
||||
else
|
||||
{
|
||||
enabledTimersForSeries.Add(timer);
|
||||
}
|
||||
_timerProvider.Add(timer);
|
||||
|
||||
TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
|
||||
}
|
||||
// Only update if not currently active - test both new timer and existing in case Id's are different
|
||||
// Id's could be different if the timer was created manually prior to series timer creation
|
||||
else if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _))
|
||||
{
|
||||
UpdateExistingTimerWithNewMetadata(existingTimer, timer);
|
||||
|
||||
// Needed by ShouldCancelTimerForSeriesTimer
|
||||
timer.IsManual = existingTimer.IsManual;
|
||||
|
||||
if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
|
||||
{
|
||||
existingTimer.Status = RecordingStatus.Cancelled;
|
||||
}
|
||||
else if (!existingTimer.IsManual)
|
||||
{
|
||||
existingTimer.Status = RecordingStatus.New;
|
||||
}
|
||||
|
||||
if (existingTimer.Status != RecordingStatus.Cancelled)
|
||||
{
|
||||
enabledTimersForSeries.Add(existingTimer);
|
||||
}
|
||||
|
||||
if (updateTimerSettings)
|
||||
{
|
||||
// Only update if not currently active - test both new timer and existing in case Id's are different
|
||||
// Id's could be different if the timer was created manually prior to series timer creation
|
||||
|
@ -2445,6 +2449,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
_timerProvider.Update(existingTimer);
|
||||
}
|
||||
}
|
||||
|
||||
existingTimer.SeriesTimerId = seriesTimer.Id;
|
||||
_timerProvider.Update(existingTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2770,15 +2777,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
{
|
||||
var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (configuredDevice != null)
|
||||
if (configuredDevice != null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_logger.LogInformation("Tuner url has changed from {0} to {1}", configuredDevice.Url, device.Url);
|
||||
_logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url);
|
||||
|
||||
configuredDevice.Url = device.Url;
|
||||
await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false);
|
||||
}
|
||||
configuredDevice.Url = device.Url;
|
||||
await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
private readonly ITaskManager _taskManager;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly ISecurityManager _security;
|
||||
private readonly Func<IChannelManager> _channelManager;
|
||||
|
||||
private readonly IDtoService _dtoService;
|
||||
|
@ -78,7 +77,23 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
private IServerApplicationHost _appHost;
|
||||
private IHttpClient _httpClient;
|
||||
|
||||
public LiveTvManager(IServerApplicationHost appHost, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security, Func<IChannelManager> channelManager)
|
||||
public LiveTvManager(
|
||||
IServerApplicationHost appHost,
|
||||
IHttpClient httpClient,
|
||||
IServerConfigurationManager config,
|
||||
ILogger logger,
|
||||
IItemRepository itemRepo,
|
||||
IImageProcessor imageProcessor,
|
||||
IUserDataManager userDataManager,
|
||||
IDtoService dtoService,
|
||||
IUserManager userManager,
|
||||
ILibraryManager libraryManager,
|
||||
ITaskManager taskManager,
|
||||
ILocalizationManager localization,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IProviderManager providerManager,
|
||||
IFileSystem fileSystem,
|
||||
Func<IChannelManager> channelManager)
|
||||
{
|
||||
_appHost = appHost;
|
||||
_config = config;
|
||||
|
@ -91,7 +106,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
_jsonSerializer = jsonSerializer;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
_security = security;
|
||||
_dtoService = dtoService;
|
||||
_userDataManager = userDataManager;
|
||||
_channelManager = channelManager;
|
||||
|
@ -2085,14 +2099,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
|
||||
public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
|
||||
{
|
||||
var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
|
||||
|
||||
if (!registration.IsValid)
|
||||
{
|
||||
_logger.LogInformation("Creating series recordings requires an active Emby Premiere subscription.");
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetService(timer.ServiceName);
|
||||
|
||||
var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
|
||||
|
@ -2434,30 +2440,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
}
|
||||
}
|
||||
|
||||
public Task<MBRegistrationRecord> GetRegistrationInfo(string feature)
|
||||
{
|
||||
if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
feature = "embytvseriesrecordings";
|
||||
}
|
||||
|
||||
if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var config = GetConfiguration();
|
||||
if (config.TunerHosts.Length > 0 &&
|
||||
config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
|
||||
{
|
||||
return Task.FromResult(new MBRegistrationRecord
|
||||
{
|
||||
IsRegistered = true,
|
||||
IsValid = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return _security.GetRegistrationStatus(feature);
|
||||
}
|
||||
|
||||
public Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||
|
|
|
@ -1,201 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace Emby.Server.Implementations.Security
|
||||
{
|
||||
internal class MBLicenseFile
|
||||
{
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ICryptoProvider _cryptographyProvider;
|
||||
|
||||
public string RegKey
|
||||
{
|
||||
get => _regKey;
|
||||
set
|
||||
{
|
||||
_updateRecords.Clear();
|
||||
_regKey = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string Filename => Path.Combine(_appPaths.ConfigurationDirectoryPath, "mb.lic");
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, FeatureRegInfo> _updateRecords = new ConcurrentDictionary<Guid, FeatureRegInfo>();
|
||||
private readonly object _fileLock = new object();
|
||||
private string _regKey;
|
||||
|
||||
public MBLicenseFile(IApplicationPaths appPaths, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
|
||||
{
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_cryptographyProvider = cryptographyProvider;
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
private void SetUpdateRecord(Guid key, FeatureRegInfo value)
|
||||
{
|
||||
_updateRecords.AddOrUpdate(key, value, (k, v) => value);
|
||||
}
|
||||
|
||||
private Guid GetKey(string featureId)
|
||||
{
|
||||
return new Guid(_cryptographyProvider.ComputeMD5(Encoding.Unicode.GetBytes(featureId)));
|
||||
}
|
||||
|
||||
public void AddRegCheck(string featureId, DateTime expirationDate)
|
||||
{
|
||||
var key = GetKey(featureId);
|
||||
var value = new FeatureRegInfo
|
||||
{
|
||||
ExpirationDate = expirationDate,
|
||||
LastChecked = DateTime.UtcNow
|
||||
};
|
||||
|
||||
SetUpdateRecord(key, value);
|
||||
Save();
|
||||
}
|
||||
|
||||
public void RemoveRegCheck(string featureId)
|
||||
{
|
||||
var key = GetKey(featureId);
|
||||
|
||||
_updateRecords.TryRemove(key, out var val);
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
public FeatureRegInfo GetRegInfo(string featureId)
|
||||
{
|
||||
var key = GetKey(featureId);
|
||||
FeatureRegInfo info = null;
|
||||
_updateRecords.TryGetValue(key, out info);
|
||||
|
||||
if (info == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// guard agains people just putting a large number in the file
|
||||
return info.LastChecked < DateTime.UtcNow ? info : null;
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
string[] contents = null;
|
||||
var licenseFile = Filename;
|
||||
lock (_fileLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
contents = _fileSystem.ReadAllLines(licenseFile);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
lock (_fileLock)
|
||||
{
|
||||
_fileSystem.WriteAllBytes(licenseFile, Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
lock (_fileLock)
|
||||
{
|
||||
_fileSystem.WriteAllBytes(licenseFile, Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contents != null && contents.Length > 0)
|
||||
{
|
||||
//first line is reg key
|
||||
RegKey = contents[0];
|
||||
|
||||
//next is legacy key
|
||||
if (contents.Length > 1)
|
||||
{
|
||||
// Don't need this anymore
|
||||
}
|
||||
|
||||
//the rest of the lines should be pairs of features and timestamps
|
||||
for (var i = 2; i < contents.Length; i = i + 2)
|
||||
{
|
||||
var line = contents[i];
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Guid.TryParse(line, out var feat))
|
||||
{
|
||||
var lineParts = contents[i + 1].Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (long.TryParse(lineParts[0], out var ticks))
|
||||
{
|
||||
var info = new FeatureRegInfo
|
||||
{
|
||||
LastChecked = new DateTime(ticks)
|
||||
};
|
||||
|
||||
if (lineParts.Length > 1 && long.TryParse(lineParts[1], out ticks))
|
||||
{
|
||||
info.ExpirationDate = new DateTime(ticks);
|
||||
}
|
||||
|
||||
SetUpdateRecord(feat, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
//build our array
|
||||
var lines = new List<string>
|
||||
{
|
||||
RegKey,
|
||||
|
||||
// Legacy key
|
||||
string.Empty
|
||||
};
|
||||
|
||||
foreach (var pair in _updateRecords
|
||||
.ToList())
|
||||
{
|
||||
lines.Add(pair.Key.ToString());
|
||||
|
||||
var dateLine = pair.Value.LastChecked.Ticks.ToString(CultureInfo.InvariantCulture) + "|" +
|
||||
pair.Value.ExpirationDate.Ticks.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
lines.Add(dateLine);
|
||||
}
|
||||
|
||||
var licenseFile = Filename;
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(licenseFile));
|
||||
lock (_fileLock)
|
||||
{
|
||||
_fileSystem.WriteAllLines(licenseFile, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class FeatureRegInfo
|
||||
{
|
||||
public DateTime ExpirationDate { get; set; }
|
||||
public DateTime LastChecked { get; set; }
|
||||
|
||||
public FeatureRegInfo()
|
||||
{
|
||||
ExpirationDate = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Class PluginSecurityManager
|
||||
/// </summary>
|
||||
public class PluginSecurityManager : ISecurityManager
|
||||
{
|
||||
private const string MBValidateUrl = "https://mb3admin.local/admin/service/registration/validate";
|
||||
private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "https://mb3admin.local/admin/service/appstore/register";
|
||||
|
||||
public async Task<bool> IsSupporter()
|
||||
{
|
||||
var result = await GetRegistrationStatusInternal("MBSupporter", false, _appHost.ApplicationVersion.ToString(), CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
return result.IsRegistered;
|
||||
}
|
||||
|
||||
private MBLicenseFile _licenseFile;
|
||||
private MBLicenseFile LicenseFile => _licenseFile ?? (_licenseFile = new MBLicenseFile(_appPaths, _fileSystem, _cryptographyProvider));
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ICryptoProvider _cryptographyProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginSecurityManager" /> class.
|
||||
/// </summary>
|
||||
public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer,
|
||||
IApplicationPaths appPaths, ILoggerFactory loggerFactory, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
|
||||
{
|
||||
if (httpClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpClient));
|
||||
}
|
||||
|
||||
_appHost = appHost;
|
||||
_httpClient = httpClient;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_cryptographyProvider = cryptographyProvider;
|
||||
_logger = loggerFactory.CreateLogger("SecurityManager");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registration status.
|
||||
/// This overload supports existing plug-ins.
|
||||
/// </summary>
|
||||
public Task<MBRegistrationRecord> GetRegistrationStatus(string feature)
|
||||
{
|
||||
return GetRegistrationStatusInternal(feature, false, null, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the supporter key.
|
||||
/// </summary>
|
||||
/// <value>The supporter key.</value>
|
||||
public string SupporterKey
|
||||
{
|
||||
get => LicenseFile.RegKey;
|
||||
set => throw new Exception("Please call UpdateSupporterKey");
|
||||
}
|
||||
|
||||
public async Task UpdateSupporterKey(string newValue)
|
||||
{
|
||||
if (newValue != null)
|
||||
{
|
||||
newValue = newValue.Trim();
|
||||
}
|
||||
|
||||
if (!string.Equals(newValue, LicenseFile.RegKey, StringComparison.Ordinal))
|
||||
{
|
||||
LicenseFile.RegKey = newValue;
|
||||
LicenseFile.Save();
|
||||
|
||||
// Reset this
|
||||
await GetRegistrationStatusInternal("MBSupporter", true, _appHost.ApplicationVersion.ToString(), CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register an app store sale with our back-end. It will validate the transaction with the store
|
||||
/// and then register the proper feature and then fill in the supporter key on success.
|
||||
/// </summary>
|
||||
/// <param name="parameters">Json parameters to send to admin server</param>
|
||||
public async Task RegisterAppStoreSale(string parameters)
|
||||
{
|
||||
var options = new HttpRequestOptions()
|
||||
{
|
||||
Url = AppstoreRegUrl,
|
||||
CancellationToken = CancellationToken.None,
|
||||
BufferContent = false
|
||||
};
|
||||
options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
|
||||
options.RequestContent = parameters;
|
||||
options.RequestContentType = "application/json";
|
||||
|
||||
try
|
||||
{
|
||||
using (var response = await _httpClient.Post(options).ConfigureAwait(false))
|
||||
{
|
||||
var reg = await _jsonSerializer.DeserializeFromStreamAsync<RegRecord>(response.Content).ConfigureAwait(false);
|
||||
|
||||
if (reg == null)
|
||||
{
|
||||
var msg = "Result from appstore registration was null.";
|
||||
_logger.LogError(msg);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(reg.key))
|
||||
{
|
||||
await UpdateSupporterKey(reg.key).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
SaveAppStoreInfo(parameters);
|
||||
throw;
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error registering appstore purchase {parameters}", parameters ?? "NO PARMS SENT");
|
||||
|
||||
throw new Exception("Error registering store sale");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error registering appstore purchase {parameters}", parameters ?? "NO PARMS SENT");
|
||||
SaveAppStoreInfo(parameters);
|
||||
//TODO - could create a re-try routine on start-up if this file is there. For now we can handle manually.
|
||||
throw new Exception("Error registering store sale");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SaveAppStoreInfo(string info)
|
||||
{
|
||||
// Save all transaction information to a file
|
||||
|
||||
try
|
||||
{
|
||||
_fileSystem.WriteAllText(Path.Combine(_appPaths.ProgramDataPath, "apptrans-error.txt"), info);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private SemaphoreSlim _regCheckLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
private async Task<MBRegistrationRecord> GetRegistrationStatusInternal(string feature, bool forceCallToServer, string version, CancellationToken cancellationToken)
|
||||
{
|
||||
await _regCheckLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var record = new MBRegistrationRecord
|
||||
{
|
||||
IsRegistered = true,
|
||||
RegChecked = true,
|
||||
TrialVersion = false,
|
||||
IsValid = true,
|
||||
RegError = false
|
||||
};
|
||||
|
||||
return record;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_regCheckLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInTrial(DateTime expirationDate, bool regChecked, bool isRegistered)
|
||||
{
|
||||
//don't set this until we've successfully obtained exp date
|
||||
if (!regChecked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var isInTrial = expirationDate > DateTime.UtcNow;
|
||||
|
||||
return isInTrial && !isRegistered;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Emby.Server.Implementations.Security
|
||||
{
|
||||
class RegRecord
|
||||
{
|
||||
public string featId { get; set; }
|
||||
public bool registered { get; set; }
|
||||
public DateTime expDate { get; set; }
|
||||
public string key { get; set; }
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ using MediaBrowser.Common.Configuration;
|
|||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
|
@ -107,7 +106,6 @@ namespace Emby.Server.Implementations.Updates
|
|||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly ISecurityManager _securityManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
|
@ -122,7 +120,16 @@ namespace Emby.Server.Implementations.Updates
|
|||
// netframework or netcore
|
||||
private readonly string _packageRuntime;
|
||||
|
||||
public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IServerConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, string packageRuntime)
|
||||
public InstallationManager(
|
||||
ILogger logger,
|
||||
IApplicationHost appHost,
|
||||
IApplicationPaths appPaths,
|
||||
IHttpClient httpClient,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IServerConfigurationManager config,
|
||||
IFileSystem fileSystem,
|
||||
ICryptoProvider cryptographyProvider,
|
||||
string packageRuntime)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
|
@ -136,7 +143,6 @@ namespace Emby.Server.Implementations.Updates
|
|||
_appPaths = appPaths;
|
||||
_httpClient = httpClient;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_securityManager = securityManager;
|
||||
_config = config;
|
||||
_fileSystem = fileSystem;
|
||||
_cryptographyProvider = cryptographyProvider;
|
||||
|
@ -163,41 +169,10 @@ namespace Emby.Server.Implementations.Updates
|
|||
string packageType = null,
|
||||
Version applicationVersion = null)
|
||||
{
|
||||
if (withRegistration)
|
||||
{
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ "key", _securityManager.SupporterKey },
|
||||
{ "mac", _applicationHost.SystemId },
|
||||
{ "systemid", _applicationHost.SystemId }
|
||||
};
|
||||
// TODO cvium: when plugins get back this would need to be fixed
|
||||
// var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var options = new HttpRequestOptions
|
||||
{
|
||||
Url = "https://www.mb3admin.local/admin/service/package/retrieveall?includeAllRuntimes=true",
|
||||
CancellationToken = cancellationToken
|
||||
};
|
||||
|
||||
options.SetPostData(data);
|
||||
|
||||
using (var response = await _httpClient.SendAsync(options, "POST").ConfigureAwait(false))
|
||||
{
|
||||
using (var json = response.Content)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var packages = await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(json).ConfigureAwait(false);
|
||||
|
||||
return FilterPackages(packages, packageType, applicationVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return FilterPackages(packages, packageType, applicationVersion);
|
||||
}
|
||||
return new List<PackageInfo>(); //FilterPackages(packages, packageType, applicationVersion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -10,7 +10,6 @@ using MediaBrowser.Common.Security;
|
|||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Controller.Devices;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Services;
|
||||
|
@ -79,6 +78,16 @@ namespace MediaBrowser.Api
|
|||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
//TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins,
|
||||
// delete all these registration endpoints. They are only kept for compatibility.
|
||||
[Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
|
||||
[Authenticated]
|
||||
public class GetRegistration : IReturn<RegistrationInfo>
|
||||
{
|
||||
[ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPluginSecurityInfo
|
||||
/// </summary>
|
||||
|
@ -105,14 +114,7 @@ namespace MediaBrowser.Api
|
|||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
|
||||
[Authenticated]
|
||||
public class GetRegistration : IReturn<RegistrationInfo>
|
||||
{
|
||||
[ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
// TODO these two classes are only kept for compability with paid plugins and should be removed
|
||||
public class RegistrationInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
@ -121,14 +123,21 @@ namespace MediaBrowser.Api
|
|||
public bool IsRegistered { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Appstore/Register", "POST", Summary = "Registers an appstore sale", IsHidden = true)]
|
||||
[Authenticated]
|
||||
public class RegisterAppstoreSale
|
||||
public class MBRegistrationRecord
|
||||
{
|
||||
[ApiMember(Name = "Parameters", Description = "Java representation of parameters to pass through to admin server", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string Parameters { get; set; }
|
||||
public DateTime ExpirationDate { get; set; }
|
||||
public bool IsRegistered { get; set; }
|
||||
public bool RegChecked { get; set; }
|
||||
public bool RegError { get; set; }
|
||||
public bool TrialVersion { get; set; }
|
||||
public bool IsValid { get; set; }
|
||||
}
|
||||
|
||||
public class PluginSecurityInfo
|
||||
{
|
||||
public string SupporterKey { get; set; }
|
||||
public bool IsMBSupporter { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Class PluginsService
|
||||
/// </summary>
|
||||
|
@ -143,14 +152,11 @@ namespace MediaBrowser.Api
|
|||
/// The _app host
|
||||
/// </summary>
|
||||
private readonly IApplicationHost _appHost;
|
||||
|
||||
private readonly ISecurityManager _securityManager;
|
||||
|
||||
private readonly IInstallationManager _installationManager;
|
||||
private readonly INetworkManager _network;
|
||||
private readonly IDeviceManager _deviceManager;
|
||||
|
||||
public PluginService(IJsonSerializer jsonSerializer, IApplicationHost appHost, ISecurityManager securityManager, IInstallationManager installationManager, INetworkManager network, IDeviceManager deviceManager)
|
||||
public PluginService(IJsonSerializer jsonSerializer, IApplicationHost appHost, IInstallationManager installationManager, INetworkManager network, IDeviceManager deviceManager)
|
||||
: base()
|
||||
{
|
||||
if (jsonSerializer == null)
|
||||
|
@ -159,7 +165,6 @@ namespace MediaBrowser.Api
|
|||
}
|
||||
|
||||
_appHost = appHost;
|
||||
_securityManager = securityManager;
|
||||
_installationManager = installationManager;
|
||||
_network = network;
|
||||
_deviceManager = deviceManager;
|
||||
|
@ -173,20 +178,26 @@ namespace MediaBrowser.Api
|
|||
/// <returns>System.Object.</returns>
|
||||
public async Task<object> Get(GetRegistrationStatus request)
|
||||
{
|
||||
var result = await _securityManager.GetRegistrationStatus(request.Name).ConfigureAwait(false);
|
||||
var record = new MBRegistrationRecord
|
||||
{
|
||||
IsRegistered = true,
|
||||
RegChecked = true,
|
||||
TrialVersion = false,
|
||||
IsValid = true,
|
||||
RegError = false
|
||||
};
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
return ToOptimizedResult(record);
|
||||
}
|
||||
|
||||
//TODO this function is only kept for compatibility and should be removed once paid plugins break
|
||||
public async Task<object> Get(GetRegistration request)
|
||||
{
|
||||
var result = await _securityManager.GetRegistrationStatus(request.Name).ConfigureAwait(false);
|
||||
|
||||
var info = new RegistrationInfo
|
||||
{
|
||||
ExpirationDate = result.ExpirationDate,
|
||||
IsRegistered = result.IsRegistered,
|
||||
IsTrial = result.TrialVersion,
|
||||
ExpirationDate = DateTime.Now.AddYears(100),
|
||||
IsRegistered = true,
|
||||
IsTrial = false,
|
||||
Name = request.Name
|
||||
};
|
||||
|
||||
|
@ -201,45 +212,6 @@ namespace MediaBrowser.Api
|
|||
public async Task<object> Get(GetPlugins request)
|
||||
{
|
||||
var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToArray();
|
||||
var requireAppStoreEnabled = request.IsAppStoreEnabled.HasValue && request.IsAppStoreEnabled.Value;
|
||||
|
||||
// Don't fail just on account of image url's
|
||||
try
|
||||
{
|
||||
var packages = (await _installationManager.GetAvailablePackagesWithoutRegistrationInfo(CancellationToken.None));
|
||||
|
||||
foreach (var plugin in result)
|
||||
{
|
||||
var pkg = packages.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i.guid) && string.Equals(i.guid.Replace("-", string.Empty), plugin.Id.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (pkg != null)
|
||||
{
|
||||
plugin.ImageUrl = pkg.thumbImage;
|
||||
}
|
||||
}
|
||||
|
||||
if (requireAppStoreEnabled)
|
||||
{
|
||||
result = result
|
||||
.Where(plugin =>
|
||||
{
|
||||
var pkg = packages.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i.guid) && new Guid(plugin.Id).Equals(new Guid(i.guid)));
|
||||
return pkg != null && pkg.enableInAppStore;
|
||||
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error getting plugin list");
|
||||
// Play it safe here
|
||||
if (requireAppStoreEnabled)
|
||||
{
|
||||
result = new PluginInfo[] { };
|
||||
}
|
||||
}
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
|
@ -265,30 +237,20 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
var result = new PluginSecurityInfo
|
||||
{
|
||||
IsMBSupporter = await _securityManager.IsSupporter().ConfigureAwait(false),
|
||||
SupporterKey = _securityManager.SupporterKey
|
||||
IsMBSupporter = true,
|
||||
SupporterKey = "IAmTotallyLegit"
|
||||
};
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post app store sale
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public Task Post(RegisterAppstoreSale request)
|
||||
{
|
||||
return _securityManager.RegisterAppStoreSale(request.Parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public Task Post(UpdatePluginSecurityInfo request)
|
||||
{
|
||||
return _securityManager.UpdateSupporterKey(request.SupporterKey);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -100,8 +100,6 @@ namespace MediaBrowser.Api.System
|
|||
|
||||
private readonly INetworkManager _network;
|
||||
|
||||
private readonly ISecurityManager _security;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemService" /> class.
|
||||
/// </summary>
|
||||
|
@ -109,13 +107,12 @@ namespace MediaBrowser.Api.System
|
|||
/// <param name="appPaths">The application paths.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <exception cref="ArgumentNullException">jsonSerializer</exception>
|
||||
public SystemService(IServerApplicationHost appHost, IApplicationPaths appPaths, IFileSystem fileSystem, INetworkManager network, ISecurityManager security)
|
||||
public SystemService(IServerApplicationHost appHost, IApplicationPaths appPaths, IFileSystem fileSystem, INetworkManager network)
|
||||
{
|
||||
_appHost = appHost;
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_network = network;
|
||||
_security = security;
|
||||
}
|
||||
|
||||
public object Post(PingSystem request)
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Security
|
||||
{
|
||||
public interface IRequiresRegistration
|
||||
{
|
||||
/// <summary>
|
||||
/// Load all registration information required for this entity.
|
||||
/// Your class should re-load all MBRegistrationRecords when this is called even if they were
|
||||
/// previously loaded.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task LoadRegistrationInfoAsync();
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Common.Security
|
||||
{
|
||||
public interface ISecurityManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is MB supporter.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value>
|
||||
Task<bool> IsSupporter();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the supporter key.
|
||||
/// </summary>
|
||||
/// <value>The supporter key.</value>
|
||||
string SupporterKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registration status. Overload to support existing plug-ins.
|
||||
/// </summary>
|
||||
Task<MBRegistrationRecord> GetRegistrationStatus(string feature);
|
||||
|
||||
/// <summary>
|
||||
/// Register and app store sale with our back-end
|
||||
/// </summary>
|
||||
/// <param name="parameters">Json parameters to pass to admin server</param>
|
||||
Task RegisterAppStoreSale(string parameters);
|
||||
Task UpdateSupporterKey(string newValue);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Security
|
||||
{
|
||||
public class PaymentRequiredException : Exception
|
||||
{
|
||||
}
|
||||
}
|
|
@ -49,9 +49,7 @@ namespace MediaBrowser.Common.Updates
|
|||
/// <param name="applicationVersion">The application version.</param>
|
||||
/// <returns>Task{List{PackageInfo}}.</returns>
|
||||
Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
|
||||
bool withRegistration = true,
|
||||
string packageType = null,
|
||||
Version applicationVersion = null);
|
||||
bool withRegistration = true, string packageType = null, Version applicationVersion = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available packages from a static resource.
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Entities
|
||||
{
|
||||
public class MBRegistrationRecord
|
||||
{
|
||||
public DateTime ExpirationDate { get; set; }
|
||||
public bool IsRegistered { get; set; }
|
||||
public bool RegChecked { get; set; }
|
||||
public bool RegError { get; set; }
|
||||
public bool TrialVersion { get; set; }
|
||||
public bool IsValid { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
|
||||
namespace MediaBrowser.Model.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Class PluginSecurityInfo
|
||||
/// </summary>
|
||||
public class PluginSecurityInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the supporter key.
|
||||
/// </summary>
|
||||
/// <value>The supporter key.</value>
|
||||
public string SupporterKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is MB supporter.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value>
|
||||
public bool IsMBSupporter { get; set; }
|
||||
}
|
||||
}
|
24
build-deb.sh
24
build-deb.sh
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Build a Jellyfin .deb file with Docker on Linux
|
||||
# Places the output .deb file in the parent directory
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
set -o nounset
|
||||
|
||||
package_temporary_dir="`mktemp -d`"
|
||||
current_user="`whoami`"
|
||||
image_name="jellyfin-debuild"
|
||||
|
||||
cleanup() {
|
||||
set +o errexit
|
||||
docker image rm $image_name --force
|
||||
rm -rf "$package_temporary_dir"
|
||||
}
|
||||
trap cleanup EXIT INT
|
||||
|
||||
docker build . -t "$image_name" -f ./Dockerfile.debian_package
|
||||
docker run --rm -v "$package_temporary_dir:/temp" "$image_name" cp -r /dist /temp/
|
||||
sudo chown -R "$current_user" "$package_temporary_dir"
|
||||
mv "$package_temporary_dir"/dist/*.deb ../
|
1
debian/source/options
vendored
1
debian/source/options
vendored
|
@ -1 +0,0 @@
|
|||
tar-ignore = ".git*"
|
8
deployment/README.md
Normal file
8
deployment/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Build scripts
|
||||
|
||||
All `build.sh` and `package.sh` scripts are for *nix platforms (or WSL on Windows 10).
|
||||
|
||||
After running both, check the `*/pkg-dist/` folders for the archives and packages.
|
||||
|
||||
`build_all.sh` will invoke every build and package script.
|
||||
Use `collect_all.sh` to copy all artifact to one directory for easy uploading.
|
21
deployment/clean.sh
Executable file
21
deployment/clean.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Execute every clean.sh scripts in every folder.
|
||||
echo "Running for platforms '$@'."
|
||||
for directory in */ ; do
|
||||
platform=`basename "${directory}"`
|
||||
if [[ $@ == *"$platform"* || $@ = *"all"* ]]; then
|
||||
echo "Processing ${platform}"
|
||||
pushd "$platform"
|
||||
if [ -f clean.sh ]; then
|
||||
echo ./clean.sh
|
||||
fi
|
||||
popd
|
||||
else
|
||||
echo "Skipping $platform."
|
||||
fi
|
||||
done
|
||||
|
||||
rm -rf ./collect-dist
|
20
deployment/collect_all.sh
Executable file
20
deployment/collect_all.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source common.build.sh
|
||||
|
||||
VERSION=`get_version ..`
|
||||
|
||||
COLLECT_DIR="./collect-dist"
|
||||
|
||||
mkdir -p ./collect-dist
|
||||
|
||||
DIRS=`find . -type d -name "pkg-dist"`
|
||||
|
||||
while read directory
|
||||
do
|
||||
echo "Collecting everything from '$directory'.."
|
||||
PLATFORM=$(basename "$(dirname "$directory")")
|
||||
# Copy all artifacts with extensions tar.gz, deb, exe, zip, rpm and add the platform name to resolve any duplicates.
|
||||
find $directory \( -name "jellyfin*.tar.gz" -o -name "jellyfin*.deb" -o -name "jellyfin*.rpm" -o -name "jellyfin*.zip" -o -name "jellyfin*.exe" \) -exec sh -c 'cp "$1" "'${COLLECT_DIR}'/jellyfin_'${PLATFORM}'_${1#*jellyfin}"' _ {} \;
|
||||
|
||||
done <<< "${DIRS}"
|
108
deployment/common.build.sh
Executable file
108
deployment/common.build.sh
Executable file
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
DEFAULT_BUILD_CONTEXT="../.."
|
||||
DEFAULT_ROOT="."
|
||||
DEFAULT_DOTNETRUNTIME="framework"
|
||||
DEFAULT_CONFIG="Release"
|
||||
DEFAULT_OUTPUT_DIR="dist/jellyfin-git"
|
||||
DEFAULT_PKG_DIR="pkg-dist"
|
||||
DEFAULT_DOCKERFILE="Dockerfile"
|
||||
DEFAULT_IMAGE_TAG="jellyfin:"`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
# Run a build
|
||||
build_jellyfin()
|
||||
(
|
||||
ROOT=${1-$DEFAULT_ROOT}
|
||||
CONFIG=${2-$DEFAULT_CONFIG}
|
||||
DOTNETRUNTIME=${3-$DEFAULT_DOTNETRUNTIME}
|
||||
OUTPUT_DIR=${4-$DEFAULT_OUTPUT_DIR}
|
||||
|
||||
echo -e "${CYAN}Building jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}'.${NC}"
|
||||
if [[ $DOTNETRUNTIME == 'framework' ]]; then
|
||||
dotnet publish "${ROOT}" --configuration "${CONFIG}" --output="${OUTPUT_DIR}"
|
||||
else
|
||||
dotnet publish "${ROOT}" --configuration "${CONFIG}" --output="${OUTPUT_DIR}" --self-contained --runtime ${DOTNETRUNTIME}
|
||||
fi
|
||||
EXIT_CODE=$?
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}[DONE] Build jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' complete.${NC}"
|
||||
else
|
||||
echo -e "${RED}[FAIL] Build jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' FAILED.${NC}"
|
||||
fi
|
||||
)
|
||||
|
||||
# Run a docker
|
||||
build_jellyfin_docker()
|
||||
(
|
||||
BUILD_CONTEXT=${1-$DEFAULT_BUILD_CONTEXT}
|
||||
DOCKERFILE=${2-$DEFAULT_DOCKERFILE}
|
||||
IMAGE_TAG=${3-$DEFAULT_IMAGE_TAG}
|
||||
|
||||
echo -e "${CYAN}Building jellyfin docker image in '${BUILD_CONTEXT}' with Dockerfile '${DOCKERFILE}' and tag '${IMAGE_TAG}'.${NC}"
|
||||
docker build -t ${IMAGE_TAG} -f ${DOCKERFILE} ${BUILD_CONTEXT}
|
||||
EXIT_CODE=$?
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}[DONE] Building jellyfin docker image in '${BUILD_CONTEXT}' with Dockerfile '${DOCKERFILE}' and tag '${IMAGE_TAG}' complete.${NC}"
|
||||
else
|
||||
echo -e "${RED}[FAIL] Building jellyfin docker image in '${BUILD_CONTEXT}' with Dockerfile '${DOCKERFILE}' and tag '${IMAGE_TAG}' FAILED.${NC}"
|
||||
fi
|
||||
)
|
||||
|
||||
# Clean a build
|
||||
clean_jellyfin()
|
||||
(
|
||||
local ROOT=${1-$DEFAULT_ROOT}
|
||||
local CONFIG=${2-$DEFAULT_CONFIG}
|
||||
local OUTPUT_DIR=${3-$DEFAULT_OUTPUT_DIR}
|
||||
local PKG_DIR=${4-$DEFAULT_PKG_DIR}
|
||||
echo -e "${CYAN}Cleaning jellyfin in '${ROOT}'' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}'.${NC}"
|
||||
echo -e "${CYAN}Deleting '${OUTPUT_DIR}'${NC}"
|
||||
rm -rf "$OUTPUT_DIR"
|
||||
echo -e "${CYAN}Deleting '${PKG_DIR}'${NC}"
|
||||
rm -rf "$PKG_DIR"
|
||||
dotnet clean "${ROOT}" -maxcpucount:1 --configuration ${CONFIG}
|
||||
local EXIT_CODE=$?
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}[DONE] Clean jellyfin in '${ROOT}' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' complete.${NC}"
|
||||
else
|
||||
echo -e "${RED}[FAIL] Clean jellyfin in '${ROOT}' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' failed.${NC}"
|
||||
fi
|
||||
)
|
||||
|
||||
# Parse the version from the AssemblyVersion
|
||||
get_version()
|
||||
(
|
||||
local ROOT=${1-$DEFAULT_ROOT}
|
||||
grep "AssemblyVersion" ${ROOT}/SharedVersion.cs | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/' | sed -E 's/.0$//'
|
||||
)
|
||||
|
||||
# Packages the output folder into an archive.
|
||||
package_portable()
|
||||
(
|
||||
local ROOT=${1-$DEFAULT_ROOT}
|
||||
local OUTPUT_DIR=${2-$DEFAULT_OUTPUT_DIR}
|
||||
local PKG_DIR=${3-$DEFAULT_PKG_DIR}
|
||||
# Package portable build result
|
||||
if [ -d ${OUTPUT_DIR} ]; then
|
||||
echo -e "${CYAN}Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}'.${NC}"
|
||||
mkdir -p ${PKG_DIR}
|
||||
tar -zcvf "${PKG_DIR}/`basename "${OUTPUT_DIR}"`.portable.tar.gz" -C "`dirname "${OUTPUT_DIR}"`" "`basename "${OUTPUT_DIR}"`"
|
||||
local EXIT_CODE=$?
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}[DONE] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' complete.${NC}"
|
||||
else
|
||||
echo -e "${RED}[FAIL] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' FAILED.${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}[FAIL] Build artifacts do not exist for ${OUTPUT_DIR}. Run build.sh first.${NC}"
|
||||
fi
|
||||
)
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
FROM debian:9
|
||||
ARG SOURCEDIR=/repo
|
||||
ENV DEB_BUILD_OPTIONS=noddebs
|
||||
|
||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
||||
RUN apt-get update \
|
||||
|
@ -11,12 +13,11 @@ RUN apt-get update \
|
|||
&& chown root:root /etc/apt/sources.list.d/microsoft-prod.list \
|
||||
&& apt-get update
|
||||
|
||||
WORKDIR /repo
|
||||
WORKDIR ${SOURCEDIR}
|
||||
COPY . .
|
||||
COPY ./deployment/debian-package-x64/pkg-src ./debian
|
||||
|
||||
RUN yes|mk-build-deps -i \
|
||||
&& dpkg-buildpackage -us -uc \
|
||||
&& mkdir /dist \
|
||||
&& mv /jellyfin*deb /dist
|
||||
RUN yes | mk-build-deps -i debian/control \
|
||||
&& dpkg-buildpackage -us -uc
|
||||
|
||||
WORKDIR /dist
|
||||
WORKDIR /
|
7
deployment/debian-package-x64/clean.sh
Executable file
7
deployment/debian-package-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
31
deployment/debian-package-x64/package.sh
Executable file
31
deployment/debian-package-x64/package.sh
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
# TODO get the version in the package automatically. And using the changelog to decide the debian package suffix version.
|
||||
|
||||
# Build a Jellyfin .deb file with Docker on Linux
|
||||
# Places the output .deb file in the parent directory
|
||||
|
||||
package_temporary_dir="`pwd`/pkg-dist-tmp"
|
||||
output_dir="`pwd`/pkg-dist"
|
||||
current_user="`whoami`"
|
||||
image_name="jellyfin-debuild"
|
||||
|
||||
cleanup() {
|
||||
set +o errexit
|
||||
docker image rm $image_name --force
|
||||
rm -rf "$package_temporary_dir"
|
||||
}
|
||||
trap cleanup EXIT INT
|
||||
|
||||
docker build ../.. -t "$image_name" -f ./Dockerfile --build-arg SOURCEDIR="/jellyfin-${VERSION}"
|
||||
mkdir -p "$package_temporary_dir"
|
||||
mkdir -p "$output_dir"
|
||||
docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find / -maxdepth 1 -type f -name "jellyfin*" -exec mv {} /temp \;'
|
||||
chown -R "$current_user" "$package_temporary_dir" \
|
||||
|| sudo chown -R "$current_user" "$package_temporary_dir"
|
||||
|
||||
mv "$package_temporary_dir"/* "$output_dir"
|
|
@ -3,4 +3,4 @@ pristine-tar = False
|
|||
cleaner = fakeroot debian/rules clean
|
||||
|
||||
[import-orig]
|
||||
filter = [ ".git*", ".hg*" ]
|
||||
filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ]
|
6
debian/rules → deployment/debian-package-x64/pkg-src/rules
Executable file → Normal file
6
debian/rules → deployment/debian-package-x64/pkg-src/rules
Executable file → Normal file
|
@ -2,7 +2,7 @@
|
|||
CONFIG := Release
|
||||
TERM := xterm
|
||||
SHELL := /bin/bash
|
||||
DOTNETRUNTIME := linux-x64
|
||||
DOTNETRUNTIME := debian-x64
|
||||
export DH_VERBOSE=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
|
||||
|
@ -16,8 +16,8 @@ override_dh_auto_test:
|
|||
override_dh_clistrip:
|
||||
|
||||
override_dh_auto_build:
|
||||
dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME)
|
||||
dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) Jellyfin.Server
|
||||
|
||||
override_dh_auto_clean:
|
||||
dotnet clean -maxcpucount:1 --configuration $(CONFIG) || true
|
||||
dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true
|
||||
rm -rf '$(CURDIR)/usr'
|
11
deployment/debian-package-x64/pkg-src/source/options
Normal file
11
deployment/debian-package-x64/pkg-src/source/options
Normal file
|
@ -0,0 +1,11 @@
|
|||
tar-ignore='.git*'
|
||||
tar-ignore='**/.git'
|
||||
tar-ignore='**/.hg'
|
||||
tar-ignore='**/.vs'
|
||||
tar-ignore='**/.vscode'
|
||||
tar-ignore='deployment'
|
||||
tar-ignore='**/bin'
|
||||
tar-ignore='**/obj'
|
||||
tar-ignore='**/.nuget'
|
||||
tar-ignore='*.deb'
|
||||
tar-ignore='ThirdParty'
|
7
deployment/debian-x64/build.sh
Executable file
7
deployment/debian-x64/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin ../../Jellyfin.Server Release debian-x64 `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/debian-x64/clean.sh
Executable file
7
deployment/debian-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/debian-x64/package.sh
Executable file
7
deployment/debian-x64/package.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
12
deployment/docker/build.sh
Executable file
12
deployment/docker/build.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin_docker ../.. ../../Dockerfile jellyfin:amd64-${VERSION}
|
||||
|
||||
build_jellyfin_docker ../.. ../../Dockerfile.arm jellyfin:arm-${VERSION}
|
||||
|
||||
#build_jellyfin_docker ../.. ../../Dockerfile.arm64v8 jellyfin:arm64v8-${VERSION}
|
||||
#build_jellyfin_docker ../.. ../../Dockerfile.arm32v7 jellyfin:arm32v7-${VERSION}
|
12
deployment/docker/package.sh
Executable file
12
deployment/docker/package.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
docker manifest create jellyfin:${VERSION} jellyfin:amd64-${VERSION} jellyfin:arm32v7-${VERSION} jellyfin:arm64v8-${VERSION}
|
||||
docker manifest annotate jellyfin:amd64-${VERSION} --os linux --arch amd64
|
||||
#docker manifest annotate jellyfin:arm32v7-${VERSION} --os linux --arch arm --variant armv7
|
||||
#docker manifest annotate jellyfin:arm64v8-${VERSION} --os linux --arch arm64 --variant armv8
|
||||
|
||||
#TODO publish.sh - docker manifest push jellyfin:${VERSION}
|
15
deployment/fedora-package-x64/Dockerfile
Normal file
15
deployment/fedora-package-x64/Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
|||
FROM fedora:29
|
||||
ARG HOME=/build
|
||||
RUN mkdir /build && \
|
||||
dnf install -y @buildsys-build rpmdevtools dnf-plugins-core && \
|
||||
dnf copr enable -y @dotnet-sig/dotnet && \
|
||||
rpmdev-setuptree
|
||||
|
||||
WORKDIR /build/rpmbuild
|
||||
COPY ./deployment/fedora-package-x64/pkg-src/jellyfin.spec SPECS
|
||||
COPY ./deployment/fedora-package-x64/pkg-src/ SOURCES
|
||||
|
||||
RUN spectool -g -R SPECS/jellyfin.spec && \
|
||||
rpmbuild -bs SPECS/jellyfin.spec && \
|
||||
dnf build-dep -y SRPMS/jellyfin-*.src.rpm && \
|
||||
rpmbuild -bb SPECS/jellyfin.spec;
|
7
deployment/fedora-package-x64/clean.sh
Executable file
7
deployment/fedora-package-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
83
deployment/fedora-package-x64/package.sh
Executable file
83
deployment/fedora-package-x64/package.sh
Executable file
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
# TODO get the version in the package automatically. And using the changelog to decide the debian package suffix version.
|
||||
|
||||
# Build a Jellyfin .rpm file with Docker on Linux
|
||||
# Places the output .rpm file in the parent directory
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
set -o nounset
|
||||
|
||||
package_temporary_dir="`pwd`/pkg-dist-tmp"
|
||||
output_dir="`pwd`/pkg-dist"
|
||||
pkg_src_dir="`pwd`/pkg-src"
|
||||
current_user="`whoami`"
|
||||
image_name="jellyfin-rpmbuild"
|
||||
|
||||
cleanup() {
|
||||
set +o errexit
|
||||
docker image rm $image_name --force
|
||||
rm -rf "$package_temporary_dir"
|
||||
rm -rf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz"
|
||||
}
|
||||
trap cleanup EXIT INT
|
||||
GNU_TAR=1
|
||||
mkdir -p "$package_temporary_dir"
|
||||
echo "Bundling all sources for RPM build."
|
||||
tar \
|
||||
--transform "s,^\.,jellyfin-${VERSION}" \
|
||||
--exclude='.git*' \
|
||||
--exclude='**/.git' \
|
||||
--exclude='**/.hg' \
|
||||
--exclude='**/.vs' \
|
||||
--exclude='**/.vscode' \
|
||||
--exclude='deployment' \
|
||||
--exclude='**/bin' \
|
||||
--exclude='**/obj' \
|
||||
--exclude='**/.nuget' \
|
||||
--exclude='*.deb' \
|
||||
--exclude='*.rpm' \
|
||||
-Jcvf \
|
||||
"$package_temporary_dir/jellyfin-${VERSION}.tar.xz" \
|
||||
-C "../.." \
|
||||
./ || true && GNU_TAR=0
|
||||
|
||||
if [ $GNU_TAR -eq 0 ]; then
|
||||
echo "The installed tar binary did not support --transform. Using workaround."
|
||||
mkdir -p "$package_temporary_dir/jellyfin-${VERSION}"
|
||||
# Not GNU tar
|
||||
tar \
|
||||
--exclude='.git*' \
|
||||
--exclude='**/.git' \
|
||||
--exclude='**/.hg' \
|
||||
--exclude='**/.vs' \
|
||||
--exclude='**/.vscode' \
|
||||
--exclude='deployment' \
|
||||
--exclude='**/bin' \
|
||||
--exclude='**/obj' \
|
||||
--exclude='**/.nuget' \
|
||||
--exclude='*.deb' \
|
||||
--exclude='*.rpm' \
|
||||
-zcf \
|
||||
"$package_temporary_dir/jellyfin-${VERSION}/jellyfin.tar.gz" \
|
||||
-C "../.." \
|
||||
./
|
||||
echo "Extracting filtered package."
|
||||
tar -xzf "$package_temporary_dir/jellyfin-${VERSION}/jellyfin.tar.gz" -C "$package_temporary_dir/jellyfin-${VERSION}"
|
||||
echo "Removing filtered package."
|
||||
rm "$package_temporary_dir/jellyfin-${VERSION}/jellyfin.tar.gz"
|
||||
echo "Repackaging package into final tarball."
|
||||
tar -zcf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" -C "$package_temporary_dir" "jellyfin-${VERSION}"
|
||||
fi
|
||||
|
||||
docker build ../.. -t "$image_name" -f ./Dockerfile
|
||||
mkdir -p "$output_dir"
|
||||
docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find /build/rpmbuild -maxdepth 3 -type f -name "jellyfin*.rpm" -exec mv {} /temp \;'
|
||||
chown -R "$current_user" "$package_temporary_dir" \
|
||||
|| sudo chown -R "$current_user" "$package_temporary_dir"
|
||||
mv "$package_temporary_dir"/*.rpm "$output_dir"
|
3
deployment/fedora-package-x64/pkg-src/.gitignore
vendored
Normal file
3
deployment/fedora-package-x64/pkg-src/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.rpm
|
||||
*.zip
|
||||
*.tar.gz
|
43
deployment/fedora-package-x64/pkg-src/README.md
Normal file
43
deployment/fedora-package-x64/pkg-src/README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Jellyfin RPM
|
||||
|
||||
## Build Fedora Package with docker
|
||||
|
||||
Change into this directory `cd rpm-package`
|
||||
Run the build script `./build-fedora-rpm.sh`.
|
||||
Resulting RPM and src.rpm will be in `../../jellyfin-*.rpm`
|
||||
|
||||
## ffmpeg
|
||||
|
||||
The RPM package for Fedora/CentOS requires some additional repositories as ffmpeg is not in the main repositories.
|
||||
|
||||
```shell
|
||||
# ffmpeg from RPMfusion free
|
||||
# Fedora
|
||||
$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
|
||||
# CentOS 7
|
||||
$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm
|
||||
```
|
||||
|
||||
## ISO mounting
|
||||
|
||||
To allow Jellyfin to mount/umount ISO files uncomment these two lines in `/etc/sudoers.d/jellyfin-sudoers`
|
||||
```
|
||||
# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount
|
||||
# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount
|
||||
```
|
||||
|
||||
## Building with dotnet
|
||||
|
||||
Jellyfin is build with `--self-contained` so no dotnet required for runtime.
|
||||
|
||||
```shell
|
||||
# dotnet required for building the RPM
|
||||
# Fedora
|
||||
$ sudo dnf copr enable @dotnet-sig/dotnet
|
||||
# CentOS
|
||||
$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] OpenSUSE
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<service>
|
||||
<short>Jellyfin</short>
|
||||
<description>The Free Software Media System.</description>
|
||||
<port protocol="tcp" port="8096"/>
|
||||
<port protocol="tcp" port="8920"/>
|
||||
<port protocol="udp" port="1900"/>
|
||||
<port protocol="udp" port="7359"/>
|
||||
</service>
|
27
deployment/fedora-package-x64/pkg-src/jellyfin.env
Normal file
27
deployment/fedora-package-x64/pkg-src/jellyfin.env
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Jellyfin default configuration options
|
||||
|
||||
# Use this file to override the default configurations; add additional
|
||||
# options with JELLYFIN_ADD_OPTS.
|
||||
|
||||
# To override the user or this config file's location, use
|
||||
# /etc/systemd/system/jellyfin.service.d/override.conf
|
||||
|
||||
#
|
||||
# This is a POSIX shell fragment
|
||||
#
|
||||
|
||||
#
|
||||
# General options
|
||||
#
|
||||
|
||||
# Tell jellyfin wich ffmpeg/ffprobe to use
|
||||
# JELLYFIN_FFMPEG="-ffmpeg /usr/bin/ffmpeg -ffprobe /usr/bin/ffprobe"
|
||||
|
||||
# Program directories
|
||||
JELLYFIN_DATA_DIRECTORY="/var/lib/jellyfin"
|
||||
JELLYFIN_CONFIG_DIRECTORY="/etc/jellyfin"
|
||||
JELLYFIN_LOG_DIRECTORY="/var/log/jellyfin"
|
||||
# In-App service control
|
||||
JELLYFIN_RESTART_OPT="-restartpath /usr/libexec/jellyfin/restart.sh"
|
||||
# Additional options for the binary
|
||||
JELLYFIN_ADD_OPTS=""
|
|
@ -0,0 +1,7 @@
|
|||
# Jellyfin systemd configuration options
|
||||
|
||||
# Use this file to override the user or environment file location.
|
||||
|
||||
[Service]
|
||||
#User = jellyfin
|
||||
#EnvironmentFile = /etc/sysconfig/jellyfin
|
15
deployment/fedora-package-x64/pkg-src/jellyfin.service
Normal file
15
deployment/fedora-package-x64/pkg-src/jellyfin.service
Normal file
|
@ -0,0 +1,15 @@
|
|||
[Unit]
|
||||
After=network.target
|
||||
Description=Jellyfin is a free software media system that puts you in control of managing and streaming your media.
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/sysconfig/jellyfin
|
||||
WorkingDirectory=/var/lib/jellyfin
|
||||
ExecStart=/usr/bin/jellyfin -programdata ${JELLYFIN_DATA_DIRECTORY} -configdir ${JELLYFIN_CONFIG_DIRECTORY} -logdir ${JELLYFIN_LOG_DIRECTORY} ${JELLYFIN_RESTART_OPT} ${JELLYFIN_ADD_OPTS} ${JELLYFIN_FFMPEG}
|
||||
TimeoutSec=15
|
||||
Restart=on-failure
|
||||
User=jellyfin
|
||||
Group=jellyfin
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
139
deployment/fedora-package-x64/pkg-src/jellyfin.spec
Normal file
139
deployment/fedora-package-x64/pkg-src/jellyfin.spec
Normal file
|
@ -0,0 +1,139 @@
|
|||
%global debug_package %{nil}
|
||||
# jellyfin tag to package
|
||||
%global gittag v10.0.1
|
||||
# Taglib-sharp commit of the submodule since github archive doesn't include submodules
|
||||
%global taglib_commit ee5ab21742b71fd1b87ee24895582327e9e04776
|
||||
%global taglib_shortcommit %(c=%{taglib_commit}; echo ${c:0:7})
|
||||
|
||||
AutoReq: no
|
||||
Name: jellyfin
|
||||
Version: 10.0.1
|
||||
Release: 1%{?dist}
|
||||
Summary: The Free Software Media Browser
|
||||
License: GPLv2
|
||||
URL: https://jellyfin.media
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
Source1: jellyfin.service
|
||||
Source2: jellyfin.env
|
||||
Source3: jellyfin.sudoers
|
||||
Source4: restart.sh
|
||||
Source5: jellyfin.override.conf
|
||||
Source6: jellyfin-firewalld.xml
|
||||
|
||||
%{?systemd_requires}
|
||||
BuildRequires: systemd
|
||||
Requires(pre): shadow-utils
|
||||
BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel
|
||||
Requires: libcurl, fontconfig, freetype, openssl, glibc libicu
|
||||
# Requirements not packaged in main repos
|
||||
# COPR @dotnet-sig/dotnet
|
||||
BuildRequires: dotnet-sdk-2.2
|
||||
# RPMfusion free
|
||||
Requires: ffmpeg
|
||||
|
||||
# For the update-db-paths.sh script to fix emby paths to jellyfin
|
||||
%{?fedora:Recommends: sqlite}
|
||||
|
||||
# Fedora has openssl1.1 which is incompatible with dotnet
|
||||
%{?fedora:Requires: compat-openssl10}
|
||||
# Disable Automatic Dependency Processing for Centos
|
||||
%{?el7:AutoReqProv: no}
|
||||
|
||||
%description
|
||||
Jellyfin is a free software media system that puts you in control of managing and streaming your media.
|
||||
|
||||
|
||||
%prep
|
||||
%autosetup -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
|
||||
dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime fedora-x64 Jellyfin.Server
|
||||
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE
|
||||
%{__install} -D -m 0644 %{SOURCE5} %{buildroot}%{_sysconfdir}/systemd/system/%{name}.service.d/override.conf
|
||||
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/%{name}/logging.json
|
||||
%{__mkdir} -p %{buildroot}%{_bindir}
|
||||
tee %{buildroot}%{_bindir}/jellyfin << EOF
|
||||
#!/bin/sh
|
||||
exec %{_libdir}/%{name}/%{name} \${@}
|
||||
EOF
|
||||
%{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin
|
||||
%{__mkdir} -p %{buildroot}%{_sysconfdir}/%{name}
|
||||
%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin
|
||||
|
||||
%{__install} -D -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/%{name}.service
|
||||
%{__install} -D -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name}
|
||||
%{__install} -D -m 0600 %{SOURCE3} %{buildroot}%{_sysconfdir}/sudoers.d/%{name}-sudoers
|
||||
%{__install} -D -m 0755 %{SOURCE4} %{buildroot}%{_libexecdir}/%{name}/restart.sh
|
||||
%{__install} -D -m 0644 %{SOURCE6} %{buildroot}%{_prefix}/lib/firewalld/service/%{name}.xml
|
||||
|
||||
%files
|
||||
%{_libdir}/%{name}/jellyfin-web/*
|
||||
%attr(755,root,root) %{_bindir}/%{name}
|
||||
%{_libdir}/%{name}/*.json
|
||||
%{_libdir}/%{name}/*.pdb
|
||||
%{_libdir}/%{name}/*.dll
|
||||
%{_libdir}/%{name}/*.so
|
||||
%{_libdir}/%{name}/*.a
|
||||
%{_libdir}/%{name}/createdump
|
||||
# Needs 755 else only root can run it since binary build by dotnet is 722
|
||||
%attr(755,root,root) %{_libdir}/%{name}/jellyfin
|
||||
%{_libdir}/%{name}/sosdocsunix.txt
|
||||
%{_unitdir}/%{name}.service
|
||||
%{_libexecdir}/%{name}/restart.sh
|
||||
%{_prefix}/lib/firewalld/service/%{name}.xml
|
||||
%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/%{name}
|
||||
%config %{_sysconfdir}/sysconfig/%{name}
|
||||
%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers
|
||||
%config(noreplace) %{_sysconfdir}/systemd/system/%{name}.service.d/override.conf
|
||||
%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/%{name}/logging.json
|
||||
%attr(-,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin
|
||||
%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin
|
||||
%if 0%{?fedora}
|
||||
%license LICENSE
|
||||
%else
|
||||
%{_datadir}/licenses/%{name}/LICENSE
|
||||
%endif
|
||||
|
||||
%pre
|
||||
getent group jellyfin >/dev/null || groupadd -r jellyfin
|
||||
getent passwd jellyfin >/dev/null || \
|
||||
useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \
|
||||
-c "Jellyfin default user" jellyfin
|
||||
exit 0
|
||||
|
||||
%post
|
||||
# Move existing configuration to /etc/jellyfin and symlink config to /etc/jellyfin
|
||||
if [ $1 -gt 1 ] ; then
|
||||
service_state=$(systemctl is-active jellyfin.service)
|
||||
if [ "${service_state}" = "active" ]; then
|
||||
systemctl stop jellyfin.service
|
||||
fi
|
||||
if [ ! -L %{_sharedstatedir}/%{name}/config ]; then
|
||||
mv %{_sharedstatedir}/%{name}/config/* %{_sysconfdir}/%{name}/
|
||||
rmdir %{_sharedstatedir}/%{name}/config
|
||||
ln -sf %{_sysconfdir}/%{name} %{_sharedstatedir}/%{name}/config
|
||||
fi
|
||||
if [ ! -L %{_sharedstatedir}/%{name}/logs ]; then
|
||||
mv %{_sharedstatedir}/%{name}/logs/* %{_var}/log/jellyfin
|
||||
rmdir %{_sharedstatedir}/%{name}/logs
|
||||
ln -sf %{_var}/log/jellyfin %{_sharedstatedir}/%{name}/logs
|
||||
fi
|
||||
if [ "${service_state}" = "active" ]; then
|
||||
systemctl start jellyfin.service
|
||||
fi
|
||||
fi
|
||||
%systemd_post jellyfin.service
|
||||
|
||||
%preun
|
||||
%systemd_preun jellyfin.service
|
||||
|
||||
%postun
|
||||
%systemd_postun_with_restart jellyfin.service
|
||||
|
||||
%changelog
|
||||
* Fri Jan 11 2019 Thomas Büttner <thomas@vergesslicher.tech> - 10.0.2-1
|
||||
- TODO Changelog for 10.0.2
|
19
deployment/fedora-package-x64/pkg-src/jellyfin.sudoers
Normal file
19
deployment/fedora-package-x64/pkg-src/jellyfin.sudoers
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Allow jellyfin group to start, stop and restart itself
|
||||
Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin
|
||||
Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin
|
||||
Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin
|
||||
|
||||
|
||||
%jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD
|
||||
%jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD
|
||||
%jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD
|
||||
|
||||
Defaults!RESTARTSERVER_SYSTEMD !requiretty
|
||||
Defaults!STARTSERVER_SYSTEMD !requiretty
|
||||
Defaults!STOPSERVER_SYSTEMD !requiretty
|
||||
|
||||
# Uncomment to allow the server to mount iso images
|
||||
# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount
|
||||
# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount
|
||||
|
||||
Defaults:%jellyfin !requiretty
|
6
deployment/fedora-package-x64/pkg-src/restart.sh
Normal file
6
deployment/fedora-package-x64/pkg-src/restart.sh
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
NAME=jellyfin
|
||||
restart_cmd="/usr/bin/systemctl restart ${NAME}"
|
||||
echo "sleep 2; sudo $restart_cmd > /dev/null 2>&1" | at now > /dev/null 2>&1
|
||||
exit 0
|
8
deployment/framework/build.sh
Executable file
8
deployment/framework/build.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
#Magic word framework will create a non self contained build
|
||||
build_jellyfin ../../Jellyfin.Server Release framework `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/framework/clean.sh
Executable file
7
deployment/framework/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/framework/package.sh
Executable file
7
deployment/framework/package.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/linux-x64/build.sh
Executable file
7
deployment/linux-x64/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin ../../Jellyfin.Server Release linux-x64 `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/linux-x64/clean.sh
Executable file
7
deployment/linux-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/linux-x64/package.sh
Executable file
7
deployment/linux-x64/package.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
34
deployment/make.sh
Executable file
34
deployment/make.sh
Executable file
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
git submodule update --init --recursive
|
||||
|
||||
pushd ../Jellyfin.Versioning
|
||||
./update-version
|
||||
popd
|
||||
|
||||
#TODO enabled proper flag parsing for enabling and disabling building, signing, packaging and publishing
|
||||
|
||||
# Execute all build.sh, package.sh, sign.sh and publish.sh scripts in every folder. In that order. Script should check for artifacts themselves.
|
||||
echo "Running for platforms '$@'."
|
||||
for directory in */ ; do
|
||||
platform=`basename "${directory}"`
|
||||
if [[ $@ == *"$platform"* || $@ = *"all"* ]]; then
|
||||
echo "Processing ${platform}"
|
||||
pushd "$platform"
|
||||
if [ -f build.sh ]; then
|
||||
./build.sh
|
||||
fi
|
||||
if [ -f package.sh ]; then
|
||||
./package.sh
|
||||
fi
|
||||
if [ -f sign.sh ]; then
|
||||
./sign.sh
|
||||
fi
|
||||
if [ -f publish.sh ]; then
|
||||
./publish.sh
|
||||
fi
|
||||
popd
|
||||
else
|
||||
echo "Skipping $platform."
|
||||
fi
|
||||
done
|
7
deployment/osx-x64/build.sh
Executable file
7
deployment/osx-x64/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin ../../Jellyfin.Server Release osx-x64 `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/osx-x64/clean.sh
Executable file
7
deployment/osx-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/osx-x64/package.sh
Executable file
7
deployment/osx-x64/package.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/ubuntu-x64/build.sh
Executable file
7
deployment/ubuntu-x64/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin ../../Jellyfin.Server Release ubuntu-x64 `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/ubuntu-x64/clean.sh
Executable file
7
deployment/ubuntu-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/ubuntu-x64/package.sh
Executable file
7
deployment/ubuntu-x64/package.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
15
deployment/unraid/docker-templates/README.md
Normal file
15
deployment/unraid/docker-templates/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# docker-templates
|
||||
|
||||
### Installation:
|
||||
|
||||
Open unRaid GUI (at least unRaid 6.5)
|
||||
|
||||
Click on the Docker tab
|
||||
|
||||
Add the following line under "Template Repositories"
|
||||
|
||||
https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates
|
||||
|
||||
Click save than click on Add Container and select jellyfin.
|
||||
|
||||
Adjust to your paths to your liking and off you go!
|
51
deployment/unraid/docker-templates/jellyfin.xml
Normal file
51
deployment/unraid/docker-templates/jellyfin.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Containers>
|
||||
<TemplateURL>https://raw.githubusercontent.com/jellyfin/jellyfin/deployment/unraid/docker-templates/jellyfin.xml</TemplateURL>
|
||||
<Beta>False</Beta>
|
||||
<Category>MediaApp:Video MediaApp:Music MediaApp:Photos MediaServer:Video MediaServer:Music MediaServer:Photos</Category>
|
||||
<Name>JellyFin</Name>
|
||||
<Description>
|
||||
JellyFin is The Free Software Media Browser Converted By Community Applications Always verify this template (and values) against the dockerhub support page for the container!![br][br]
|
||||
You can add as many mount points as needed for recordings, movies ,etc. [br][br]
|
||||
[b][span style='color: #E80000;']Directions:[/span][/b][br]
|
||||
[b]/config[/b] : this is where Jellyfin will store it's databases and configuration.[br][br]
|
||||
[b]Port[/b] : This is the default port for Jellyfin. (Will add ssl port later)[br][br]
|
||||
[b]Media[/b] : This is the mounting point of your media. When you access it in Jellyfin it will be /media or whatever you chose for a mount point
|
||||
[b]Tip:[/b] You can add more volume mappings if you wish Jellyfin has access to it.
|
||||
</Description>
|
||||
<Overview>
|
||||
Jellyfin Server is a home media server built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono and will remain completely free!
|
||||
</Overview>
|
||||
<Support>https://www.reddit.com/r/jellyfin/</Support>
|
||||
<Registry>https://hub.docker.com/r/jellyfin/jellyfin/</Registry>
|
||||
<GitHub>https://github.com/jellyfin/jellyfin/></GitHub>
|
||||
<Repository>jellyfin/jellyfin</Repository>
|
||||
<Project>https://jellyfin.media/</Project>
|
||||
<BindTime>true</BindTime>
|
||||
<Privileged>false</Privileged>
|
||||
<Networking>
|
||||
<Mode>host</Mode>
|
||||
<Publish>
|
||||
<Port>
|
||||
<HostPort>8096</HostPort>
|
||||
<ContainerPort>8096</ContainerPort>
|
||||
<Protocol>tcp</Protocol>
|
||||
</Port>
|
||||
</Publish>
|
||||
</Networking>
|
||||
<Data>
|
||||
<Volume>
|
||||
<HostDir>/mnt/cache/appdata/config</HostDir>
|
||||
<ContainerDir>/config</ContainerDir>
|
||||
<Mode>rw</Mode>
|
||||
</Volume>
|
||||
<Volume>
|
||||
<HostDir>/mnt/user</HostDir>
|
||||
<ContainerDir>/media</ContainerDir>
|
||||
<Mode>rw</Mode>
|
||||
</Volume>
|
||||
</Data>
|
||||
<WebUI>http://[IP]:[PORT:8096]/</WebUI>
|
||||
<Icon>https://raw.githubusercontent.com/binhex/docker-templates/master/binhex/images/emby-icon.png</Icon>
|
||||
<ExtraParams></ExtraParams>
|
||||
</Containers>
|
|
@ -1,110 +1,110 @@
|
|||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$InstallFFMPEG,
|
||||
[switch]$InstallNSSM,
|
||||
[switch]$GenerateZip,
|
||||
[string]$InstallLocation = "$Env:AppData/Jellyfin-Server/",
|
||||
[ValidateSet('Debug','Release')][string]$BuildType = 'Release',
|
||||
[ValidateSet('Quiet','Minimal', 'Normal')][string]$DotNetVerbosity = 'Minimal',
|
||||
[ValidateSet('win','win7', 'win8','win81','win10')][string]$WindowsVersion = 'win',
|
||||
[ValidateSet('x64','x86', 'arm', 'arm64')][string]$Architecture = 'x64'
|
||||
)
|
||||
|
||||
#PowershellCore and *nix check to make determine which temp dir to use.
|
||||
if(($PSVersionTable.PSEdition -eq 'Core') -and (-not $IsWindows)){
|
||||
$TempDir = mktemp -d
|
||||
}else{
|
||||
$TempDir = $env:Temp
|
||||
}
|
||||
|
||||
function Build-JellyFin {
|
||||
if(($Architecture -eq 'arm64') -and ($WindowsVersion -ne 'win10')){
|
||||
Write-Error "arm64 only supported with Windows10 Version"
|
||||
exit
|
||||
}
|
||||
if(($Architecture -eq 'arm') -and ($WindowsVersion -notin @('win10','win81','win8'))){
|
||||
Write-Error "arm only supported with Windows 8 or higher"
|
||||
exit
|
||||
}
|
||||
dotnet publish -c $BuildType -r "$windowsversion-$Architecture" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
|
||||
}
|
||||
|
||||
function Install-FFMPEG {
|
||||
param(
|
||||
[string]$InstallLocation,
|
||||
[string]$Architecture
|
||||
)
|
||||
Write-Verbose "Checking Architecture"
|
||||
if($Architecture -notin @('x86','x64')){
|
||||
Write-Warning "No builds available for your selected architecture of $Architecture"
|
||||
Write-Warning "FFMPEG will not be installed"
|
||||
}elseif($Architecture -eq 'x64'){
|
||||
Write-Verbose "Downloading 64 bit FFMPEG"
|
||||
Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.1-win64-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose
|
||||
}else{
|
||||
Write-Verbose "Downloading 32 bit FFMPEG"
|
||||
Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-4.1-win32-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose
|
||||
}
|
||||
|
||||
Expand-Archive "$tempdir/fmmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" | Write-Verbose
|
||||
if($Architecture -eq 'x64'){
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win64-static/bin" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}else{
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win32-static/bin" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}
|
||||
Remove-Item "$tempdir/ffmpeg/" -Recurse -Force -ErrorAction Continue | Write-Verbose
|
||||
Remove-Item "$tempdir/fmmpeg.zip" -Force -ErrorAction Continue | Write-Verbose
|
||||
}
|
||||
|
||||
function Install-NSSM {
|
||||
param(
|
||||
[string]$InstallLocation,
|
||||
[string]$Architecture
|
||||
)
|
||||
Write-Verbose "Checking Architecture"
|
||||
if($Architecture -notin @('x86','x64')){
|
||||
Write-Warning "No builds available for your selected architecture of $Architecture"
|
||||
Write-Warning "NSSM will not be installed"
|
||||
}else{
|
||||
Write-Verbose "Downloading NSSM"
|
||||
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -UseBasicParsing -OutFile "$tempdir/nssm.zip" | Write-Verbose
|
||||
}
|
||||
|
||||
Expand-Archive "$tempdir/nssm.zip" -DestinationPath "$tempdir/nssm/" | Write-Verbose
|
||||
if($Architecture -eq 'x64'){
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win64" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}else{
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win32" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}
|
||||
Remove-Item "$tempdir/nssm/" -Recurse -Force -ErrorAction Continue | Write-Verbose
|
||||
Remove-Item "$tempdir/nssm.zip" -Force -ErrorAction Continue | Write-Verbose
|
||||
}
|
||||
|
||||
Write-Verbose "Starting Build Process: Selected Environment is $WindowsVersion-$Architecture"
|
||||
Build-JellyFin
|
||||
if($InstallFFMPEG.IsPresent -or ($InstallFFMPEG -eq $true)){
|
||||
Write-Verbose "Starting FFMPEG Install"
|
||||
Install-FFMPEG $InstallLocation $Architecture
|
||||
}
|
||||
if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){
|
||||
Write-Verbose "Starting NSSM Install"
|
||||
Install-NSSM $InstallLocation $Architecture
|
||||
}
|
||||
Copy-Item .\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
|
||||
Copy-Item .\install.bat $InstallLocation\install.bat
|
||||
if($GenerateZip.IsPresent -or ($GenerateZip -eq $true)){
|
||||
Compress-Archive -Path $InstallLocation -DestinationPath "$InstallLocation/jellyfin.zip" -Force
|
||||
}
|
||||
Write-Verbose "Finished"
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$InstallFFMPEG,
|
||||
[switch]$InstallNSSM,
|
||||
[switch]$GenerateZip,
|
||||
[string]$InstallLocation = "$Env:AppData/Jellyfin-Server/",
|
||||
[ValidateSet('Debug','Release')][string]$BuildType = 'Release',
|
||||
[ValidateSet('Quiet','Minimal', 'Normal')][string]$DotNetVerbosity = 'Minimal',
|
||||
[ValidateSet('win','win7', 'win8','win81','win10')][string]$WindowsVersion = 'win',
|
||||
[ValidateSet('x64','x86', 'arm', 'arm64')][string]$Architecture = 'x64'
|
||||
)
|
||||
|
||||
#PowershellCore and *nix check to make determine which temp dir to use.
|
||||
if(($PSVersionTable.PSEdition -eq 'Core') -and (-not $IsWindows)){
|
||||
$TempDir = mktemp -d
|
||||
}else{
|
||||
$TempDir = $env:Temp
|
||||
}
|
||||
|
||||
function Build-JellyFin {
|
||||
if(($Architecture -eq 'arm64') -and ($WindowsVersion -ne 'win10')){
|
||||
Write-Error "arm64 only supported with Windows10 Version"
|
||||
exit
|
||||
}
|
||||
if(($Architecture -eq 'arm') -and ($WindowsVersion -notin @('win10','win81','win8'))){
|
||||
Write-Error "arm only supported with Windows 8 or higher"
|
||||
exit
|
||||
}
|
||||
dotnet publish -c $BuildType -r "$windowsversion-$Architecture" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
|
||||
}
|
||||
|
||||
function Install-FFMPEG {
|
||||
param(
|
||||
[string]$InstallLocation,
|
||||
[string]$Architecture
|
||||
)
|
||||
Write-Verbose "Checking Architecture"
|
||||
if($Architecture -notin @('x86','x64')){
|
||||
Write-Warning "No builds available for your selected architecture of $Architecture"
|
||||
Write-Warning "FFMPEG will not be installed"
|
||||
}elseif($Architecture -eq 'x64'){
|
||||
Write-Verbose "Downloading 64 bit FFMPEG"
|
||||
Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.1-win64-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose
|
||||
}else{
|
||||
Write-Verbose "Downloading 32 bit FFMPEG"
|
||||
Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-4.1-win32-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose
|
||||
}
|
||||
|
||||
Expand-Archive "$tempdir/fmmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" | Write-Verbose
|
||||
if($Architecture -eq 'x64'){
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win64-static/bin" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}else{
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win32-static/bin" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}
|
||||
Remove-Item "$tempdir/ffmpeg/" -Recurse -Force -ErrorAction Continue | Write-Verbose
|
||||
Remove-Item "$tempdir/fmmpeg.zip" -Force -ErrorAction Continue | Write-Verbose
|
||||
}
|
||||
|
||||
function Install-NSSM {
|
||||
param(
|
||||
[string]$InstallLocation,
|
||||
[string]$Architecture
|
||||
)
|
||||
Write-Verbose "Checking Architecture"
|
||||
if($Architecture -notin @('x86','x64')){
|
||||
Write-Warning "No builds available for your selected architecture of $Architecture"
|
||||
Write-Warning "NSSM will not be installed"
|
||||
}else{
|
||||
Write-Verbose "Downloading NSSM"
|
||||
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -UseBasicParsing -OutFile "$tempdir/nssm.zip" | Write-Verbose
|
||||
}
|
||||
|
||||
Expand-Archive "$tempdir/nssm.zip" -DestinationPath "$tempdir/nssm/" | Write-Verbose
|
||||
if($Architecture -eq 'x64'){
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win64" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}else{
|
||||
Write-Verbose "Copying Binaries to Jellyfin location"
|
||||
Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win32" | ForEach-Object {
|
||||
Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
|
||||
}
|
||||
}
|
||||
Remove-Item "$tempdir/nssm/" -Recurse -Force -ErrorAction Continue | Write-Verbose
|
||||
Remove-Item "$tempdir/nssm.zip" -Force -ErrorAction Continue | Write-Verbose
|
||||
}
|
||||
|
||||
Write-Verbose "Starting Build Process: Selected Environment is $WindowsVersion-$Architecture"
|
||||
Build-JellyFin
|
||||
if($InstallFFMPEG.IsPresent -or ($InstallFFMPEG -eq $true)){
|
||||
Write-Verbose "Starting FFMPEG Install"
|
||||
Install-FFMPEG $InstallLocation $Architecture
|
||||
}
|
||||
if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){
|
||||
Write-Verbose "Starting NSSM Install"
|
||||
Install-NSSM $InstallLocation $Architecture
|
||||
}
|
||||
Copy-Item .\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
|
||||
Copy-Item .\install.bat $InstallLocation\install.bat
|
||||
if($GenerateZip.IsPresent -or ($GenerateZip -eq $true)){
|
||||
Compress-Archive -Path $InstallLocation -DestinationPath "$InstallLocation/jellyfin.zip" -Force
|
||||
}
|
||||
Write-Verbose "Finished"
|
7
deployment/win-x64/build.sh
Executable file
7
deployment/win-x64/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin ../../Jellyfin.Server Release win-x64 `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/win-x64/clean.sh
Executable file
7
deployment/win-x64/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
9
deployment/win-x64/package.sh
Executable file
9
deployment/win-x64/package.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
||||
|
||||
#TODO setup and maybe change above code to produce the Windows native zip format.
|
7
deployment/win-x86/build.sh
Executable file
7
deployment/win-x86/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
build_jellyfin ../../Jellyfin.Server Release win-x86 `pwd`/dist/jellyfin_${VERSION}
|
7
deployment/win-x86/clean.sh
Executable file
7
deployment/win-x86/clean.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
9
deployment/win-x86/package.sh
Executable file
9
deployment/win-x86/package.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source ../common.build.sh
|
||||
|
||||
VERSION=`get_version ../..`
|
||||
|
||||
package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
|
||||
|
||||
#TODO setup and maybe change above code to produce the Windows native zip format.
|
Loading…
Add table
Add a link
Reference in a new issue