public async Task StartPlay( string mrl, int videoStreamIndex, int audioStreamIndex, int subtitleStreamIndex, int quality, FFProbeFileInfo fileInfo, double seconds = 0) { if (fileInfo == null) { _logger.LogWarning($"{nameof(StartPlay)}: No file info was provided for mrl = {mrl}"); throw new ArgumentNullException(nameof(fileInfo), "A file info must be provided"); } _ffmpegService.KillTranscodeProcess(); bool isLocal = _fileService.IsLocalFile(mrl); bool isUrlFile = _fileService.IsUrlFile(mrl); bool isVideoFile = _fileService.IsVideoFile(mrl); bool isMusicFile = _fileService.IsMusicFile(mrl); if (!isLocal && !isUrlFile) { var msg = "Invalid = {mrl}. Its not a local file and its not a url file"; _logger.LogWarning($"{nameof(StartPlay)}: {msg}"); throw new NotSupportedException(msg); } if (AvailableDevices.Count == 0) { _logger.LogWarning($"{nameof(StartPlay)}: No renders were found, file = {mrl}"); throw new NoDevicesException(); } if (_connecting) { _logger.LogWarning($"{nameof(StartPlay)}: We are in the middle of connecting to a device, can't play file = {mrl} right now"); throw new ConnectingException(); } if (!_renderWasSet && AvailableDevices.Count > 0) { await SetCastRenderer(AvailableDevices.First()).ConfigureAwait(false); } // create new media bool videoNeedsTranscode = isVideoFile && _ffmpegService.VideoNeedsTranscode(videoStreamIndex, _appSettings.ForceVideoTranscode, _appSettings.VideoScale, fileInfo); bool audioNeedsTranscode = _ffmpegService.AudioNeedsTranscode(audioStreamIndex, _appSettings.ForceAudioTranscode, fileInfo, isMusicFile); var hwAccelToUse = isVideoFile ? _ffmpegService.GetHwAccelToUse(videoStreamIndex, fileInfo, _appSettings.EnableHardwareAcceleration) : HwAccelDeviceType.None; _currentFilePath = mrl; string title = isLocal ? Path.GetFileName(mrl) : mrl; string url = isLocal ? _appWebServer.GetMediaUrl( mrl, videoStreamIndex, audioStreamIndex, seconds, videoNeedsTranscode, audioNeedsTranscode, hwAccelToUse, _appSettings.VideoScale, fileInfo.Videos.Find(f => f.Index == videoStreamIndex)?.WidthAndHeightText) : mrl; var metadata = isVideoFile ? new MovieMetadata { Title = title, } : new GenericMediaMetadata { Title = title, }; var media = new MediaInformation { ContentId = url, Metadata = metadata, //You have to set the contenttype before hand, with that, the album art of a music file will be shown ContentType = _ffmpegService.GetOutputTranscodeMimeType(mrl) }; var activeTrackIds = new List <int>(); bool useSubTitleStream = subtitleStreamIndex >= 0; if (useSubTitleStream || !string.IsNullOrEmpty(GetSubTitles?.Invoke())) { _logger.LogInformation($"{nameof(StartPlay)}: Subtitles were specified, generating a compatible one..."); string subtitleLocation = useSubTitleStream ? mrl : GetSubTitles.Invoke(); string subTitleFilePath = _fileService.GetSubTitleFilePath(); await _ffmpegService.GenerateSubTitles( subtitleLocation, subTitleFilePath, seconds, useSubTitleStream?subtitleStreamIndex : 0, _appSettings.SubtitleDelayInSeconds, default); _subtitle.TrackContentId = _appWebServer.GetSubTitlePath(subTitleFilePath); _logger.LogInformation($"{nameof(StartPlay)}: Subtitles were generated"); media.Tracks.Add(_subtitle); media.TextTrackStyle = GetSubtitleStyle(); activeTrackIds.Add(SubTitleDefaultTrackId); } string firstThumbnail = await GetFirstThumbnail().ConfigureAwait(false); string imgUrl = string.Empty; if (isLocal) { _logger.LogInformation($"{nameof(StartPlay)}: File is a local one, generating metadata..."); imgUrl = _appWebServer.GetPreviewPath(firstThumbnail); if (isVideoFile) { media.StreamType = StreamType.Live; } media.Duration = fileInfo.Format.Duration; if (isMusicFile) { media.Metadata = new MusicTrackMediaMetadata { Title = title, AlbumName = fileInfo.Format.Tag?.Album, Artist = fileInfo.Format.Tag?.Artist, }; } } else if (_youtubeUrlDecoder.IsYoutubeUrl(media.ContentId)) { _logger.LogInformation($"{nameof(StartPlay)}: File is a youtube link, parsing it..."); var ytMedia = await _youtubeUrlDecoder.Parse(media.ContentId, quality); QualitiesChanged?.Invoke(ytMedia.SelectedQuality, ytMedia.Qualities); imgUrl = ytMedia.ThumbnailUrl; media.ContentId = ytMedia.Url; media.Metadata.Title = ytMedia.Title; media.Metadata.Subtitle = ytMedia.Description; if (ytMedia.IsHls) { fileInfo = await _ffmpegService.GetFileInfo(ytMedia.Url, default); if (fileInfo == null) { _logger.LogWarning($"{nameof(StartPlay)}: Couldn't get the file info for url = {ytMedia.Url}"); throw new Exception($"File info is null for yt hls = {ytMedia.Url}"); } var closestQuality = fileInfo.Videos .Select(v => v.Height) .GetClosest(quality); var videoInfo = fileInfo.Videos.First(v => v.Height == closestQuality); videoStreamIndex = videoInfo.Index; audioStreamIndex = fileInfo.Audios.Any() ? fileInfo.Audios.Select(a => a.Index).GetClosest(videoStreamIndex) : -1; videoNeedsTranscode = _ffmpegService.VideoNeedsTranscode(videoStreamIndex, _appSettings.ForceVideoTranscode, _appSettings.VideoScale, fileInfo); audioNeedsTranscode = _ffmpegService.AudioNeedsTranscode(audioStreamIndex, _appSettings.ForceAudioTranscode, fileInfo); media.Duration = -1; media.StreamType = StreamType.Live; media.ContentId = _appWebServer.GetMediaUrl( ytMedia.Url, videoStreamIndex, audioStreamIndex, seconds, videoNeedsTranscode, audioNeedsTranscode, HwAccelDeviceType.None, VideoScaleType.Original, videoInfo.WidthAndHeightText); media.ContentType = _ffmpegService.GetOutputTranscodeMimeType(media.ContentId); } } if (!string.IsNullOrEmpty(imgUrl)) { media.Metadata.Images.Add(new GoogleCast.Models.Image { Url = imgUrl }); } _logger.LogInformation($"{nameof(StartPlay)}: Trying to load url = {media.ContentId}"); var status = await _player.LoadAsync(media, true, seconds, activeTrackIds.ToArray()); if (status is null) { var msg = $"Couldn't load url = {media.ContentId}"; _logger.LogWarning($"{nameof(StartPlay)}: {msg}"); throw new Exception(msg); } _logger.LogInformation($"{nameof(StartPlay)}: Url was successfully loaded"); FileLoaded(metadata.Title, imgUrl, _player.CurrentMediaDuration, _player.CurrentVolumeLevel, _player.IsMuted); }