14 Commits

Author SHA1 Message Date
philipp.haefelfinger 8d55bfd2ff updates b uild process for new drone version
continuous-integration/drone/push Build is passing
2020-02-06 23:55:30 +01:00
philipp.haefelfinger f92b5a29d3 updates copyright and license info 2020-02-06 23:45:56 +01:00
philipp.haefelfinger 5fcc4afcd1 updates the Dockerfile to build in aspnet core 3.1 2020-02-06 23:38:57 +01:00
philipp.haefelfinger b71807cf16 adds some warnings about the datatypechange in sqlite 2020-02-06 23:13:15 +01:00
philipp.haefelfinger b89ae349ca addes migration for sqlite to make sure the new id field is text 2020-02-06 23:09:24 +01:00
philipp.haefelfinger cef19f9a22 upgrades the main application to .net core 3.1 2020-02-06 22:05:06 +01:00
philipp.haefelfinger 94802dc123 updates project to net standard 2.1 2020-02-06 22:01:33 +01:00
philipp.haefelfinger 7f824ee792 updates gitignore and adds *.blob and TestResults 2020-02-06 21:58:50 +01:00
philipp.haefelfinger a72a8cc8a9 updated EF nuget packages to 2.2.6
continuous-integration/drone/push Build is passing
2019-10-24 22:12:59 +02:00
philipp.haefelfinger 501e81c2c5 added tag 2.0.1 to dockerfile
continuous-integration/drone/push Build is passing
2019-06-04 22:58:33 +02:00
philipp.haefelfinger c8e3c1b9f5 updated readme 2019-06-04 22:48:15 +02:00
philipp.haefelfinger 0b818452b0 Added mass disable / enable 2019-06-04 22:46:26 +02:00
philipp.haefelfinger f190d3b5af added emby as hint 2019-06-04 22:12:05 +02:00
philipp.haefelfinger babd41055f updated packages 2019-06-04 21:46:42 +02:00
16 changed files with 365 additions and 110 deletions
+19 -11
View File
@@ -1,12 +1,20 @@
pipeline:
docker:
kind: pipeline
name: default
steps:
- name: docker
image: plugins/docker
repo: phaefelfinger/tv7playlist
tags:
- latest
- 2.0
secrets: [ docker_username, docker_password ]
debug: true
when:
event: push
branch: master
settings:
repo: phaefelfinger/tv7playlist
tags:
- latest
- '3.0'
- '3.0.0'
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
branch:
- master
+1 -1
View File
@@ -92,7 +92,6 @@ AppPackages/
[Bb]in
[Oo]bj
sql
TestResults
[Tt]est[Rr]esult*
*.Cache
ClientBin
@@ -107,3 +106,4 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
*.blob
+3 -3
View File
@@ -1,4 +1,4 @@
FROM microsoft/dotnet:sdk AS build-env
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /app
# Copy everything else and build
@@ -7,12 +7,12 @@ RUN dotnet restore Tv7Playlist.sln
RUN dotnet publish -c Release -o out Tv7Playlist.sln
# Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
WORKDIR /app
RUN mkdir /data
COPY --from=build-env /app/Tv7Playlist/out .
COPY --from=build /app/out .
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80
+14 -2
View File
@@ -12,11 +12,23 @@ There are more features than just changing the URL:
- Resorting of the channel list
- Enable or disable a channel
- Override the channel number -> better EPG Detection support in plex
- Override the channel name -> better EPG Detection support in plex
- Enable or disable multiple channels at once
- Override the channel number -> better EPG Detection support in plex / emby
- Override the channel name -> better EPG Detection support in plex / emby
This is licensed under GPLv2. See License file.
## Possible breaking change of .net core 3.1.1 upgrade
Something changed within the entity framework driver for SqLite. A guid is now stored as a text and not blob anymore.
With the wrong datatype, you will get an error if you try to update a record.
The latest version of this application has a migration built in that should convert the blobs to the values as text.
The migrations are applied automatically.
**Backup your database before starting the new version**
A workaround might be clearing your database and rebuilding it by syncing the latest playlist from init7.
## Docker
### Run the application
+3 -3
View File
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,75 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Tv7Playlist.Data;
namespace Tv7Playlist.Data.Migrations
{
[DbContext(typeof(PlaylistContext))]
[Migration("20200206214445_DotnetCore3_1_upgrade")]
partial class DotnetCore3_1_upgrade
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.1");
modelBuilder.Entity("Tv7Playlist.Data.PlaylistEntry", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("ChannelNumberExport")
.HasColumnType("INTEGER");
b.Property<int>("ChannelNumberImport")
.HasColumnType("INTEGER");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("EpgMatchName")
.HasColumnType("TEXT");
b.Property<bool>("IsAvailable")
.HasColumnType("INTEGER");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<string>("LogoUrl")
.HasColumnType("TEXT");
b.Property<DateTime>("Modified")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("Position")
.HasColumnType("INTEGER");
b.Property<string>("UrlOriginal")
.HasColumnType("TEXT");
b.Property<string>("UrlProxy")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ChannelNumberImport")
.IsUnique();
b.HasIndex("Name");
b.ToTable("PlaylistEntries");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,73 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tv7Playlist.Data.Migrations
{
public partial class DotnetCore3_1_upgrade : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
"PlaylistEntriesTemp",
table => new
{
Id = table.Column<Guid>(),
Position = table.Column<int>(),
ChannelNumberImport = table.Column<int>(),
ChannelNumberExport = table.Column<int>(),
Name = table.Column<string>(nullable: true),
EpgMatchName = table.Column<string>(nullable: true),
UrlProxy = table.Column<string>(nullable: true),
UrlOriginal = table.Column<string>(nullable: true),
LogoUrl = table.Column<string>(nullable: true),
IsAvailable = table.Column<bool>(),
IsEnabled = table.Column<bool>(),
Created = table.Column<DateTime>(),
Modified = table.Column<DateTime>()
});
migrationBuilder.Sql("INSERT INTO PlaylistEntriesTemp SELECT * From PlaylistEntries");
migrationBuilder.DropTable("PlaylistEntries");
migrationBuilder.CreateTable(
"PlaylistEntries",
table => new
{
Id = table.Column<Guid>(),
Position = table.Column<int>(),
ChannelNumberImport = table.Column<int>(),
ChannelNumberExport = table.Column<int>(),
Name = table.Column<string>(nullable: true),
EpgMatchName = table.Column<string>(nullable: true),
UrlProxy = table.Column<string>(nullable: true),
UrlOriginal = table.Column<string>(nullable: true),
LogoUrl = table.Column<string>(nullable: true),
IsAvailable = table.Column<bool>(),
IsEnabled = table.Column<bool>(),
Created = table.Column<DateTime>(),
Modified = table.Column<DateTime>()
},
constraints: table => { table.PrimaryKey("PK_PlaylistEntries", x => x.Id); });
migrationBuilder.Sql("INSERT INTO PlaylistEntries SELECT * From PlaylistEntriesTemp");
migrationBuilder.CreateIndex(
"IX_PlaylistEntries_ChannelNumberImport",
"PlaylistEntries",
"ChannelNumberImport",
unique: true);
migrationBuilder.CreateIndex(
"IX_PlaylistEntries_Name",
"PlaylistEntries",
"Name");
migrationBuilder.DropTable("PlaylistEntriesTemp");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -14,36 +14,49 @@ namespace Tv7Playlist.Data.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.1-servicing-10028");
.HasAnnotation("ProductVersion", "3.1.1");
modelBuilder.Entity("Tv7Playlist.Data.PlaylistEntry", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("ChannelNumberExport");
b.Property<int>("ChannelNumberExport")
.HasColumnType("INTEGER");
b.Property<int>("ChannelNumberImport");
b.Property<int>("ChannelNumberImport")
.HasColumnType("INTEGER");
b.Property<DateTime>("Created");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("EpgMatchName");
b.Property<string>("EpgMatchName")
.HasColumnType("TEXT");
b.Property<bool>("IsAvailable");
b.Property<bool>("IsAvailable")
.HasColumnType("INTEGER");
b.Property<bool>("IsEnabled");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<string>("LogoUrl");
b.Property<string>("LogoUrl")
.HasColumnType("TEXT");
b.Property<DateTime>("Modified");
b.Property<DateTime>("Modified")
.HasColumnType("TEXT");
b.Property<string>("Name");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<int>("Position");
b.Property<int>("Position")
.HasColumnType("INTEGER");
b.Property<string>("UrlOriginal");
b.Property<string>("UrlOriginal")
.HasColumnType("TEXT");
b.Property<string>("UrlProxy");
b.Property<string>("UrlProxy")
.HasColumnType("TEXT");
b.HasKey("Id");
+4 -5
View File
@@ -1,15 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>latest</LangVersion>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.1" />
</ItemGroup>
</Project>
+35 -3
View File
@@ -12,9 +12,9 @@ namespace Tv7Playlist.Controllers
{
public class HomeController : Controller
{
private readonly IAppConfig _appConfig;
private readonly PlaylistContext _playlistContext;
private readonly IPlaylistSynchronizer _playlistSynchronizer;
private readonly IAppConfig _appConfig;
public HomeController(PlaylistContext playlistContext, IPlaylistSynchronizer playlistSynchronizer, IAppConfig appConfig)
{
@@ -26,12 +26,29 @@ namespace Tv7Playlist.Controllers
[HttpGet]
public async Task<IActionResult> Index()
{
var playlistEntries = await _playlistContext.PlaylistEntries.AsNoTracking().OrderBy(e => e.Position).ToListAsync();
var playlistEntries = await _playlistContext.PlaylistEntries.AsNoTracking().OrderBy(e => e.Position)
.Select(e => new PlaylistEntryModel(e)).ToListAsync();
var model = new HomeModel(playlistEntries);
return View(model);
}
[HttpPost]
public async Task<IActionResult> DisableSelectedEntries([FromForm] HomeModel model)
{
if (ModelState.IsValid) await UpdateEnabledForItems(model, false);
return Redirect("/");
}
[HttpPost]
public async Task<IActionResult> EnableSelectedEntries([FromForm] HomeModel model)
{
if (ModelState.IsValid) await UpdateEnabledForItems(model, true);
return Redirect("/");
}
[HttpGet]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
@@ -54,8 +71,23 @@ namespace Tv7Playlist.Controllers
public async Task<IActionResult> Synchronize(bool ok)
{
await _playlistSynchronizer.SynchronizeAsync();
return RedirectToAction("Index", "Home");
}
private async Task UpdateEnabledForItems(HomeModel model, bool isEnabled)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var idsToUpdate = model.PlaylistEntries.Where(e => e.Selected).Select(e => e.Id);
foreach (var id in idsToUpdate)
{
var entry = await _playlistContext.PlaylistEntries.FindAsync(id);
if (entry == null) continue;
entry.IsEnabled = isEnabled;
}
await _playlistContext.SaveChangesAsync();
}
}
}
+6 -3
View File
@@ -1,16 +1,19 @@
using System;
using System.Collections.Generic;
using Tv7Playlist.Data;
namespace Tv7Playlist.Models
{
public class HomeModel
{
public HomeModel(List<PlaylistEntry> playlistEntries)
public HomeModel()
{
}
public HomeModel(List<PlaylistEntryModel> playlistEntries)
{
PlaylistEntries = playlistEntries ?? throw new ArgumentNullException(nameof(playlistEntries));
}
public List<PlaylistEntry> PlaylistEntries { get; }
public List<PlaylistEntryModel> PlaylistEntries { get; set; }
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using Tv7Playlist.Data;
namespace Tv7Playlist.Models
{
public class PlaylistEntryModel
{
public PlaylistEntryModel()
{
}
public PlaylistEntryModel(PlaylistEntry entry)
{
Entry = entry ?? throw new ArgumentNullException(nameof(entry));
Id = entry.Id;
}
public Guid Id { get; set; }
public PlaylistEntry Entry { get; set; }
public bool Selected { get; set; }
}
}
+8 -7
View File
@@ -2,10 +2,10 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Tv7Playlist.Core;
using Tv7Playlist.Core.Parsers;
@@ -43,11 +43,11 @@ namespace Tv7Playlist
ConfigureParser(services, appConfig);
ConfigureDatabase(services, appConfig);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
@@ -67,11 +67,12 @@ namespace Tv7Playlist
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
+4 -5
View File
@@ -1,16 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.1" />
</ItemGroup>
+68 -52
View File
@@ -3,56 +3,72 @@
ViewData["Title"] = "TV7 Playlist";
}
<div class="row">
<div class="col col-12">
<table class="table table-hover table-striped">
<tr>
<th></th>
<th>Number Import</th>
<th>Number Export</th>
<th>Position</th>
<th>Name</th>
<th>EPG Name</th>
<th>Enabled</th>
<th>Available</th>
<th>URL Proxy</th>
<th>URL Original</th>
<th>Created</th>
<th>Modified</th>
</tr>
@{
foreach (var channel in Model.PlaylistEntries)
{
<tr>
<td>
<a class="btn btn-secondary" asp-area="" asp-controller="PlaylistEntry" asp-action="Edit" asp-route-id="@channel.Id">Edit</a>
<a class="btn btn-danger" asp-area="" asp-controller="PlaylistEntry" asp-action="Delete" asp-route-id="@channel.Id">Delete</a>
@{
if (channel.IsEnabled)
{
<a class="btn btn-warning" asp-area="" asp-controller="PlaylistEntry" asp-action="ToggleEnabled" asp-route-id="@channel.Id">Disable</a>
}
else
{
<a class="btn btn-info" asp-area="" asp-controller="PlaylistEntry" asp-action="ToggleEnabled" asp-route-id="@channel.Id">Enable</a>
}
}
</td>
<td>@channel.ChannelNumberImport</td>
<td>@channel.ChannelNumberExport</td>
<td>@channel.Position</td>
<td>@channel.Name</td>
<td>@channel.EpgMatchName</td>
<td class="text-center">@Html.Raw(channel.IsEnabled ? "<span class=\"text-primary\">Enabled</span>" : "<span class=\"text-danger\">Disabled</span>")</td>
<td class="text-center">@Html.Raw(channel.IsAvailable ? "<span class=\"text-primary\">yes</span>" : "<span class=\"text-danger\">no</span>")</td>
<td>@channel.UrlProxy</td>
<td>@channel.UrlOriginal</td>
<td>@channel.Created.ToString("g")</td>
<td>@channel.Modified.ToString("g")</td>
</tr>
}
}
</table>
<form method="post">
<div class="row">
<div class="col col-4">
<button class="btn btn-warning" asp-action="DisableSelectedEntries" asp-controller="Home">Disable selected</button>
<button class="btn btn-info" asp-action="EnableSelectedEntries" asp-controller="Home">Enable selected</button>
</div>
</div>
</div>
<div class="row">
<div class="col col-12">
<table class="table table-hover table-striped">
<tr>
<th>Selected</th>
<th>Single Action</th>
<th>Number Import</th>
<th>Number Export</th>
<th>Position</th>
<th>Name</th>
<th>EPG Name</th>
<th>Enabled</th>
<th>Available</th>
<th>URL Proxy</th>
<th>URL Original</th>
<th>Created</th>
<th>Modified</th>
</tr>
@{
for (var i = 0; i < Model.PlaylistEntries.Count; i++)
{
@Html.HiddenFor(m => m.PlaylistEntries[i].Id)
<tr>
<td>
<input asp-for="PlaylistEntries[i].Selected" type="checkbox" />
</td>
<td>
<a class="btn btn-secondary" asp-area="" asp-controller="PlaylistEntry" asp-action="Edit" asp-route-id="@Model.PlaylistEntries[i].Id">Edit</a>
<a class="btn btn-danger" asp-area="" asp-controller="PlaylistEntry" asp-action="Delete" asp-route-id="@Model.PlaylistEntries[i].Id">Delete</a>
@{
if (Model.PlaylistEntries[i].Entry.IsEnabled)
{
<a class="btn btn-warning" asp-area="" asp-controller="PlaylistEntry" asp-action="ToggleEnabled" asp-route-id="@Model.PlaylistEntries[i].Id">Disable</a>
}
else
{
<a class="btn btn-info" asp-area="" asp-controller="PlaylistEntry" asp-action="ToggleEnabled" asp-route-id="@Model.PlaylistEntries[i].Id">Enable</a>
}
}
</td>
<td>@Model.PlaylistEntries[i].Entry.ChannelNumberImport</td>
<td>@Model.PlaylistEntries[i].Entry.ChannelNumberExport</td>
<td>@Model.PlaylistEntries[i].Entry.Position</td>
<td>@Model.PlaylistEntries[i].Entry.Name</td>
<td>@Model.PlaylistEntries[i].Entry.EpgMatchName</td>
<td class="text-center">@Html.Raw(Model.PlaylistEntries[i].Entry.IsEnabled ? "<span class=\"text-primary\">Enabled</span>" : "<span class=\"text-danger\">Disabled</span>")</td>
<td class="text-center">@Html.Raw(Model.PlaylistEntries[i].Entry.IsAvailable ? "<span class=\"text-primary\">yes</span>" : "<span class=\"text-danger\">no</span>")</td>
<td>@Model.PlaylistEntries[i].Entry.UrlProxy</td>
<td>@Model.PlaylistEntries[i].Entry.UrlOriginal</td>
<td>@Model.PlaylistEntries[i].Entry.Created.ToString("g")</td>
<td>@Model.PlaylistEntries[i].Entry.Modified.ToString("g")</td>
</tr>
}
}
</table>
</div>
</div>
</form>
+1 -1
View File
@@ -51,7 +51,7 @@
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2018 - Philipp H&auml;felfinger - Tv7Playlist
&copy; 2018-2020 - Philipp H&auml;felfinger - Licensed under GPLv2 - Tv7Playlist
</div>
</footer>