Skip to content

Commit

Permalink
Merge pull request #77 from CodeItQuick/dev_original
Browse files Browse the repository at this point in the history
Issue #35 Curate the Live Channels Display on Homepage
  • Loading branch information
benrick authored May 14, 2019
2 parents 431082f + 0f1ee3f commit 2c4bd31
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public interface IChannelAggregateService
List<Channel> GetAll();
List<Channel> GetAll(string userId);
Channel GetAggregate(int id);
Task<List<Channel>> GetAllAggregates();
Task<int?> Create(Channel model, string userId);
Task<int> Update(Channel model);
Task<int> Delete(int id);
Expand Down
7 changes: 6 additions & 1 deletion src/DevChatter.DevStreams.Core/Twitch/ChannelLiveState.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
namespace DevChatter.DevStreams.Core.Twitch
using NodaTime;

namespace DevChatter.DevStreams.Core.Twitch
{
public class ChannelLiveState
{
public string TwitchId { get; set; }
public bool IsLive { get; set; }
public Instant StartedAt { get; set; }
public Duration TimeOnline => SystemClock.Instance.GetCurrentInstant() - StartedAt;
public int ViewerCount { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/DevChatter.DevStreams.Core/Twitch/TwitchResult.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using NodaTime;

namespace DevChatter.DevStreams.Core.Twitch
{
Expand Down Expand Up @@ -34,7 +35,7 @@ public class StreamResultData
public string Type { get; set; }
public string Title { get; set; }
public int Viewer_count { get; set; }
public DateTime Started_at { get; set; }
public Instant Started_at { get; set; }
public string Language { get; set; }
public string Thumbnail_url { get; set; }
public List<string> Tag_ids { get; set; }
Expand Down
25 changes: 24 additions & 1 deletion src/DevChatter.DevStreams.Core/Twitch/TwitchResultExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using DevChatter.DevStreams.Core.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using DevChatter.DevStreams.Core.Model;
using NodaTime;

namespace DevChatter.DevStreams.Core.Twitch
{
Expand All @@ -15,5 +19,24 @@ public static TwitchChannel ToTwitchChannelModel(this UserResultData src)
IsPartner = src.Broadcaster_type == TwitchConstants.PARTNER,
};
}

public static List<ChannelLiveState> CreateChannelLiveStatesFromStreamResults(
this StreamResult result, List<string> twitchIds)
{
List<ChannelLiveState> returnStat = twitchIds
.Select(twitchId =>
{
StreamResultData thisResult = result.Data.FirstOrDefault(x => x.User_id == twitchId);
return new ChannelLiveState
{
TwitchId = twitchId,
IsLive = thisResult != null,
StartedAt = thisResult?.Started_at ?? Instant.MinValue,
ViewerCount = thisResult?.Viewer_count ?? 0,
};
})
.ToList();
return returnStat;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ public Channel GetAggregate(int id)
}
}

/// <summary>
/// Returns all the channel data for a channel object.
/// </summary>
/// <returns>All Channel Info in a List<Channel> Object </Channel></returns>
public async Task<List<Channel>> GetAllAggregates()
{
string sql = @"SELECT * FROM Channels;
SELECT * FROM TwitchChannels;";

using (IDbConnection connection = new SqlConnection(_dbSettings.DefaultConnection))
{
var dbQuery = await connection.QueryMultipleAsync(sql);
List<Channel> channels = dbQuery.Read<Channel>().ToList();
List<TwitchChannel> twitchChannels = dbQuery.Read<TwitchChannel>().ToList();

foreach (var channel in channels)
{
channel.Twitch = twitchChannels.SingleOrDefault(x => x.ChannelId == channel.Id);
}

return channels;
}
}

public async Task<int?> Create(Channel model, string userId)
{
using (IDbConnection connection = new SqlConnection(_dbSettings.DefaultConnection))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
25 changes: 11 additions & 14 deletions src/DevChatter.DevStreams.Infra.Twitch/TwitchStreamService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
using DevChatter.DevStreams.Core.Twitch;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using NodaTime;
using NodaTime.Serialization.JsonNet;


namespace DevChatter.DevStreams.Infra.Twitch
{
Expand All @@ -30,22 +32,17 @@ public async Task<List<ChannelLiveState>> GetChannelLiveStates(List<string> twit
{
return new List<ChannelLiveState>(); // TODO: Replace with Guard Clause
}
var channelIdsQueryFormat = String.Join("&user_id=", twitchIds);
var channelIdsQueryFormat = string.Join("&user_id=", twitchIds);

var url = $"{_twitchSettings.BaseApiUrl}/streams?user_id={channelIdsQueryFormat}";
var jsonResult = await Get(url);

var result = JsonConvert.DeserializeObject<StreamResult>(jsonResult);
string jsonResult = await Get(url);

var liveChannels = result.Data.Where(x => x.Type == "live").ToList();
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);

var result = JsonConvert.DeserializeObject<StreamResult>(jsonResult, serializerSettings);

return twitchIds
.Select(twitchId => new ChannelLiveState
{
TwitchId = twitchId,
IsLive = liveChannels.Any(x => x.User_id == twitchId)
})
.ToList();
return result.CreateChannelLiveStatesFromStreamResults(twitchIds);
}

public async Task<ChannelLiveState> IsLive(string twitchId)
Expand All @@ -57,7 +54,7 @@ public async Task<ChannelLiveState> IsLive(string twitchId)

var result = JsonConvert.DeserializeObject<StreamResult>(jsonResult);

return new ChannelLiveState{TwitchId = twitchId, IsLive = result.Data.Any()};
return new ChannelLiveState { TwitchId = twitchId, IsLive = result.Data.Any() };
}

// TODO: Extract to composed dependency
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using DevChatter.DevStreams.Core.Twitch;
using FluentAssertions;
using NodaTime;
using NodaTime.Text;
using Xunit;

namespace DevChatter.DevStreams.UnitTests.Core.Twitch.TwitchResultExtensions
{
public class CreateChannelLiveStatesFromStreamResultsShould
{
[Fact]
public void ReturnEmpty_GivenNoResults()
{
var streamResult = new StreamResult { Data = new List<StreamResultData>() };
var twitchIds = new List<string>();

var liveStates = streamResult.CreateChannelLiveStatesFromStreamResults(twitchIds);

liveStates.Should().BeEmpty();
}

[Fact]
public void ReturnNonLiveResult_GivenNotLiveChannel()
{
const string twitchId = "123Link";
var streamResult = new StreamResult {Data = new List<StreamResultData>()};
var twitchIds = new List<string> { twitchId };

var liveStates = streamResult.CreateChannelLiveStatesFromStreamResults(twitchIds);

liveStates.Single().IsLive.Should().BeFalse();
liveStates.Single().TwitchId.Should().Be(twitchId);
liveStates.Single().StartedAt.Should().Be(Instant.MinValue);
liveStates.Single().ViewerCount.Should().Be(0);
}

[Fact]
public void ReturnLiveResultWithData_GivenLiveChannel()
{
const string twitchId = "DevChatter42";
var startedAt = InstantPattern.General.Parse("2019-05-04T17:04:19Z").Value;
const int viewerCount = 77;
var streamResult = new StreamResult {Data = new List<StreamResultData>
{
new StreamResultData
{
Started_at = startedAt,
Viewer_count = viewerCount,
User_id = twitchId,
}
}};
var twitchIds = new List<string> { twitchId };

var liveState = streamResult.CreateChannelLiveStatesFromStreamResults(twitchIds)
.Single();

liveState.IsLive.Should().BeTrue();
liveState.TwitchId.Should().Be(twitchId);
liveState.StartedAt.Should().Be(startedAt);
liveState.ViewerCount.Should().Be(viewerCount);
}
}
}
21 changes: 14 additions & 7 deletions src/DevChatter.DevStreams.Web/Controllers/IsLiveController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
using DevChatter.DevStreams.Core.Model;
using DevChatter.DevStreams.Core.Twitch;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DevChatter.DevStreams.Web.Data.ViewModel.LiveChannels;
using NodaTime;

namespace DevChatter.DevStreams.Web.Controllers
{
Expand All @@ -26,20 +29,24 @@ public IsLiveController(ICrudRepository crudRepository,


/// <summary>
/// Get all live streams.
/// Get info about all currently live channels.
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Get()
{
List<TwitchChannel> channels = await _crudRepository.GetAll<TwitchChannel>();
List<string> twitchIds = channels.Select(x => x.TwitchId).ToList();
var liveTwitchIds = (await _twitchService.GetChannelLiveStates(twitchIds))
List<Channel> channels = (await _channelAggregateService.GetAllAggregates())
.Where(c => c.Twitch != null).ToList();
List<string> twitchIds = channels.Select(x => x?.Twitch?.TwitchId).ToList();
twitchIds.RemoveAll(string.IsNullOrWhiteSpace);
List<ChannelLiveState> channelLiveStates = (await _twitchService.GetChannelLiveStates(twitchIds));
var liveTwitchData = channelLiveStates
.Where(x => x.IsLive)
.Select(x => x.TwitchId)
.OrderByDescending(x => x.ViewerCount)
.Select(x => x.ToViewModel(channels.Single(c => c.Twitch.TwitchId == x.TwitchId)))
.ToList();
var liveChannelNames = channels.Where(c => liveTwitchIds.Contains(c.TwitchId)).Select(c => c.TwitchName);
return Ok(liveChannelNames);

return Ok(liveTwitchData);
}

[HttpGet, Route("{twitchId}")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using NodaTime;

namespace DevChatter.DevStreams.Web.Data.ViewModel.LiveChannels
{
public class LiveChannelViewModel
{
public string ChannelName { get; set; }
public string Uri { get; set; }
public Instant StartedAt { get; set; }
public Duration TimeOnline => SystemClock.Instance.GetCurrentInstant() - StartedAt;
public int ViewerCount { get; set; }

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using DevChatter.DevStreams.Core.Model;
using DevChatter.DevStreams.Core.Twitch;

namespace DevChatter.DevStreams.Web.Data.ViewModel.LiveChannels
{
public static class LiveChannelsMappingExtensions
{
public static LiveChannelViewModel ToViewModel(this ChannelLiveState src, Channel channel)
{
var viewModel = new LiveChannelViewModel
{
ChannelName = channel.Name,
Uri = channel.Uri,
StartedAt = src.StartedAt,
ViewerCount = src.ViewerCount,
};

return viewModel;
}
}
}
26 changes: 20 additions & 6 deletions src/DevChatter.DevStreams.Web/Pages/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,26 @@
}
<div class="row" id="mainContent">
<div class="col-md-3">
<h2>Live Now</h2> @*TODO: Show channels that are live right now.*@
<ul>
<li v-for="liveChannel in liveChannels">
{{liveChannel}}
</li>
</ul>
<h2>Live Now</h2>
<div>
<div class="channel-card" v-for="liveChannel in liveChannels">
<div class="channel-card-img">
<img :src="getImageOrPlaceholder(liveChannel)" alt="ProfileImage">
</div>
<div class="container">
<h4><b>{{ liveChannel.channelName }}</b></h4>
<ul>
<li>
Viewers: {{ liveChannel.viewerCount }}
</li>
<li>
Online: {{ humanizeTime(liveChannel.timeOnline) }}
</li>
</ul>
<a :href="liveChannel.uri" class="btn btn-primary">Watch Now</a>
</div>
</div>
</div>
</div>
<div class="col-md-5">
<h2>DevChatter</h2>
Expand Down
Loading

0 comments on commit 2c4bd31

Please sign in to comment.