WIP: VCinema ASP.NET Core edition
This commit is contained in:
parent
fff2389a7b
commit
4078a211da
|
@ -1,15 +1,21 @@
|
||||||
# ---> VisualStudioCode
|
# globs
|
||||||
.vscode/*
|
Makefile.in
|
||||||
!.vscode/settings.json
|
*.userprefs
|
||||||
!.vscode/tasks.json
|
*.usertasks
|
||||||
!.vscode/launch.json
|
config.make
|
||||||
!.vscode/extensions.json
|
config.status
|
||||||
*.code-workspace
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# Mac bundle stuff
|
||||||
.history/
|
*.dmg
|
||||||
|
*.app
|
||||||
|
|
||||||
# ---> macOS
|
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||||
# General
|
# General
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
|
@ -38,25 +44,9 @@ Network Trash Folder
|
||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
# ---> Linux
|
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||||
*~
|
|
||||||
|
|
||||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
|
||||||
.fuse_hidden*
|
|
||||||
|
|
||||||
# KDE directory preferences
|
|
||||||
.directory
|
|
||||||
|
|
||||||
# Linux trash folder which might appear on any partition or disk
|
|
||||||
.Trash-*
|
|
||||||
|
|
||||||
# .nfs files are created when an open file is removed but is still being accessed
|
|
||||||
.nfs*
|
|
||||||
|
|
||||||
# ---> Windows
|
|
||||||
# Windows thumbnail cache files
|
# Windows thumbnail cache files
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Thumbs.db:encryptable
|
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
ehthumbs_vista.db
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
@ -79,145 +69,337 @@ $RECYCLE.BIN/
|
||||||
# Windows shortcuts
|
# Windows shortcuts
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
## Python
|
# content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# User-specific files
|
||||||
__pycache__/
|
*.suo
|
||||||
*.py[cod]
|
*.user
|
||||||
*$py.class
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
# C extensions
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.so
|
*.userprefs
|
||||||
|
|
||||||
# Distribution / packaging
|
# Build results
|
||||||
.Python
|
[Dd]ebug/
|
||||||
build/
|
[Dd]ebugPublic/
|
||||||
develop-eggs/
|
[Rr]elease/
|
||||||
dist/
|
[Rr]eleases/
|
||||||
downloads/
|
x64/
|
||||||
eggs/
|
x86/
|
||||||
.eggs/
|
bld/
|
||||||
lib/
|
[Bb]in/
|
||||||
lib64/
|
[Oo]bj/
|
||||||
parts/
|
[Ll]og/
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
# Usually these files are written by a python script from a template
|
.vs/
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
*.manifest
|
#wwwroot/
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
# Visual Studio 2017 auto generated files
|
||||||
pip-log.txt
|
Generated\ Files/
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
# MSTest test Results
|
||||||
htmlcov/
|
[Tt]est[Rr]esult*/
|
||||||
.tox/
|
[Bb]uild[Ll]og.*
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
# NUNIT
|
||||||
*.mo
|
*.VisualState.xml
|
||||||
*.pot
|
TestResult.xml
|
||||||
|
|
||||||
# Django stuff:
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
*.vspscc
|
||||||
db.sqlite3
|
*.vssscc
|
||||||
db.sqlite3-journal
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
# Flask stuff:
|
# Chutzpah Test files
|
||||||
instance/
|
_Chutzpah*
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
# Visual C++ cache files
|
||||||
.scrapy
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
# Sphinx documentation
|
# Visual Studio profiler
|
||||||
docs/_build/
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
# PyBuilder
|
# Visual Studio Trace Files
|
||||||
.pybuilder/
|
*.e2e
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
# TFS 2012 Local Workspace
|
||||||
.ipynb_checkpoints
|
$tf/
|
||||||
|
|
||||||
# IPython
|
# Guidance Automation Toolkit
|
||||||
profile_default/
|
*.gpState
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
# ReSharper is a .NET coding add-in
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
_ReSharper*/
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
*.[Rr]e[Ss]harper
|
||||||
# .python-version
|
*.DotSettings.user
|
||||||
|
|
||||||
# pipenv
|
# JustCode is a .NET coding add-in
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
.JustCode
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
# TeamCity is a build add-in
|
||||||
__pypackages__/
|
_TeamCity*
|
||||||
|
|
||||||
# Celery stuff
|
# DotCover is a Code Coverage Tool
|
||||||
celerybeat-schedule
|
*.dotCover
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
# AxoCover is a Code Coverage Tool
|
||||||
*.sage.py
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
# Environments
|
# Visual Studio code coverage results
|
||||||
.env
|
*.coverage
|
||||||
.venv
|
*.coveragexml
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
# NCrunch
|
||||||
.spyderproject
|
_NCrunch_*
|
||||||
.spyproject
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
# Rope project settings
|
# MightyMoose
|
||||||
.ropeproject
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
# mkdocs documentation
|
# Web workbench (sass)
|
||||||
/site
|
.sass-cache/
|
||||||
|
|
||||||
# mypy
|
# Installshield output folder
|
||||||
.mypy_cache/
|
[Ee]xpress/
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
# DocProject is a documentation generator add-in
|
||||||
.pyre/
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
# pytype static type analyzer
|
# Click-Once directory
|
||||||
.pytype/
|
publish/
|
||||||
|
|
||||||
# Cython debug symbols
|
# Publish Web Output
|
||||||
cython_debug/
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
.vscode/
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VCinemaApi", "VCinema\VCinemaApi.csproj", "{CF731C3C-2EB8-4C7A-B0BA-556B5CB74106}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{CF731C3C-2EB8-4C7A-B0BA-556B5CB74106}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CF731C3C-2EB8-4C7A-B0BA-556B5CB74106}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CF731C3C-2EB8-4C7A-B0BA-556B5CB74106}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CF731C3C-2EB8-4C7A-B0BA-556B5CB74106}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,111 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using VCinemaApi.Models;
|
||||||
|
|
||||||
|
namespace VCinemaApi.Hubs
|
||||||
|
{
|
||||||
|
public class VCinemaHub : Hub
|
||||||
|
{
|
||||||
|
private readonly VCinemaContext _context;
|
||||||
|
|
||||||
|
public VCinemaHub(VCinemaContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task OnConnectedAsync()
|
||||||
|
{
|
||||||
|
var watcher = new Watcher()
|
||||||
|
{
|
||||||
|
ConnectionId = Context.ConnectionId,
|
||||||
|
};
|
||||||
|
await _context.Watchers.AddAsync(watcher);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await base.OnConnectedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task OnDisconnectedAsync(Exception exception)
|
||||||
|
{
|
||||||
|
Watcher watcher = await _context.Watchers.SingleOrDefaultAsync(watcher => watcher.ConnectionId == Context.ConnectionId);
|
||||||
|
_context.Watchers.Remove(watcher);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await base.OnDisconnectedAsync(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GetScreens()
|
||||||
|
{
|
||||||
|
List<Screen> screens = await _context.Screens.ToListAsync();
|
||||||
|
await Clients.Caller.SendAsync("ReceiveScreens", screens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GetScreen(int screenId)
|
||||||
|
{
|
||||||
|
Screen screen = await _context.Screens.SingleOrDefaultAsync(screen => screen.ScreenId == screenId);
|
||||||
|
if (screen == null)
|
||||||
|
{
|
||||||
|
throw new HubException($"No screen with ID {screenId} found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateScreen(string name, string source)
|
||||||
|
{
|
||||||
|
var screen = new Screen()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Source = source
|
||||||
|
};
|
||||||
|
await _context.AddAsync<Screen>(screen);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var screens = await _context.Screens.ToListAsync();
|
||||||
|
await Clients.All.SendAsync("ReceiveScreens", screens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task JoinScreen(int screenId)
|
||||||
|
{
|
||||||
|
Screen screen = await _context.Screens.SingleOrDefaultAsync(screen => screen.ScreenId == screenId);
|
||||||
|
if (screen == null)
|
||||||
|
{
|
||||||
|
throw new HubException($"No screen with ID {screenId} found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Watcher watcher = await _context.Watchers.SingleOrDefaultAsync(watcher => watcher.ConnectionId == Context.ConnectionId);
|
||||||
|
if (watcher == null)
|
||||||
|
{
|
||||||
|
throw new HubException($"No watcher with connection ID {Context.ConnectionId} found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher.Screen = screen;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await Groups.AddToGroupAsync(Context.ConnectionId, screenId.ToString());
|
||||||
|
await Clients.Caller.SendAsync("ReceiveScreen", screen);
|
||||||
|
|
||||||
|
/* Send new watcher notification of watchers to screen. */
|
||||||
|
await Clients.Group(screenId.ToString()).SendAsync("NewWatcher", watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LeaveScreen()
|
||||||
|
{
|
||||||
|
Watcher watcher = await _context.Watchers.SingleOrDefaultAsync(watcher => watcher.ConnectionId == Context.ConnectionId);
|
||||||
|
if (watcher == null)
|
||||||
|
{
|
||||||
|
throw new HubException($"No watcher with connection ID {Context.ConnectionId} found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int screenId = watcher.Screen.ScreenId;
|
||||||
|
if (screenId == 0)
|
||||||
|
{
|
||||||
|
throw new HubException($"Watcher is not joined to any screens.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Groups.RemoveFromGroupAsync(Context.ConnectionId, screenId.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace VCinemaApi.Models
|
||||||
|
{
|
||||||
|
public class Screen
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int ScreenId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Source { get; set; }
|
||||||
|
|
||||||
|
public DateTime PlayStateUpdated { get; set; }
|
||||||
|
public bool PlayState { get; set; }
|
||||||
|
public int PlayOffset { get; set; }
|
||||||
|
|
||||||
|
public ICollection<Watcher> Watchers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
namespace VCinemaApi.Models
|
||||||
|
{
|
||||||
|
public class VCinemaContext : DbContext
|
||||||
|
{
|
||||||
|
public VCinemaContext(DbContextOptions<VCinemaContext> options): base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<Watcher> Watchers { get; set; }
|
||||||
|
public DbSet<Screen> Screens { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace VCinemaApi.Models
|
||||||
|
{
|
||||||
|
public class Watcher
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int WatcherId { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string ConnectionId { get; set; }
|
||||||
|
|
||||||
|
public Screen Screen { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace VCinemaApi
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:36425",
|
||||||
|
"sslPort": 44359
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "weatherforecast",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"VCinema": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "weatherforecast",
|
||||||
|
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
namespace VCinemaApi.Repositories
|
||||||
|
{
|
||||||
|
public class ScreenRepository
|
||||||
|
{
|
||||||
|
public ScreenRepository()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
namespace VCinemaApi.Repositories
|
||||||
|
{
|
||||||
|
public class WatcherRepository
|
||||||
|
{
|
||||||
|
public WatcherRepository()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using VCinemaApi.Hubs;
|
||||||
|
using VCinemaApi.Models;
|
||||||
|
|
||||||
|
namespace VCinemaApi
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddDbContext<VCinemaContext>(options => options.UseInMemoryDatabase("VCinema"));
|
||||||
|
services.AddControllers();
|
||||||
|
services.AddSignalR();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
//app.UseHttpsRedirection();
|
||||||
|
app.UseDefaultFiles();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapHub<VCinemaHub>("/vcinemahub");
|
||||||
|
endpoints.MapControllers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
<Folder Include="Hubs\" />
|
||||||
|
<Folder Include="wwwroot\" />
|
||||||
|
<Folder Include="wwwroot\js\" />
|
||||||
|
<Folder Include="Repositories\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.8" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.8" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.4" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.8" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.8">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.8" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"defaultProvider": "unpkg",
|
||||||
|
"libraries": [
|
||||||
|
{
|
||||||
|
"library": "@microsoft/signalr@latest",
|
||||||
|
"destination": "wwwroot/js/signalr",
|
||||||
|
"files": [
|
||||||
|
"dist/browser/signalr.js",
|
||||||
|
"dist/browser/signalr.min.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title></title>
|
||||||
|
<script src="/js/signalr/dist/browser/signalr.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Hello, World! :-)
|
||||||
|
<script>
|
||||||
|
var connection = new signalR.HubConnectionBuilder().withUrl("/vcinemahub").build();
|
||||||
|
|
||||||
|
connection.on("GetScreens", function (obj) {
|
||||||
|
console.log(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.start();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,3 +0,0 @@
|
||||||
Flask>=1.1.2
|
|
||||||
Flask-RESTful>=0.3.8
|
|
||||||
Flask-SQLAlchemy>=2.4.4
|
|
28
setup.py
28
setup.py
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
|
|
||||||
with open("requirements.txt") as f:
|
|
||||||
requirements = f.readlines()
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="vcinema",
|
|
||||||
version="0.1",
|
|
||||||
description="",
|
|
||||||
author="Butlersaurus",
|
|
||||||
packages=[
|
|
||||||
"vcinema",
|
|
||||||
"vcinema.dto",
|
|
||||||
"vcinema.models",
|
|
||||||
"vcinema.resources",
|
|
||||||
],
|
|
||||||
entry_points={
|
|
||||||
"console_scripts" : [
|
|
||||||
"vcinema = vcinema.app:main"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
install_requires=requirements,
|
|
||||||
test_suite="nose.collector"
|
|
||||||
)
|
|
|
@ -1,30 +0,0 @@
|
||||||
from flask import Flask
|
|
||||||
from flask_restful import Api
|
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite://"
|
|
||||||
db = SQLAlchemy(app)
|
|
||||||
api = Api(app)
|
|
||||||
|
|
||||||
|
|
||||||
def init_db():
|
|
||||||
from vcinema.models.room import Room
|
|
||||||
from vcinema.models.user import User
|
|
||||||
db.create_all()
|
|
||||||
|
|
||||||
def init_resources():
|
|
||||||
from vcinema.resources.rooms import RoomApi, RoomsApi
|
|
||||||
from vcinema.resources.users import UsersApi
|
|
||||||
api.add_resource(RoomApi, "/rooms/<id>")
|
|
||||||
api.add_resource(RoomsApi, "/rooms")
|
|
||||||
api.add_resource(UsersApi, "/users")
|
|
||||||
# api.add_resource(UserAPI, "/users/<id>")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
init_db()
|
|
||||||
init_resources()
|
|
||||||
app.run(debug=True)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,9 +0,0 @@
|
||||||
class RoomDto:
|
|
||||||
@staticmethod
|
|
||||||
def to_dict(room):
|
|
||||||
return {
|
|
||||||
"id": room.id,
|
|
||||||
"name": room.name,
|
|
||||||
"source": room.source,
|
|
||||||
"users": [{"id": user.id, "name": user.name} for user in room.users]
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
class UserDto:
|
|
||||||
@staticmethod
|
|
||||||
def to_dict(user):
|
|
||||||
return {
|
|
||||||
"id": user.id,
|
|
||||||
"name": user.name,
|
|
||||||
"token": user.token,
|
|
||||||
"room": user.room.id
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
from vcinema.app import db
|
|
||||||
|
|
||||||
|
|
||||||
class Room(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
name = db.Column(db.String(20), unique=True, nullable=False)
|
|
||||||
source = db.Column(db.String(200), unique=True, nullable=False)
|
|
||||||
users = db.relationship("User", backref="room", cascade="all, delete-orphan")
|
|
|
@ -1,8 +0,0 @@
|
||||||
from vcinema.app import db
|
|
||||||
|
|
||||||
|
|
||||||
class User(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
name = db.Column(db.String(20), unique=True, nullable=False)
|
|
||||||
token = db.Column(db.String(36), unique=True, nullable=False)
|
|
||||||
room_id = db.Column(db.Integer, db.ForeignKey("room.id"), nullable=False)
|
|
|
@ -1,73 +0,0 @@
|
||||||
from flask_restful import abort, request, Resource
|
|
||||||
from sqlalchemy.exc import DatabaseError
|
|
||||||
|
|
||||||
from vcinema.app import db
|
|
||||||
from vcinema.models.room import Room
|
|
||||||
from vcinema.dto.room import RoomDto
|
|
||||||
|
|
||||||
|
|
||||||
class RoomsApi(Resource):
|
|
||||||
def get(self):
|
|
||||||
try:
|
|
||||||
rooms = Room.query.all()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst querying the database", exception=str(e))
|
|
||||||
|
|
||||||
return [RoomDto.to_dict(room) for room in rooms], 200
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
req = request.get_json(force=True)
|
|
||||||
name = req.get("name")
|
|
||||||
source = req.get("source")
|
|
||||||
|
|
||||||
if not name or not source:
|
|
||||||
abort(400, status="error", message="Insufficient parameters specified")
|
|
||||||
|
|
||||||
try:
|
|
||||||
room = Room(name=name, source=source)
|
|
||||||
db.session.add(room)
|
|
||||||
db.session.commit()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst writing to the database", exception=str(e))
|
|
||||||
|
|
||||||
return RoomDto.to_dict(room), 201
|
|
||||||
|
|
||||||
|
|
||||||
class RoomApi(Resource):
|
|
||||||
def get(self, id):
|
|
||||||
try:
|
|
||||||
id = int(id)
|
|
||||||
except ValueError:
|
|
||||||
abort(400, status="error", message="ID must be an integer")
|
|
||||||
|
|
||||||
try:
|
|
||||||
room = Room.query.filter_by(id=id).first()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst querying the database", exception=str(e))
|
|
||||||
|
|
||||||
if not room:
|
|
||||||
abort(404, status="error", message=f"Room with ID {id} not found")
|
|
||||||
|
|
||||||
return RoomDto.to_dict(room), 200
|
|
||||||
|
|
||||||
def delete(self, id):
|
|
||||||
try:
|
|
||||||
id = int(id)
|
|
||||||
except ValueError:
|
|
||||||
abort(400, status="error", message="ID must be an integer")
|
|
||||||
|
|
||||||
try:
|
|
||||||
room = Room.query.filter_by(id=id).first()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst querying the database", exception=str(e))
|
|
||||||
|
|
||||||
if not room:
|
|
||||||
abort(404, status="error", message=f"Room with ID {id} not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
db.session.delete(room)
|
|
||||||
db.session.commit()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst deleting a record from the database", exception=str(e))
|
|
||||||
|
|
||||||
return "", 204
|
|
|
@ -1,41 +0,0 @@
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from flask_restful import abort, request, Resource
|
|
||||||
from sqlalchemy.exc import DatabaseError
|
|
||||||
|
|
||||||
from vcinema.app import db
|
|
||||||
from vcinema.models.room import Room
|
|
||||||
from vcinema.models.user import User
|
|
||||||
from vcinema.dto.user import UserDto
|
|
||||||
|
|
||||||
|
|
||||||
class UsersApi(Resource):
|
|
||||||
def post(self):
|
|
||||||
req = request.get_json(force=True)
|
|
||||||
name = req.get("name")
|
|
||||||
room_id = req.get("room-id")
|
|
||||||
|
|
||||||
if not name or not room_id:
|
|
||||||
abort(400, status="error", message="Insufficient parameters specified")
|
|
||||||
|
|
||||||
try:
|
|
||||||
room_id = int(room_id)
|
|
||||||
except ValueError:
|
|
||||||
abort(400, status="error", message="Room ID must be an integer")
|
|
||||||
|
|
||||||
try:
|
|
||||||
room = Room.query.filter_by(id=room_id).first()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst querying the database", exception=str(e))
|
|
||||||
|
|
||||||
if not room:
|
|
||||||
abort(400, status="error", message=f"Room with ID {room_id} not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User(name=name, token=str(uuid4()), room=room)
|
|
||||||
db.session.add(user)
|
|
||||||
db.session.commit()
|
|
||||||
except DatabaseError as e:
|
|
||||||
abort(500, status="error", message="An error occured whilst writing to the database", exception=str(e))
|
|
||||||
|
|
||||||
return UserDto.to_dict(user), 201
|
|
Loading…
Reference in New Issue