private async Task Login(PublicSystemInfo systemInfo) { //Check for auto-login credientials var config = _appHost.TheaterConfigurationManager.Configuration; try { if (systemInfo != null && string.Equals(systemInfo.Id, config.AutoLoginConfiguration.ServerId)) { await _appHost.SessionManager.ValidateSavedLogin(config.AutoLoginConfiguration); return; } } catch (UnauthorizedAccessException ex) { //Login failed, redirect to login page and clear the auto-login _logger.ErrorException("Auto-login failed", ex); config.AutoLoginConfiguration = new AutoLoginConfiguration(); _appHost.TheaterConfigurationManager.SaveConfiguration(); } catch (FormatException ex) { //Login failed, redirect to login page and clear the auto-login _logger.ErrorException("Auto-login password hash corrupt", ex); config.AutoLoginConfiguration = new AutoLoginConfiguration(); _appHost.TheaterConfigurationManager.SaveConfiguration(); } await _appHost.NavigationService.NavigateToLoginPage(); }
public void ImportInfo(PublicSystemInfo systemInfo) { Name = systemInfo.ServerName; Id = systemInfo.Id; if (!IsLocalAddressFixed && !string.IsNullOrEmpty(systemInfo.LocalAddress)) { LocalAddress = systemInfo.LocalAddress; } if (!string.IsNullOrEmpty(systemInfo.WanAddress)) { RemoteAddress = systemInfo.WanAddress; } var fullSystemInfo = systemInfo as SystemInfo; if (fullSystemInfo != null) { WakeOnLanInfos = new List <WakeOnLanInfo>(); if (!string.IsNullOrEmpty(fullSystemInfo.MacAddress)) { WakeOnLanInfos.Add(new WakeOnLanInfo { MacAddress = fullSystemInfo.MacAddress }); } } }
/// <summary> /// Create from a native SystemInfo object /// </summary> /// <param name="sysInfo"></param> /// <returns></returns> internal static PublicSystemInfo CreateFromSystemInfos(TPLink.SmartHome.SystemInfo sysInfo) { PublicSystemInfo infos = new PublicSystemInfo(); infos.FillWithSystemInfo(sysInfo); return(infos); }
private async Task OnSuccessfulConnection(ServerInfo server, ConnectionOptions options, PublicSystemInfo systemInfo, ConnectionResult result, ConnectionMode connectionMode, CancellationToken cancellationToken) { server.ImportInfo(systemInfo); var credentials = await _credentialProvider.GetServerCredentials().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(credentials.ConnectAccessToken)) { await EnsureConnectUser(credentials, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(server.ExchangeToken)) { await AddAuthenticationInfoFromConnect(server, connectionMode, credentials, cancellationToken).ConfigureAwait(false); } } if (!string.IsNullOrWhiteSpace(server.AccessToken)) { await ValidateAuthentication(server, connectionMode, options, cancellationToken).ConfigureAwait(false); } credentials.AddOrUpdateServer(server); if (options.UpdateDateLastAccessed) { server.DateLastAccessed = DateTime.UtcNow; } server.LastConnectionMode = connectionMode; await _credentialProvider.SaveServerCredentials(credentials).ConfigureAwait(false); result.ApiClient = GetOrAddApiClient(server, connectionMode); result.State = string.IsNullOrEmpty(server.AccessToken) ? ConnectionState.ServerSignIn : ConnectionState.SignedIn; ((ApiClient)result.ApiClient).EnableAutomaticNetworking(server, connectionMode, _networkConnectivity); if (result.State == ConnectionState.SignedIn) { AfterConnected(result.ApiClient, options); } CurrentApiClient = result.ApiClient; result.Servers.Add(server); if (Connected != null) { Connected(this, new GenericEventArgs <ConnectionResult>(result)); } }
public object Get(GetPublicSystemInfo request) { var result = _appHost.GetSystemInfo(); var publicInfo = new PublicSystemInfo { Id = result.Id, ServerName = result.ServerName, Version = result.Version }; return(ToOptimizedResult(publicInfo)); }
public void ImportInfo(PublicSystemInfo systemInfo) { Name = systemInfo.ServerName; Id = systemInfo.Id; if (!string.IsNullOrEmpty(systemInfo.LocalAddress)) { Address = new Uri(systemInfo.LocalAddress, UriKind.Relative); } if (!string.IsNullOrEmpty(systemInfo.LocalAddress)) { Address = new Uri(systemInfo.LocalAddress, UriKind.Relative); } }
public object Get(GetPublicSystemInfo request) { var result = _appHost.GetSystemInfo(); var publicInfo = new PublicSystemInfo { Id = result.Id, ServerName = result.ServerName, Version = result.Version, LocalAddress = result.LocalAddress, WanAddress = result.WanAddress, OperatingSystem = result.OperatingSystem }; return(ToOptimizedResult(publicInfo)); }
public async Task <object> Get(GetPublicSystemInfo request) { var result = await _appHost.GetSystemInfo().ConfigureAwait(false); var publicInfo = new PublicSystemInfo { Id = result.Id, ServerName = result.ServerName, Version = result.Version, LocalAddress = result.LocalAddress, WanAddress = result.WanAddress, OperatingSystem = result.OperatingSystem }; return(ToOptimizedResult(publicInfo)); }
public async Task <ConnectionResult> Connect(ServerInfo server, ConnectionOptions options, CancellationToken cancellationToken = default(CancellationToken)) { var result = new ConnectionResult { State = ConnectionState.Unavailable }; PublicSystemInfo systemInfo = null; var connectionMode = ConnectionMode.Manual; var tests = new[] { ConnectionMode.Manual, ConnectionMode.Local, ConnectionMode.Remote }.ToList(); // If we've connected to the server before, try to optimize by starting with the last used connection mode if (server.LastConnectionMode.HasValue) { tests.Remove(server.LastConnectionMode.Value); tests.Insert(0, server.LastConnectionMode.Value); } var isLocalNetworkAvailable = _networkConnectivity.GetNetworkStatus().GetIsAnyLocalNetworkAvailable(); // Kick off wake on lan on a separate thread (if applicable) var sendWakeOnLan = server.WakeOnLanInfos.Count > 0 && isLocalNetworkAvailable; var wakeOnLanTask = sendWakeOnLan ? Task.Run(() => WakeServer(server, cancellationToken), cancellationToken) : Task.FromResult(true); var wakeOnLanSendTime = DateTime.Now; foreach (var mode in tests) { _logger.Debug("Attempting to connect to server {0}. ConnectionMode: {1}", server.Name, mode.ToString()); if (mode == ConnectionMode.Local) { // Try connect locally if there's a local address, // and we're either on localhost or the device has a local connection if (!string.IsNullOrEmpty(server.LocalAddress) && isLocalNetworkAvailable) { // Try to connect to the local address systemInfo = await TryConnect(server.LocalAddress, 8000, cancellationToken).ConfigureAwait(false); } } else if (mode == ConnectionMode.Manual) { // Try manual address if there is one, but only if it's different from the local/remote addresses if (!string.IsNullOrEmpty(server.ManualAddress) && !string.Equals(server.ManualAddress, server.LocalAddress, StringComparison.OrdinalIgnoreCase) && !string.Equals(server.ManualAddress, server.RemoteAddress, StringComparison.OrdinalIgnoreCase)) { // Try to connect to the local address systemInfo = await TryConnect(server.ManualAddress, 15000, cancellationToken).ConfigureAwait(false); } } else if (mode == ConnectionMode.Remote) { if (!string.IsNullOrEmpty(server.RemoteAddress)) { systemInfo = await TryConnect(server.RemoteAddress, 15000, cancellationToken).ConfigureAwait(false); } } if (systemInfo != null) { connectionMode = mode; break; } } if (systemInfo == null && !string.IsNullOrEmpty(server.LocalAddress) && isLocalNetworkAvailable && sendWakeOnLan) { await wakeOnLanTask.ConfigureAwait(false); // After wake on lan finishes, make sure at least 10 seconds have elapsed since the time it was first sent out var waitTime = TimeSpan.FromSeconds(10).TotalMilliseconds - (DateTime.Now - wakeOnLanSendTime).TotalMilliseconds; if (waitTime > 0) { await Task.Delay(Convert.ToInt32(waitTime, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false); } systemInfo = await TryConnect(server.LocalAddress, 15000, cancellationToken).ConfigureAwait(false); } if (systemInfo != null) { await OnSuccessfulConnection(server, options, systemInfo, result, connectionMode, cancellationToken) .ConfigureAwait(false); } result.ConnectUser = ConnectUser; return(result); }
private void CheckForMetadata(object sender, ElapsedEventArgs eventArgs) { try { queuedUpdateCheck.ToList().ForEach(async updateCheck => { Guid itemId = updateCheck.Key; _logger.Debug("{0} queued for recheck", itemId.ToString()); BaseItem item = _libraryManager.GetItemById(itemId); LibraryOptions itemLibraryOptions = _libraryManager.GetLibraryOptions(item); DiscordOptions options = Plugin.Instance.Configuration.Options.FirstOrDefault(opt => opt.MediaAddedOverride == true); PublicSystemInfo sysInfo = await _applicationHost.GetPublicSystemInfo(CancellationToken.None); ServerConfiguration serverConfig = _serverConfiguration.Configuration; if (!isInVisibleLibrary(options.MediaBrowserUserId, item)) { queuedUpdateCheck.Remove(itemId); _logger.Debug("User does not have access to library, skipping..."); return; } // for whatever reason if you have extraction on during library scans then it waits for the extraction to finish before populating the metadata.... I don't get why the f**k it goes in that order // its basically impossible to make a prediction on how long it could take as its dependent on the bitrate, duration, codec, and processing power of the system Boolean localMetadataFallback = queuedUpdateCheck[itemId] >= (itemLibraryOptions.ExtractChapterImagesDuringLibraryScan ? Constants.MaxRetriesBeforeFallback * 5.5 : Constants.MaxRetriesBeforeFallback); if (item.ProviderIds.Count > 0 || localMetadataFallback) { _logger.Debug("{0}[{1}] has metadata, sending notification", item.Id, item.Name); string serverName = options.ServerNameOverride ? serverConfig.ServerName : "Emby Server"; string LibraryType = item.GetType().Name; // build primary info DiscordMessage mediaAddedEmbed = new DiscordMessage { username = options.Username, avatar_url = options.AvatarUrl, embeds = new List <DiscordEmbed>() { new DiscordEmbed() { color = DiscordWebhookHelper.FormatColorCode(options.EmbedColor), footer = new Footer { text = $"From {serverName}", icon_url = options.AvatarUrl }, timestamp = DateTime.Now } }, }; // populate title string titleText; if (LibraryType == "Episode") { titleText = $"{item.Parent.Parent.Name} {(item.ParentIndexNumber != null ? $"S{formatIndex(item.ParentIndexNumber)}" : "")}{(item.IndexNumber != null ? $"E{formatIndex(item.IndexNumber)}" : "")} {item.Name}"; } else { titleText = $"{item.Name} {(!String.IsNullOrEmpty(item.ProductionYear.ToString()) ? $"({item.ProductionYear.ToString()})" : "")}"; } mediaAddedEmbed.embeds.First().title = _localizationManager.GetLocalizedString("ValueHasBeenAddedToLibrary").Replace("{0}", titleText).Replace("{1}", serverName); // populate description if (LibraryType == "Audio") { List <BaseItem> artists = _libraryManager.GetAllArtists(item); IEnumerable <string> artistsFormat = artists.Select(artist => { if (artist.ProviderIds.Count() > 0) { KeyValuePair <string, string> firstProvider = artist.ProviderIds.FirstOrDefault(); string providerUrl = firstProvider.Key == "MusicBrainzArtist" ? $"https://musicbrainz.org/artist/{firstProvider.Value}" : $"https://theaudiodb.com/artist/{firstProvider.Value}"; return($"[{artist.Name}]({providerUrl})"); } else { return(artist.Name); } }); if (artists.Count() > 0) { mediaAddedEmbed.embeds.First().description = $"By {string.Join(", ", artistsFormat)}"; } } else { if (!String.IsNullOrEmpty(item.Overview)) { mediaAddedEmbed.embeds.First().description = item.Overview; } } // populate title URL if (!String.IsNullOrEmpty(sysInfo.WanAddress) && !options.ExcludeExternalServerLinks) { mediaAddedEmbed.embeds.First().url = $"{sysInfo.WanAddress}/web/index.html#!/item?id={itemId}&serverId={sysInfo.Id}"; } // populate images if (item.HasImage(ImageType.Primary)) { string imageUrl = ""; if (!item.GetImageInfo(ImageType.Primary, 0).IsLocalFile) { imageUrl = item.GetImagePath(ImageType.Primary); } else if (serverConfig.EnableRemoteAccess == true && !options.ExcludeExternalServerLinks) // in the future we can proxy images through memester server if people want to hide their server address { imageUrl = $"{sysInfo.WanAddress}/emby/Items/{itemId}/Images/Primary"; } else { string localPath = item.GetImagePath(ImageType.Primary); Stream imageData = _fileSystem.OpenRead(localPath); try { imageUrl = await MemesterServiceHelper.UploadImage(imageData, _jsonSerializer, _httpClient); } catch (Exception e) { _logger.ErrorException("Failed to proxy image", e); } } mediaAddedEmbed.embeds.First().thumbnail = new Thumbnail { url = imageUrl }; } if (options.MentionType == MentionTypes.Everyone) { mediaAddedEmbed.content = "@everyone"; } else if (options.MentionType == MentionTypes.Here) { mediaAddedEmbed.content = "@here"; } // populate external URLs List <Field> providerFields = new List <Field>(); if (!localMetadataFallback) { item.ProviderIds.ToList().ForEach(provider => { Field field = new Field { name = "External Details" }; Boolean didPopulate = true; switch (provider.Key.ToLower()) { case "imdb": field.value = $"[IMDb](https://www.imdb.com/title/{provider.Value}/)"; break; case "tmdb": field.value = $"[TMDb](https://www.themoviedb.org/{(LibraryType == "Movie" ? "movie" : "tv")}/{provider.Value})"; break; case "musicbrainztrack": field.value = $"[MusicBrainz Track](https://musicbrainz.org/track/{provider.Value})"; break; case "musicbrainzalbum": field.value = $"[MusicBrainz Album](https://musicbrainz.org/release/{provider.Value})"; break; case "theaudiodbalbum": field.value = $"[TADb Album](https://theaudiodb.com/album/{provider.Value})"; break; default: didPopulate = false; break; } if (didPopulate == true) { providerFields.Add(field); } }); if (providerFields.Count() > 0) { mediaAddedEmbed.embeds.First().fields = providerFields; } } pendingSendQueue.Add(mediaAddedEmbed, options); queuedUpdateCheck.Remove(itemId); } else { queuedUpdateCheck[itemId]++; _logger.Debug("Attempt: {0}", queuedUpdateCheck[itemId]); } }); } catch (Exception e) { _logger.ErrorException("Something unexpected happened in the update checker", e); } }
private void CheckForMetadata(object sender, ElapsedEventArgs eventArgs) { try { int queueCount = queuedUpdateCheck.Count(); if (queueCount > 0) { _logger.Debug("Item in queue : {0}", queueCount); } queuedUpdateCheck.ToList().ForEach(async queuedItem => { // sometimes an update check might execute while another one is hanging and causes crash ! if (queuedUpdateCheck.ContainsKey(queuedItem.Key)) { DiscordOptions options = queuedItem.Value.Configuration; Guid itemId = queuedItem.Value.ItemId; _logger.Debug("{0} queued for recheck", itemId.ToString()); BaseItem item = _libraryManager.GetItemById(itemId); LibraryOptions itemLibraryOptions = _libraryManager.GetLibraryOptions(item); PublicSystemInfo sysInfo = await _applicationHost.GetPublicSystemInfo(CancellationToken.None); ServerConfiguration serverConfig = _serverConfiguration.Configuration; string LibraryType = item.GetType().Name; string serverName = options.ServerNameOverride ? serverConfig.ServerName : "Emby Server"; if (string.IsNullOrEmpty(serverName)) { serverName = "Emby Server"; } // for whatever reason if you have extraction on during library scans then it waits for the extraction to finish before populating the metadata.... I don't get why the f**k it goes in that order // its basically impossible to make a prediction on how long it could take as its dependent on the bitrate, duration, codec, and processing power of the system Boolean localMetadataFallback = queuedUpdateCheck[queuedItem.Key].Retries >= (itemLibraryOptions.ExtractChapterImagesDuringLibraryScan ? Constants.MaxRetriesBeforeFallback * 5.5 : Constants.MaxRetriesBeforeFallback); if (item.ProviderIds.Count > 0 || localMetadataFallback) { _logger.Debug("{0}[{1}] has metadata (Local fallback: {2}), adding to queue", item.Id, item.Name, localMetadataFallback, options.MediaBrowserUserId); if (queuedUpdateCheck.ContainsKey(queuedItem.Key)) { queuedUpdateCheck.Remove(queuedItem.Key); // remove it beforehand because if some operation takes any longer amount of time it might allow enough time for another notification to slip through } // build primary info DiscordMessage mediaAddedEmbed = new DiscordMessage { username = options.Username, avatar_url = options.AvatarUrl, embeds = new List <DiscordEmbed>() { new DiscordEmbed() { color = DiscordWebhookHelper.FormatColorCode(options.EmbedColor), footer = new Footer { text = $"From {serverName}", icon_url = options.AvatarUrl }, timestamp = DateTime.Now } }, }; // populate title string titleText; if (LibraryType == "Episode") { titleText = $"{item.Parent.Parent.Name}{(item.ParentIndexNumber.HasValue ? $" S{formatIndex(item.ParentIndexNumber)}" : "")}{(item.IndexNumber.HasValue ? $"E{formatIndex(item.IndexNumber)}" : "")} {item.Name}"; } else if (LibraryType == "Season") { titleText = $"{item.Parent.Name} {item.Name}"; } else { titleText = $"{item.Name}{(item.ProductionYear.HasValue ? $" ({item.ProductionYear.ToString()})" : "")}"; } mediaAddedEmbed.embeds.First().title = $"{titleText} has been added to {serverName.Trim()}"; // populate description if (LibraryType == "Audio") { List <BaseItem> artists = _libraryManager.GetAllArtists(item); IEnumerable <string> artistsFormat = artists.Select(artist => { string formattedArtist = artist.Name; if (artist.ProviderIds.Count() > 0) { KeyValuePair <string, string> firstProvider = artist.ProviderIds.FirstOrDefault(); string providerUrl = firstProvider.Key == "MusicBrainzArtist" ? $"https://musicbrainz.org/artist/{firstProvider.Value}" : $"https://theaudiodb.com/artist/{firstProvider.Value}"; formattedArtist += $" [(Music Brainz)]({providerUrl})"; } if (serverConfig.EnableRemoteAccess && !options.ExcludeExternalServerLinks) { formattedArtist += $" [(Emby)]({sysInfo.WanAddress}/web/index.html#!/item?id={itemId}&serverId={artist.InternalId})"; } return(formattedArtist); }); if (artists.Count() > 0) { mediaAddedEmbed.embeds.First().description = $"By {string.Join(", ", artistsFormat)}"; } } else { if (!String.IsNullOrEmpty(item.Overview)) { mediaAddedEmbed.embeds.First().description = item.Overview; } } // populate title URL if (serverConfig.EnableRemoteAccess && !options.ExcludeExternalServerLinks) { mediaAddedEmbed.embeds.First().url = $"{sysInfo.WanAddress}/web/index.html#!/item?id={itemId}&serverId={sysInfo.Id}"; } // populate images if (item.HasImage(ImageType.Primary)) { string imageUrl = ""; if (!item.GetImageInfo(ImageType.Primary, 0).IsLocalFile) { imageUrl = item.GetImagePath(ImageType.Primary); } else if (serverConfig.EnableRemoteAccess == true && !options.ExcludeExternalServerLinks) // in the future we can proxy images through memester server if people want to hide their server address { imageUrl = $"{sysInfo.WanAddress}/emby/Items/{itemId}/Images/Primary"; } else { string localPath = item.GetImagePath(ImageType.Primary); try { ImageServiceResponse response = MemesterServiceHelper.UploadImage(localPath, _jsonSerializer); imageUrl = response.filePath; } catch (Exception e) { _logger.ErrorException("Failed to proxy image", e); } } mediaAddedEmbed.embeds.First().thumbnail = new Thumbnail { url = imageUrl }; } if (options.MentionType == MentionTypes.Everyone) { mediaAddedEmbed.content = "@everyone"; } else if (options.MentionType == MentionTypes.Here) { mediaAddedEmbed.content = "@here"; } // populate external URLs List <Field> providerFields = new List <Field>(); if (!localMetadataFallback) { item.ProviderIds.ToList().ForEach(provider => { Field field = new Field { name = "External Links" }; Boolean didPopulate = true; switch (provider.Key.ToLower()) { case "imdb": field.value = $"[IMDb](https://www.imdb.com/title/{provider.Value}/)"; break; case "tmdb": field.value = $"[TMDb](https://www.themoviedb.org/{(LibraryType == "Movie" ? "movie" : "tv")}/{provider.Value})"; break; case "musicbrainztrack": field.value = $"[MusicBrainz Track](https://musicbrainz.org/track/{provider.Value})"; break; case "musicbrainzalbum": field.value = $"[MusicBrainz Album](https://musicbrainz.org/release/{provider.Value})"; break; case "theaudiodbalbum": field.value = $"[TADb Album](https://theaudiodb.com/album/{provider.Value})"; break; default: didPopulate = false; break; } if (didPopulate == true) { providerFields.Add(field); } }); if (providerFields.Count() > 0) { mediaAddedEmbed.embeds.First().fields = providerFields; } } pendingSendQueue.Add(mediaAddedEmbed, options); } else { if (queuedUpdateCheck.ContainsKey(queuedItem.Key)) { queuedUpdateCheck[queuedItem.Key].Retries++; } } } }); } catch (Exception e) { _logger.ErrorException("Something unexpected happened in the item update checker", e); } }
/// <summary> /// Loads the initial presentation. /// </summary> /// <returns>Task.</returns> private async Task LoadInitialPresentation() { var foundServer = false; _appHost.PresentationManager.ShowModalLoadingAnimation(); PublicSystemInfo systemInfo = null; //Try and send WOL now to give system time to wake await _appHost.SendWolCommand(); try { systemInfo = await _appHost.ApiClient.GetPublicSystemInfoAsync(CancellationToken.None).ConfigureAwait(false); foundServer = true; } catch (Exception ex) { _logger.ErrorException( "Error connecting to server using saved connection information. Host: {0}, Port {1}", ex, _appHost.ApiClient.ServerAddress); } //Try and wait for WOL if its configured if (!foundServer && _appHost.TheaterConfigurationManager.Configuration.WolConfiguration != null && _appHost.TheaterConfigurationManager.Configuration.WolConfiguration.HostMacAddresses.Count > 0) { for (var i = 0; i < _appHost.TheaterConfigurationManager.Configuration.WolConfiguration.WakeAttempts; i++) { try { systemInfo = await _appHost.ApiClient.GetSystemInfoAsync(CancellationToken.None).ConfigureAwait(false); } catch (Exception) { } if (systemInfo != null) { foundServer = true; break; } } } //Try and find server if (!foundServer) { try { var info = (await new ServerLocator(_logger).FindServers(500, CancellationToken.None).ConfigureAwait(false)).FirstOrDefault(); if (info != null) { _appHost.ApiClient.ChangeServerLocation(info.Address); systemInfo = await _appHost.ApiClient.GetPublicSystemInfoAsync(CancellationToken.None).ConfigureAwait(false); foundServer = true; } } catch (Exception ex) { _logger.ErrorException("Error attempting to locate server.", ex); } } _appHost.PresentationManager.HideModalLoadingAnimation(); var mediaFilters = _appHost.MediaFilters; if (!foundServer || !AreRequiredMediaFiltersInstalled(mediaFilters)) { // Show connection wizard await Dispatcher.InvokeAsync(async() => await _appHost.NavigationService.Navigate(new StartupWizardPage(_appHost.NavigationService, _appHost.TheaterConfigurationManager, _appHost.ApiClient, _appHost.PresentationManager, _logger, mediaFilters))); return; } //Do final login await Dispatcher.InvokeAsync(async() => await Login(systemInfo)); }