aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Endpoints/CreateRadioIndex.cs (renamed from src/Endpoints/RadioSearchEndpoint.cs)7
-rw-r--r--src/Models/Database/RadioEpisode.cs13
-rw-r--r--src/Models/Database/RadioSeason.cs11
-rw-r--r--src/Models/Database/RadioSeries.cs11
-rw-r--r--src/Models/NrkLinks.cs14
-rw-r--r--src/Models/NrkPlaybackManifest.cs19
-rw-r--r--src/Models/NrkRadioCategorySearchResult.cs (renamed from src/Models/RadioCategorySearchResult.cs)19
-rw-r--r--src/Models/NrkRadioSeries.cs79
-rw-r--r--src/Models/RadioSeries.cs18
-rw-r--r--src/Program.cs6
-rw-r--r--src/RadioIndexDb.cs81
-rw-r--r--src/Services/NrkRadioService.cs63
12 files changed, 286 insertions, 55 deletions
diff --git a/src/Endpoints/RadioSearchEndpoint.cs b/src/Endpoints/CreateRadioIndex.cs
index 79f24ce..1ef19ab 100644
--- a/src/Endpoints/RadioSearchEndpoint.cs
+++ b/src/Endpoints/CreateRadioIndex.cs
@@ -10,8 +10,9 @@ public class RadioSearchEndpoint : EndpointBase
_radio = radio;
}
- [HttpGet("~/radio-search")]
- public async Task HandleASync(string q) {
-
+ [HttpGet("~/create-radio-index")]
+ public async Task<ActionResult> HandleASync() {
+ await _radio.CreateIndex();
+ return Ok();
}
} \ No newline at end of file
diff --git a/src/Models/Database/RadioEpisode.cs b/src/Models/Database/RadioEpisode.cs
new file mode 100644
index 0000000..508177b
--- /dev/null
+++ b/src/Models/Database/RadioEpisode.cs
@@ -0,0 +1,13 @@
+namespace I2R.LightNews.Models;
+
+public class RadioEpisode
+{
+ public int Id { get; set; }
+ public int SeriesId { get; set; }
+ public int SeasonId { get; set; }
+ public string NrkId { get; set; }
+ public string Title { get; set; }
+ public string Subtitle { get; set; }
+ public string SourceUrl { get; set; }
+ public string CanonicalUrl { get; set; }
+} \ No newline at end of file
diff --git a/src/Models/Database/RadioSeason.cs b/src/Models/Database/RadioSeason.cs
new file mode 100644
index 0000000..5db925d
--- /dev/null
+++ b/src/Models/Database/RadioSeason.cs
@@ -0,0 +1,11 @@
+namespace I2R.LightNews.Models;
+
+public class RadioSeason
+{
+ public int Id { get; set; }
+ public int SeriesId { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public string CanonicalUrl { get; set; }
+ public string NrkId { get; set; }
+} \ No newline at end of file
diff --git a/src/Models/Database/RadioSeries.cs b/src/Models/Database/RadioSeries.cs
new file mode 100644
index 0000000..946b6ce
--- /dev/null
+++ b/src/Models/Database/RadioSeries.cs
@@ -0,0 +1,11 @@
+namespace I2R.LightNews.Models;
+
+public class RadioSeries
+{
+ public int Id { get; set; }
+ public string NrkId { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public string Type { get; set; }
+ public string CanonicalUrl { get; set; }
+} \ No newline at end of file
diff --git a/src/Models/NrkLinks.cs b/src/Models/NrkLinks.cs
new file mode 100644
index 0000000..69f5ae3
--- /dev/null
+++ b/src/Models/NrkLinks.cs
@@ -0,0 +1,14 @@
+namespace I2R.LightNews.Models;
+
+public class NrkLinks
+{
+ public LinkModel NextPage { get; set; }
+ public LinkModel LastPage { get; set; }
+ public LinkModel Share { get; set; }
+ public LinkModel Episodes { get; set; }
+
+ public class LinkModel
+ {
+ public string Href { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Models/NrkPlaybackManifest.cs b/src/Models/NrkPlaybackManifest.cs
new file mode 100644
index 0000000..52f9503
--- /dev/null
+++ b/src/Models/NrkPlaybackManifest.cs
@@ -0,0 +1,19 @@
+namespace I2R.LightNews.Models;
+
+public class NrkPlaybackManifest
+{
+ public PlayableModel Playable { get; set; }
+
+ public class PlayableModel
+ {
+ public List<Asset> Assets { get; set; }
+
+ public class Asset
+ {
+ public string Url { get; set; }
+ public string Format { get; set; }
+ public string MimeType { get; set; }
+ public bool Encrypted { get; set; }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Models/RadioCategorySearchResult.cs b/src/Models/NrkRadioCategorySearchResult.cs
index 7fd4c5d..6672105 100644
--- a/src/Models/RadioCategorySearchResult.cs
+++ b/src/Models/NrkRadioCategorySearchResult.cs
@@ -5,7 +5,7 @@ namespace I2R.LightNews.Models;
public class RadioCategorySearchResult
{
[JsonPropertyName("_links")]
- public LinksModel Links { get; set; }
+ public NrkLinks Links { get; set; }
public List<LetterModel> Letters { get; set; }
public string Title { get; set; }
@@ -17,7 +17,7 @@ public class RadioCategorySearchResult
[JsonPropertyName("_links")]
public LinksModel Links { get; set; }
- public Guid Id { get; set; }
+ public string Id { get; set; }
public string SeriesId { get; set; }
public string SeasonId { get; set; }
public string Title { get; set; }
@@ -33,7 +33,8 @@ public class RadioCategorySearchResult
public class LinksModel
{
- public RadioCategorySearchResult.LinksModel.LinkModel CustomSeason { get; set; }
+ public NrkLinks.LinkModel CustomSeason { get; set; }
+ public NrkLinks.LinkModel Series { get; set; }
}
}
@@ -43,16 +44,4 @@ public class RadioCategorySearchResult
public int Count { get; set; }
public string Link { get; set; }
}
-
- public class LinksModel
- {
- public LinkModel NextPage { get; set; }
- public LinkModel LastPage { get; set; }
-
-
- public class LinkModel
- {
- public string Href { get; set; }
- }
- }
} \ No newline at end of file
diff --git a/src/Models/NrkRadioSeries.cs b/src/Models/NrkRadioSeries.cs
new file mode 100644
index 0000000..3496f36
--- /dev/null
+++ b/src/Models/NrkRadioSeries.cs
@@ -0,0 +1,79 @@
+using System.Text.Json.Serialization;
+
+namespace I2R.LightNews.Models;
+
+public class NrkRadioSeries
+{
+ [JsonPropertyName("_links")]
+ public NrkLinks Links { get; set; }
+
+ [JsonPropertyName("_embedded")]
+ public EmbeddedModel Embedded { get; set; }
+
+ public class EmbeddedModel
+ {
+ public List<SeasonModel> Seasons { get; set; }
+
+ public class SeasonModel
+ {
+ public List<TitleModel> Titles { get; set; }
+ public List<EpisodeModel> Episodes { get; set; }
+ public string Id { get; set; }
+ public bool HasAvailableEpisodes { get; set; }
+ public int EpisodeCount { get; set; }
+
+ public class EpisodeModel
+ {
+ [JsonPropertyName("_embedded")]
+ public EmbeddedModel Embedded { get; set; }
+
+ public class EmbeddedModel
+ {
+ public List<EpisodeModel> Episodes { get; set; }
+
+ public class EpisodeModel
+ {
+ [JsonPropertyName("_links")]
+ public LinksModel Links { get; set; }
+
+ public string Id { get; set; }
+ public string EpisodeId { get; set; }
+ public List<TitlesModel> Titles { get; set; }
+ public DateTime Date { get; set; }
+ public int DurationInSeconds { get; set; }
+ public int ProductionYear { get; set; }
+
+ public class TitlesModel
+ {
+ public string Title { get; set; }
+ public string Subtitle { get; set; }
+ }
+
+ public class LinksModel
+ {
+ public NrkLinks.LinkModel Playback { get; set; }
+ public NrkLinks.LinkModel Share { get; set; }
+ }
+ }
+ }
+ }
+
+ public class TitleModel
+ {
+ public string Title { get; set; }
+ }
+ }
+ }
+
+ public class NrkRadioSeriesLinks : NrkLinks
+ {
+ public List<Season> Seasons { get; set; }
+
+ public class Season
+ {
+ public string Name { get; set; }
+ public string Href { get; set; }
+ public string Title { get; set; }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Models/RadioSeries.cs b/src/Models/RadioSeries.cs
deleted file mode 100644
index 6bd4efe..0000000
--- a/src/Models/RadioSeries.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace I2R.LightNews.Models;
-
-public class RadioSeries
-{
- public string Name { get; set; }
- public string Description { get; set; }
- public string Type { get; set; }
- public Uri CanonicalUri { get; set; }
- public List<Episode> Episodes { get; set; }
-
- public class Episode
- {
- public string Title { get; set; }
- public string Subtitle { get; set; }
- public Uri SourceUri { get; set; }
- public Uri CanonicalUri { get; set; }
- }
-} \ No newline at end of file
diff --git a/src/Program.cs b/src/Program.cs
index ba011a6..7fd2903 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -1,7 +1,9 @@
global using I2R.LightNews.Services;
global using I2R.LightNews.Models;
global using IOL.Helpers;
+using System.Text.Json.Serialization;
using I2R.LightNews;
+using Microsoft.AspNetCore.Http.Json;
var builder = WebApplication.CreateBuilder(args);
@@ -10,6 +12,10 @@ builder.Services.AddHttpClient<NrkNewsService>();
builder.Services.AddMemoryCache();
builder.Services.AddScoped<NrkNewsService>();
builder.Services.AddScoped<NrkRadioService>();
+builder.Services.Configure<JsonOptions>(options => {
+ options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
+ options.SerializerOptions.PropertyNameCaseInsensitive = true;
+});
builder.Services.AddControllers();
builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
diff --git a/src/RadioIndexDb.cs b/src/RadioIndexDb.cs
index f96c4af..e69ca4b 100644
--- a/src/RadioIndexDb.cs
+++ b/src/RadioIndexDb.cs
@@ -9,27 +9,58 @@ public static class RadioIndexDb
{
private static readonly string ConnectionString = "data source=AppData/radio-index.db";
- public static void AddEntry(RadioSeries entry) {
+ public static int AddSeries(RadioSeries entry) {
using var db = new SqliteConnection(ConnectionString);
- if (!db.TableExists("series")) return;
- var addOperation = db.Execute(@"insert series(name,description,canonical_url,episodes) values (@name,@description,@canonical_url,@episodes)", new {
+ if (!db.TableExists("series")) return -1;
+ return db.ExecuteScalar<int>(@"
+insert into series(name,description,canonical_url,nrk_id) values (@name,@description,@canonical_url,@nrk_id);
+select last_insert_rowid();", new {
name = entry.Name,
description = entry.Description,
- canonical_url = entry.CanonicalUri,
- episodes = JsonSerializer.Serialize(entry.Episodes)
+ canonical_url = entry.CanonicalUrl,
+ nrk_id = entry.NrkId
});
- if (addOperation == 0) Console.WriteLine("No rows were added");
}
-
- public static void DeleteEntry(int id) { }
- public static RadioSeries GetEntry(int id) {
+ public static int AddSeason(RadioSeason entry) {
+ using var db = new SqliteConnection(ConnectionString);
+ if (!db.TableExists("seasons")) return -1;
+ return db.ExecuteScalar<int>(@"
+insert into seasons(name,description,canonical_url,nrk_id,series_id) values (@name,@description,@canonical_url,@nrk_id,@series_id);
+select last_insert_rowid();", new {
+ name = entry.Name,
+ description = entry.Description,
+ canonical_url = entry.CanonicalUrl,
+ nrk_id = entry.NrkId,
+ series_id = entry.SeriesId
+ });
+ }
+
+ public static int AddEpisode(RadioEpisode entry) {
+ using var db = new SqliteConnection(ConnectionString);
+ if (!db.TableExists("episodes")) return -1;
+ return db.ExecuteScalar<int>(@"
+insert into episodes(title,subtitle,canonical_url,nrk_id,series_id,season_id,source_url) values (@title,@subtitle,@canonical_url,@nrk_id,@series_id,@season_id,@source_url);
+select last_insert_rowid();", new {
+ title = entry.Title,
+ subtitle = entry.Subtitle,
+ canonical_url = entry.CanonicalUrl,
+ nrk_id = entry.NrkId,
+ series_id = entry.SeriesId,
+ season_id = entry.SeasonId,
+ source_url = entry.SourceUrl,
+ });
+ }
+
+ public static void DeleteSeries(int id) { }
+
+ public static RadioSeries GetSeries(int id) {
using var db = new SqliteConnection(ConnectionString);
if (!db.TableExists("series")) return default;
return db.QueryFirstOrDefault<RadioSeries>(@"select * from series where id=@id", new {id});
}
- public static List<RadioSeries> GetEntries(string query, bool includeEpisodes = false) {
+ public static List<RadioSeries> GetSeries(string query, bool includeEpisodes = false) {
using var db = new SqliteConnection(ConnectionString);
if (!db.TableExists("series")) return default;
var selectSet = includeEpisodes ? "*" : "id,name,description,type,canonical_url";
@@ -49,7 +80,35 @@ public static class RadioIndexDb
description text,
type text,
canonical_url text,
- episodes json
+ nrk_id text
+ )
+ ");
+ }
+
+ if (!db.TableExists("seasons")) {
+ db.Execute(@"
+ create table seasons(
+ id integer primary key autoincrement,
+ series_id integer,
+ name text,
+ description text,
+ canonical_url text,
+ nrk_id text
+ )
+ ");
+ }
+
+ if (!db.TableExists("episodes")) {
+ db.Execute(@"
+ create table episodes(
+ id integer primary key autoincrement,
+ series_id integer,
+ season_id integer,
+ name text,
+ description text,
+ canonical_url text,
+ source_url text,
+ nrk_id text
)
");
}
diff --git a/src/Services/NrkRadioService.cs b/src/Services/NrkRadioService.cs
index a2889ce..d4e06a5 100644
--- a/src/Services/NrkRadioService.cs
+++ b/src/Services/NrkRadioService.cs
@@ -7,23 +7,70 @@ public class NrkRadioService
private readonly IMemoryCache _cache;
private readonly HttpClient _http;
private const string CATEGORY_SEARCH_CACHE_KEY = "category_search";
+ private readonly ILogger<NrkRadioService> _logger;
- public NrkRadioService(IMemoryCache cache, HttpClient http) {
+ public NrkRadioService(IMemoryCache cache, HttpClient http, ILogger<NrkRadioService> logger) {
_cache = cache;
http.BaseAddress = new Uri("https://psapi.nrk.no");
_http = http;
+ _logger = logger;
}
- public async Task GetEverythingAsync() {
- var path = "/radio/search/categories/alt-innhold";
- var everything = new List<RadioSeries>();
- while (path.HasValue()) {
- var response = await _http.GetFromJsonAsync<RadioCategorySearchResult>(path);
-
+ public async Task CreateIndex() {
+ var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ".ToCharArray();
+ var skip = 0;
+ foreach (var letter in letters) {
+ var path = "/radio/search/categories/alt-innhold?letter=" + letter + "&skip=0&take=50";
+ while (path.HasValue()) {
+ var response = await _http.GetFromJsonAsync<RadioCategorySearchResult>(path);
+ if (response == default) break;
+ await Task.Delay(2000);
+ foreach (var series in response.Series) {
+ var dbSeries = new RadioSeries {
+ Name = series.Title,
+ NrkId = series.Id,
+ Type = series.Type,
+ };
+ var seriesId = RadioIndexDb.AddSeries(dbSeries);
+ _logger.LogInformation("Added series {0} with id {1}, to the database", dbSeries.Name, seriesId);
+ if (!series.Links.Series.Href.HasValue()) continue;
+ var seriesMetadata = await _http.GetFromJsonAsync<NrkRadioSeries>(series.Links.Series.Href);
+ if (seriesMetadata == default) continue;
+ await Task.Delay(1000);
+ foreach (var season in seriesMetadata.Embedded.Seasons) {
+ var dbSeason = new RadioSeason() {
+ Name = season.Titles.FirstOrDefault()?.Title,
+ NrkId = season.Id,
+ SeriesId = seriesId
+ };
+ var seasonId = RadioIndexDb.AddSeason(dbSeason);
+ _logger.LogInformation("Added season {0} to series {1} with id {2}, to the database", dbSeason.Name, dbSeries.Name, seasonId);
+ foreach (var episode in season.Episodes) {
+ foreach (var actuallyEpisode in episode.Embedded.Episodes) {
+ var dbEpisode = new RadioEpisode {
+ CanonicalUrl = actuallyEpisode.Links.Share.Href,
+ Title = actuallyEpisode.Titles.FirstOrDefault()?.Title,
+ Subtitle = actuallyEpisode.Titles.FirstOrDefault()?.Subtitle,
+ NrkId = actuallyEpisode.EpisodeId,
+ SeasonId = seasonId,
+ SeriesId = dbSeason.SeriesId
+ };
+ var playbackResponse = await _http.GetFromJsonAsync<NrkPlaybackManifest>("/playback/manifest/program/" + dbEpisode.NrkId);
+ if (playbackResponse == default) continue;
+ dbEpisode.SourceUrl = playbackResponse.Playable.Assets.FirstOrDefault()?.Url;
+ var episodeId = RadioIndexDb.AddEpisode(dbEpisode);
+ _logger.LogInformation("Added episode {0} to series {1} season {2} with id {3}, to the database", dbEpisode.Title, dbSeries.Name, dbSeason.Name, episodeId);
+ }
+ }
+ }
+ }
+
+ path = response.Links.NextPage.Href.HasValue() ? response.Links.NextPage.Href : "";
+ }
}
}
- public async Task<RadioCategorySearchResult> SearchCategoriesAsync(string query, int take = 50, int skip = 50) {
+ public async Task<RadioCategorySearchResult> SearchCategoriesAsync(string query, int take = 50, int skip = 0) {
return await _http.GetFromJsonAsync<RadioCategorySearchResult>(
"/radio/search/categories/alt-innhold?q=" + query + "&take=" + take + "&skip=" + skip
);