mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Removing the web app files
This commit is contained in:
parent
04beaf4c49
commit
b96b79f02d
35
Kyoo.sln
35
Kyoo.sln
@ -1,51 +1,16 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.29123.88
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kyoo", "Kyoo\Kyoo.csproj", "{0F8275B6-C7DD-42DF-A168-755C81B1C329}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kyoo", "Kyoo\Kyoo.csproj", "{0F8275B6-C7DD-42DF-A168-755C81B1C329}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit Tests", "Unit Tests\Unit Tests.csproj", "{CC8144B5-8868-4EA7-B171-4E47746ED003}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{CC8144B5-8868-4EA7-B171-4E47746ED003}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {2940DD02-D039-437D-B47B-169D9B83B16A}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_STYLE/@EntryValue">Tab</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
# Editor configuration, see https://editorconfig.org
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
max_line_length = off
|
|
||||||
trim_trailing_whitespace = false
|
|
46
Kyoo/ClientApp/.gitignore
vendored
46
Kyoo/ClientApp/.gitignore
vendored
@ -1,46 +0,0 @@
|
|||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# compiled output
|
|
||||||
/dist
|
|
||||||
/tmp
|
|
||||||
/out-tsc
|
|
||||||
# Only exists if Bazel was run
|
|
||||||
/bazel-out
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# profiling files
|
|
||||||
chrome-profiler-events*.json
|
|
||||||
speed-measure-plugin*.json
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
/.idea
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.c9/
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# IDE - VSCode
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.history/*
|
|
||||||
|
|
||||||
# misc
|
|
||||||
/.sass-cache
|
|
||||||
/connect.lock
|
|
||||||
/coverage
|
|
||||||
/libpeerconnection.log
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
testem.log
|
|
||||||
/typings
|
|
||||||
|
|
||||||
# System Files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
@ -1,27 +0,0 @@
|
|||||||
# Kyoo
|
|
||||||
|
|
||||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.1.
|
|
||||||
|
|
||||||
## Development server
|
|
||||||
|
|
||||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
|
||||||
|
|
||||||
## Code scaffolding
|
|
||||||
|
|
||||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
||||||
|
|
||||||
## Running end-to-end tests
|
|
||||||
|
|
||||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
|
||||||
|
|
||||||
## Further help
|
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
|
@ -1,133 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"version": 1,
|
|
||||||
"newProjectRoot": "projects",
|
|
||||||
"projects": {
|
|
||||||
"Kyoo": {
|
|
||||||
"projectType": "application",
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "",
|
|
||||||
"sourceRoot": "src",
|
|
||||||
"prefix": "app",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist",
|
|
||||||
"index": "src/index.html",
|
|
||||||
"main": "src/main.ts",
|
|
||||||
"polyfills": "src/polyfills.ts",
|
|
||||||
"tsConfig": "tsconfig.app.json",
|
|
||||||
"aot": false,
|
|
||||||
"assets": [
|
|
||||||
"src/assets"
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": [
|
|
||||||
"./node_modules/jquery/dist/jquery.min.js",
|
|
||||||
"./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
|
|
||||||
"./node_modules/hls.js/dist/hls.js",
|
|
||||||
"./src/libraries/subtitles.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "src/environments/environment.ts",
|
|
||||||
"with": "src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"optimization": true,
|
|
||||||
"outputHashing": "all",
|
|
||||||
"sourceMap": false,
|
|
||||||
"extractCss": true,
|
|
||||||
"namedChunks": false,
|
|
||||||
"aot": true,
|
|
||||||
"extractLicenses": true,
|
|
||||||
"vendorChunk": false,
|
|
||||||
"buildOptimizer": true,
|
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "3mb",
|
|
||||||
"maximumError": "5mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "6kb",
|
|
||||||
"maximumError": "10kb"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "Kyoo:build"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"browserTarget": "Kyoo:build:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "Kyoo:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "src/test.ts",
|
|
||||||
"polyfills": "src/polyfills.ts",
|
|
||||||
"tsConfig": "tsconfig.spec.json",
|
|
||||||
"karmaConfig": "karma.conf.js",
|
|
||||||
"assets": [
|
|
||||||
"src/favicon.ico",
|
|
||||||
"src/assets"
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.sass"
|
|
||||||
],
|
|
||||||
"scripts": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"builder": "@angular-devkit/build-angular:tslint",
|
|
||||||
"options": {
|
|
||||||
"tsConfig": [
|
|
||||||
"tsconfig.app.json",
|
|
||||||
"tsconfig.spec.json",
|
|
||||||
"e2e/tsconfig.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"**/node_modules/**"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"e2e": {
|
|
||||||
"builder": "@angular-devkit/build-angular:protractor",
|
|
||||||
"options": {
|
|
||||||
"protractorConfig": "e2e/protractor.conf.js",
|
|
||||||
"devServerTarget": "Kyoo:serve"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "Kyoo:serve:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
"defaultProject": "Kyoo"
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
|
||||||
# For additional information regarding the format and rule options, please see:
|
|
||||||
# https://github.com/browserslist/browserslist#queries
|
|
||||||
|
|
||||||
# You can see what browsers were selected by your queries by running:
|
|
||||||
# npx browserslist
|
|
||||||
|
|
||||||
> 0.5%
|
|
||||||
last 2 versions
|
|
||||||
Firefox ESR
|
|
||||||
not dead
|
|
||||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
|
@ -1,32 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Protractor configuration file, see link for more information
|
|
||||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
|
||||||
|
|
||||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type { import("protractor").Config }
|
|
||||||
*/
|
|
||||||
exports.config = {
|
|
||||||
allScriptsTimeout: 11000,
|
|
||||||
specs: [
|
|
||||||
'./src/**/*.e2e-spec.ts'
|
|
||||||
],
|
|
||||||
capabilities: {
|
|
||||||
'browserName': 'chrome'
|
|
||||||
},
|
|
||||||
directConnect: true,
|
|
||||||
baseUrl: 'http://localhost:4200/',
|
|
||||||
framework: 'jasmine',
|
|
||||||
jasmineNodeOpts: {
|
|
||||||
showColors: true,
|
|
||||||
defaultTimeoutInterval: 30000,
|
|
||||||
print: function() {}
|
|
||||||
},
|
|
||||||
onPrepare() {
|
|
||||||
require('ts-node').register({
|
|
||||||
project: require('path').join(__dirname, './tsconfig.json')
|
|
||||||
});
|
|
||||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,23 +0,0 @@
|
|||||||
import { AppPage } from './app.po';
|
|
||||||
import { browser, logging } from 'protractor';
|
|
||||||
|
|
||||||
describe('workspace-project App', () => {
|
|
||||||
let page: AppPage;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
page = new AppPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display welcome message', () => {
|
|
||||||
page.navigateTo();
|
|
||||||
expect(page.getTitleText()).toEqual('Welcome to Kyoo!');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
// Assert that there are no errors emitted from the browser
|
|
||||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
|
||||||
expect(logs).not.toContain(jasmine.objectContaining({
|
|
||||||
level: logging.Level.SEVERE,
|
|
||||||
} as logging.Entry));
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,11 +0,0 @@
|
|||||||
import { browser, by, element } from 'protractor';
|
|
||||||
|
|
||||||
export class AppPage {
|
|
||||||
navigateTo() {
|
|
||||||
return browser.get(browser.baseUrl) as Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTitleText() {
|
|
||||||
return element(by.css('app-root h1')).getText() as Promise<string>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../out-tsc/e2e",
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es5",
|
|
||||||
"types": [
|
|
||||||
"jasmine",
|
|
||||||
"jasminewd2",
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
// Karma configuration file, see link for more information
|
|
||||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: '',
|
|
||||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
|
||||||
plugins: [
|
|
||||||
require('karma-jasmine'),
|
|
||||||
require('karma-chrome-launcher'),
|
|
||||||
require('karma-jasmine-html-reporter'),
|
|
||||||
require('karma-coverage-istanbul-reporter'),
|
|
||||||
require('@angular-devkit/build-angular/plugins/karma')
|
|
||||||
],
|
|
||||||
client: {
|
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
|
||||||
},
|
|
||||||
coverageIstanbulReporter: {
|
|
||||||
dir: require('path').join(__dirname, './coverage/Kyoo'),
|
|
||||||
reports: ['html', 'lcovonly', 'text-summary'],
|
|
||||||
fixWebpackSourcePaths: true
|
|
||||||
},
|
|
||||||
reporters: ['progress', 'kjhtml'],
|
|
||||||
port: 9876,
|
|
||||||
colors: true,
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
autoWatch: true,
|
|
||||||
browsers: ['Chrome'],
|
|
||||||
singleRun: false,
|
|
||||||
restartOnFileChange: true
|
|
||||||
});
|
|
||||||
};
|
|
14517
Kyoo/ClientApp/package-lock.json
generated
14517
Kyoo/ClientApp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "kyoo",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"ng": "ng",
|
|
||||||
"start": "ng serve",
|
|
||||||
"build": "ng build",
|
|
||||||
"test": "ng test",
|
|
||||||
"lint": "ng lint",
|
|
||||||
"e2e": "ng e2e"
|
|
||||||
},
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@angular/animations": "^8.2.14",
|
|
||||||
"@angular/cdk": "^8.2.3",
|
|
||||||
"@angular/common": "^8.2.14",
|
|
||||||
"@angular/compiler": "^8.2.14",
|
|
||||||
"@angular/core": "^8.2.14",
|
|
||||||
"@angular/forms": "^8.2.14",
|
|
||||||
"@angular/material": "^8.2.3",
|
|
||||||
"@angular/platform-browser": "^8.2.14",
|
|
||||||
"@angular/platform-browser-dynamic": "^8.2.14",
|
|
||||||
"@angular/router": "^8.2.14",
|
|
||||||
"bootstrap": "^4.4.1",
|
|
||||||
"detect-browser": "^4.8.0",
|
|
||||||
"hammerjs": "^2.0.8",
|
|
||||||
"hls.js": "^0.12.4",
|
|
||||||
"jquery": "^3.4.1",
|
|
||||||
"popper.js": "^1.16.0",
|
|
||||||
"zone.js": "~0.9.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@angular-devkit/build-angular": "^0.803.21",
|
|
||||||
"@angular/cli": "^8.3.21",
|
|
||||||
"@angular/compiler-cli": "^8.2.14",
|
|
||||||
"@angular/language-service": "^8.2.14",
|
|
||||||
"@types/bootstrap": "^4.3.1",
|
|
||||||
"@types/hls.js": "^0.12.5",
|
|
||||||
"@types/jasmine": "~3.3.8",
|
|
||||||
"@types/jasminewd2": "^2.0.8",
|
|
||||||
"@types/jquery": "^3.3.31",
|
|
||||||
"@types/node": "~8.9.4",
|
|
||||||
"@types/video.js": "^7.3.3",
|
|
||||||
"codelyzer": "^5.2.1",
|
|
||||||
"jasmine-core": "~3.4.0",
|
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
|
||||||
"karma": "~4.1.0",
|
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
|
||||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
|
||||||
"karma-jasmine": "~2.0.1",
|
|
||||||
"karma-jasmine-html-reporter": "^1.5.1",
|
|
||||||
"protractor": "~5.4.0",
|
|
||||||
"ts-node": "~7.0.0",
|
|
||||||
"tslint": "~5.15.0",
|
|
||||||
"typescript": "~3.5.3"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
|
||||||
import { BrowseComponent } from './browse/browse.component';
|
|
||||||
import { CollectionComponent } from "./collection/collection.component";
|
|
||||||
import { NotFoundComponent } from './not-found/not-found.component';
|
|
||||||
import { PlayerComponent } from "./player/player.component";
|
|
||||||
import { SearchComponent } from "./search/search.component";
|
|
||||||
import { CollectionResolverService } from "./services/collection-resolver.service";
|
|
||||||
import { LibraryResolverService } from './services/library-resolver.service';
|
|
||||||
import { PeopleResolverService } from "./services/people-resolver.service";
|
|
||||||
import { SearchResolverService } from "./services/search-resolver.service";
|
|
||||||
import { ShowResolverService } from './services/show-resolver.service';
|
|
||||||
import { StreamResolverService } from "./services/stream-resolver.service";
|
|
||||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
|
||||||
{ path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } },
|
|
||||||
{ path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } },
|
|
||||||
{ path: "show/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } },
|
|
||||||
{ path: "collection/:collection-slug", component: CollectionComponent, resolve: { collection: CollectionResolverService } },
|
|
||||||
{ path: "people/:people-slug", component: CollectionComponent, resolve: { collection: PeopleResolverService } },
|
|
||||||
{ path: "watch/:item", component: PlayerComponent, resolve: { item: StreamResolverService } },
|
|
||||||
{ path: "search/:query", component: SearchComponent, resolve: { items: SearchResolverService } },
|
|
||||||
{ path: "**", component: NotFoundComponent }
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forRoot(routes,
|
|
||||||
{
|
|
||||||
scrollPositionRestoration: "enabled"
|
|
||||||
})],
|
|
||||||
exports: [RouterModule],
|
|
||||||
providers: [
|
|
||||||
LibraryResolverService,
|
|
||||||
ShowResolverService,
|
|
||||||
CollectionResolverService,
|
|
||||||
PeopleResolverService,
|
|
||||||
StreamResolverService,
|
|
||||||
SearchResolverService
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class AppRoutingModule { }
|
|
@ -1,35 +0,0 @@
|
|||||||
<header id="nav" style="height: 68px;">
|
|
||||||
<div class="fixed-top">
|
|
||||||
<nav id="toolbar" class="navbar navbar-dark bg-secondary">
|
|
||||||
<a class="navbar-brand nav-item ml-3" routerLink="/">
|
|
||||||
Kyoo
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ul class="navbar-nav flex-row">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" routerLink="/browse" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">All</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" *ngFor="let library of this.libraries">
|
|
||||||
<a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="navbar-nav flex-row ml-auto">
|
|
||||||
<li class="nav-item icon searchbar">
|
|
||||||
<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($event)"/>
|
|
||||||
<mat-icon matTooltipPosition="below" matTooltip="Search" (click)="openSearch()">search</mat-icon>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="icon" routerLink="/login" routerLinkActive="active" matTooltipPosition="below" matTooltip="Login">
|
|
||||||
<mat-icon>account_circle</mat-icon>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<mat-progress-bar *ngIf="this.isLoading" color="accent" mode="indeterminate"> </mat-progress-bar>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main >
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</main>
|
|
@ -1,86 +0,0 @@
|
|||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
@import "~bootstrap/scss/mixins/breakpoints";
|
|
||||||
|
|
||||||
.navbar
|
|
||||||
{
|
|
||||||
justify-content: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item
|
|
||||||
{
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
> a
|
|
||||||
{
|
|
||||||
outline: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link
|
|
||||||
{
|
|
||||||
padding: 12px;
|
|
||||||
color: rgba(255, 255, 255, 0.7) !important;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active
|
|
||||||
{
|
|
||||||
color: var(--accentColor) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand:hover
|
|
||||||
{
|
|
||||||
color: var(--accentColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchbar
|
|
||||||
{
|
|
||||||
border-radius: 30px;
|
|
||||||
|
|
||||||
> input
|
|
||||||
{
|
|
||||||
background: none !important;
|
|
||||||
color: white;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #cfcfcf;
|
|
||||||
width: 0;
|
|
||||||
padding: 0;
|
|
||||||
transition: width 0.4s ease-in-out;
|
|
||||||
|
|
||||||
&:focus, &.searching
|
|
||||||
{
|
|
||||||
width: 12rem;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
width: 20rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input::-webkit-search-cancel-button
|
|
||||||
{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.icon
|
|
||||||
{
|
|
||||||
padding: 8px;
|
|
||||||
display: inline-block;
|
|
||||||
opacity: 0.7;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Event, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
|
|
||||||
import * as $ from "jquery";
|
|
||||||
import { Location } from "@angular/common";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-root',
|
|
||||||
templateUrl: './app.component.html',
|
|
||||||
styleUrls: ['./app.component.scss']
|
|
||||||
})
|
|
||||||
export class AppComponent
|
|
||||||
{
|
|
||||||
libraries: Library[];
|
|
||||||
isLoading: boolean = false;
|
|
||||||
|
|
||||||
constructor(http: HttpClient, private router: Router, private location: Location)
|
|
||||||
{
|
|
||||||
http.get<Library[]>("api/libraries").subscribe(result =>
|
|
||||||
{
|
|
||||||
this.libraries = result;
|
|
||||||
}, error => console.error(error));
|
|
||||||
|
|
||||||
this.router.events.subscribe((event: Event) =>
|
|
||||||
{
|
|
||||||
switch (true)
|
|
||||||
{
|
|
||||||
case event instanceof NavigationStart:
|
|
||||||
this.isLoading = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case event instanceof NavigationEnd:
|
|
||||||
case event instanceof NavigationCancel:
|
|
||||||
case event instanceof NavigationError:
|
|
||||||
this.isLoading = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.isLoading = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!navigator.userAgent.match(/Mobi/))
|
|
||||||
document.body.classList.add("hoverEnabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
openSearch()
|
|
||||||
{
|
|
||||||
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("search");
|
|
||||||
|
|
||||||
input.value = "";
|
|
||||||
input.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdateValue(event)
|
|
||||||
{
|
|
||||||
let query: string = event.target.value;
|
|
||||||
if (query != "")
|
|
||||||
{
|
|
||||||
event.target.classList.add("searching");
|
|
||||||
this.router.navigate(["/search/" + query], { replaceUrl: this.router.url.startsWith("/search/") });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
event.target.classList.remove("searching");
|
|
||||||
this.location.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Library
|
|
||||||
{
|
|
||||||
id: number;
|
|
||||||
slug: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
|
||||||
import { MatCardModule } from '@angular/material/card';
|
|
||||||
import { MatRippleModule } from '@angular/material/core';
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
|
||||||
import { MatSliderModule } from '@angular/material/slider';
|
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { BrowseComponent } from './browse/browse.component';
|
|
||||||
import { CollectionComponent } from './collection/collection.component';
|
|
||||||
import { EpisodesListComponent } from './episodes-list/episodes-list.component';
|
|
||||||
import { NotFoundComponent } from './not-found/not-found.component';
|
|
||||||
import { PeopleListComponent } from './people-list/people-list.component';
|
|
||||||
import { PlayerComponent } from './player/player.component';
|
|
||||||
import { SearchComponent } from './search/search.component';
|
|
||||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
|
||||||
import { ShowsListComponent } from './shows-list/shows-list.component';
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
AppComponent,
|
|
||||||
NotFoundComponent,
|
|
||||||
BrowseComponent,
|
|
||||||
ShowDetailsComponent,
|
|
||||||
EpisodesListComponent,
|
|
||||||
PlayerComponent,
|
|
||||||
CollectionComponent,
|
|
||||||
SearchComponent,
|
|
||||||
PeopleListComponent,
|
|
||||||
ShowsListComponent
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
HttpClientModule,
|
|
||||||
AppRoutingModule,
|
|
||||||
BrowserAnimationsModule,
|
|
||||||
MatSnackBarModule,
|
|
||||||
MatProgressBarModule,
|
|
||||||
MatButtonModule,
|
|
||||||
MatIconModule,
|
|
||||||
MatSelectModule,
|
|
||||||
MatMenuModule,
|
|
||||||
MatSliderModule,
|
|
||||||
MatTooltipModule,
|
|
||||||
MatRippleModule,
|
|
||||||
MatCardModule
|
|
||||||
],
|
|
||||||
providers: [],
|
|
||||||
bootstrap: [AppComponent]
|
|
||||||
})
|
|
||||||
export class AppModule { }
|
|
@ -1,30 +0,0 @@
|
|||||||
<div class="container-fluid justify-content-center">
|
|
||||||
<button mat-icon-button matTooltipPosition="below" matTooltip="Filter">
|
|
||||||
<mat-icon>filter_list</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-button matTooltipPosition="below" matTooltip="Sort" [matMenuTriggerFor]="sortMenu">
|
|
||||||
<mat-icon>sort</mat-icon> Sort by {{this.sortType}} <i *ngIf="this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="!this.sortUp" class="material-icons arrow">arrow_downward</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-menu #sortMenu="matMenu">
|
|
||||||
<div *ngFor="let type of this.sortTypes">
|
|
||||||
<button *ngIf="type != this.sortType; else elseBlock;" mat-menu-item (click)="sort(type, true)">
|
|
||||||
Sort by {{type}}
|
|
||||||
</button>
|
|
||||||
<ng-template #elseBlock>
|
|
||||||
<button mat-menu-item (click)="sort(type, !this.sortUp)">
|
|
||||||
Sort by {{type}} <i *ngIf="!this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="this.sortUp" class="material-icons arrow">arrow_downward</i>
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
</mat-menu>
|
|
||||||
|
|
||||||
<div class="container-fluid justify-content-center">
|
|
||||||
<a class="show" *ngFor="let show of this.shows" [href]="getLink(show)" [routerLink]="getLink(show)">
|
|
||||||
<div matRipple [style.background-image]="getThumb(show.slug)" > </div>
|
|
||||||
<p class="title">{{show.title}}</p>
|
|
||||||
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
|
|
||||||
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
@ -1,96 +0,0 @@
|
|||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
@import "~bootstrap/scss/mixins/breakpoints";
|
|
||||||
|
|
||||||
button
|
|
||||||
{
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow
|
|
||||||
{
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-fluid
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show
|
|
||||||
{
|
|
||||||
width: 33%;
|
|
||||||
min-width: 120px;
|
|
||||||
max-width: 200px;
|
|
||||||
list-style: none;
|
|
||||||
padding: .5em;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
width: 20%;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(lg)
|
|
||||||
{
|
|
||||||
width: 18%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(xl)
|
|
||||||
{
|
|
||||||
width: 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&:focus, &:hover
|
|
||||||
{
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
outline: solid var(--accentColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .title
|
|
||||||
{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
padding-top: 147.0588%;
|
|
||||||
background-size: cover;
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
> p
|
|
||||||
{
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
&.date
|
|
||||||
{
|
|
||||||
opacity: 0.8;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { BrowseComponent } from './browse.component';
|
|
||||||
|
|
||||||
describe('BrowseComponent', () => {
|
|
||||||
let component: BrowseComponent;
|
|
||||||
let fixture: ComponentFixture<BrowseComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ BrowseComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(BrowseComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,60 +0,0 @@
|
|||||||
import { Component, OnInit, Input } from '@angular/core';
|
|
||||||
import { ActivatedRoute } from '@angular/router';
|
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
|
||||||
import { Show } from "../../models/show";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-browse',
|
|
||||||
templateUrl: './browse.component.html',
|
|
||||||
styleUrls: ['./browse.component.scss']
|
|
||||||
})
|
|
||||||
export class BrowseComponent
|
|
||||||
{
|
|
||||||
@Input() shows: Show[];
|
|
||||||
sortType: string = "title";
|
|
||||||
sortUp: boolean = true;
|
|
||||||
|
|
||||||
sortTypes: string[] = ["title", "release date"];
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer)
|
|
||||||
{
|
|
||||||
this.route.data.subscribe((data) =>
|
|
||||||
{
|
|
||||||
this.shows = data.shows;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getThumb(slug: string)
|
|
||||||
{
|
|
||||||
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
getLink(show: Show)
|
|
||||||
{
|
|
||||||
if (show.isCollection)
|
|
||||||
return "/collection/" + show.slug;
|
|
||||||
else
|
|
||||||
return "/show/" + show.slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
sort(type: string, order: boolean)
|
|
||||||
{
|
|
||||||
this.sortType = type;
|
|
||||||
this.sortUp = order;
|
|
||||||
|
|
||||||
if (type == this.sortTypes[0])
|
|
||||||
{
|
|
||||||
if (order)
|
|
||||||
this.shows.sort((a, b) => { if (a.title < b.title) return -1; else if (a.title > b.title) return 1; return 0; });
|
|
||||||
else
|
|
||||||
this.shows.sort((a, b) => { if (a.title < b.title) return 1; else if (a.title > b.title) return -1; return 0; });
|
|
||||||
}
|
|
||||||
else if (type == this.sortTypes[1])
|
|
||||||
{
|
|
||||||
if (order)
|
|
||||||
this.shows.sort((a, b) => a.startYear - b.startYear);
|
|
||||||
else
|
|
||||||
this.shows.sort((a, b) => b.startYear - a.startYear);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<div class="container-fluid">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-4 col-lg-3 col-xl-2 collection-info">
|
|
||||||
<div [style.background-image]="getThumb()"></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8 col-lg-9 col-xl-10">
|
|
||||||
<h3 class="text-center text-md-left p-2 p-md-3">{{collection.name}}</h3>
|
|
||||||
<h5 class="date" *ngIf="collection.endYear; else elseBlock">{{collection.startYear}} - {{collection.endYear}}</h5>
|
|
||||||
<ng-template #elseBlock><h5 class="date">{{collection.startYear}}</h5></ng-template>
|
|
||||||
<hr />
|
|
||||||
<app-browse [shows]="collection.shows"></app-browse>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,23 +0,0 @@
|
|||||||
.collection-info
|
|
||||||
{
|
|
||||||
width: 60%;
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
padding-top: 147.0588%;
|
|
||||||
background-size: cover;
|
|
||||||
background-color: #333333;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr
|
|
||||||
{
|
|
||||||
margin: 10px 0 10px 0;
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, .60);
|
|
||||||
border-left: 0;
|
|
||||||
width: inherit;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { CollectionComponent } from './collection.component';
|
|
||||||
|
|
||||||
describe('CollectionComponent', () => {
|
|
||||||
let component: CollectionComponent;
|
|
||||||
let fixture: ComponentFixture<CollectionComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ CollectionComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(CollectionComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Collection } from "../../models/collection";
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-collection',
|
|
||||||
templateUrl: './collection.component.html',
|
|
||||||
styleUrls: ['./collection.component.scss']
|
|
||||||
})
|
|
||||||
export class CollectionComponent
|
|
||||||
{
|
|
||||||
collection: Collection;
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer)
|
|
||||||
{
|
|
||||||
this.route.data.subscribe((data) =>
|
|
||||||
{
|
|
||||||
this.collection = data.collection;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getThumb()
|
|
||||||
{
|
|
||||||
return this.sanitizer.bypassSecurityTrustStyle("url(" + this.collection.poster + ")");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
<div class="root">
|
|
||||||
<div class="episodes" #scrollView (scroll)="onScroll()">
|
|
||||||
<a class="episode" *ngFor="let episode of this.episodes" #episodeDom routerLink="/watch/{{episode.link}}" href="/watch/{{episode.link}}">
|
|
||||||
<div matRipple class="img" [style.background-image]="sanitize(episode.thumb)">
|
|
||||||
<button mat-icon-button class="playBtn"><i class="material-icons playIcon">play_circle_outline</i></button>
|
|
||||||
</div>
|
|
||||||
<ng-container *ngIf="displayShowTitle; else noTitle;">
|
|
||||||
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">{{episode.showTitle}} - S{{episode.seasonNumber}}:E{{episode.episodeNumber}}</h6>
|
|
||||||
<ng-template #elseBlock><h6 class="title">{{episode.showTitle}}</h6></ng-template>
|
|
||||||
<p class="subtitle">{{episode.title}}</p>
|
|
||||||
</ng-container>
|
|
||||||
<ng-template #noTitle>
|
|
||||||
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">S{{episode.seasonNumber}}:E{{episode.episodeNumber}} - {{episode.title}}</h6>
|
|
||||||
<ng-template #elseBlock><h6 class="title">{{episode.title}}</h6></ng-template>
|
|
||||||
<p class="overview">{{episode.overview}}</p>
|
|
||||||
</ng-template>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
|
||||||
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
|
|
||||||
</div>
|
|
@ -1,182 +0,0 @@
|
|||||||
@import "~bootstrap//scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
@import "~bootstrap/scss//mixins/breakpoints";
|
|
||||||
|
|
||||||
.root
|
|
||||||
{
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
.scrollBtn
|
|
||||||
{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.episodes
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-width: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-direction: row;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #999 transparent;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar
|
|
||||||
{
|
|
||||||
height: 4px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb
|
|
||||||
{
|
|
||||||
background-color: #999;
|
|
||||||
border-radius: 90px;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
background-color: rgb(134, 127, 127);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.episode
|
|
||||||
{
|
|
||||||
visibility: visible;
|
|
||||||
display: inline-block;
|
|
||||||
padding: .25rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 55%;
|
|
||||||
cursor: pointer;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: inherit;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
width: 40%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
width: 33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(lg)
|
|
||||||
{
|
|
||||||
width: 28%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(xl)
|
|
||||||
{
|
|
||||||
width: 18%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
padding-top: 56.25%;
|
|
||||||
background-color: #333333;
|
|
||||||
background-size: contain;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
> button
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
outline: none;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title
|
|
||||||
{
|
|
||||||
padding-top: .2rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 1;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overview
|
|
||||||
{
|
|
||||||
font-weight: 300;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 4;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle
|
|
||||||
{
|
|
||||||
font-weight: 300;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 1;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
.img
|
|
||||||
{
|
|
||||||
outline: solid var(--accentColor);
|
|
||||||
|
|
||||||
.playBtn
|
|
||||||
{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title
|
|
||||||
{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.playIcon
|
|
||||||
{
|
|
||||||
font-size: 64px;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
line-height: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollBtn
|
|
||||||
{
|
|
||||||
padding: 0;
|
|
||||||
outline: none;
|
|
||||||
min-width: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 20%;
|
|
||||||
bottom: 60%;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&.leftBtn
|
|
||||||
{
|
|
||||||
left: 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.rightBtn
|
|
||||||
{
|
|
||||||
right: 0;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-left: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { EpisodesListComponent } from './episodes-list.component';
|
|
||||||
|
|
||||||
describe('EpisodesListComponent', () => {
|
|
||||||
let component: EpisodesListComponent;
|
|
||||||
let fixture: ComponentFixture<EpisodesListComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ EpisodesListComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(EpisodesListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,60 +0,0 @@
|
|||||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
|
|
||||||
import { MatButton } from "@angular/material/button";
|
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
|
||||||
import { Episode } from "../../models/episode";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-episodes-list',
|
|
||||||
templateUrl: './episodes-list.component.html',
|
|
||||||
styleUrls: ['./episodes-list.component.scss']
|
|
||||||
})
|
|
||||||
export class EpisodesListComponent
|
|
||||||
{
|
|
||||||
@Input() displayShowTitle: boolean = false;
|
|
||||||
@Input() episodes: Episode[];
|
|
||||||
@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
|
|
||||||
@ViewChild("leftBtn", { static: false }) private leftBtn: MatButton;
|
|
||||||
@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
|
|
||||||
@ViewChild("episodeDom", { static: false }) private episode: ElementRef;
|
|
||||||
|
|
||||||
constructor(private sanitizer: DomSanitizer) { }
|
|
||||||
|
|
||||||
scrollLeft()
|
|
||||||
{
|
|
||||||
let scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
|
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollRight()
|
|
||||||
{
|
|
||||||
let scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
|
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
roundScroll(offset: number): number
|
|
||||||
{
|
|
||||||
let episodeSize: number = this.episode.nativeElement.scrollWidth;
|
|
||||||
|
|
||||||
offset = Math.round(offset / episodeSize) * episodeSize;
|
|
||||||
if (offset == 0)
|
|
||||||
offset = episodeSize;
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll()
|
|
||||||
{
|
|
||||||
if (this.scrollView.nativeElement.scrollLeft <= 0)
|
|
||||||
this.leftBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.leftBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
if (this.scrollView.nativeElement.scrollLeft >= this.scrollView.nativeElement.scrollWidth - this.scrollView.nativeElement.clientWidth)
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
}
|
|
||||||
|
|
||||||
sanitize(url: string)
|
|
||||||
{
|
|
||||||
return this.sanitizer.bypassSecurityTrustStyle("url(" + url + ")");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<div class="text-center">
|
|
||||||
<h1>404 Error</h1>
|
|
||||||
<p>The page you requested was not found.</p>
|
|
||||||
</div>
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { NotFoundComponent } from './not-found.component';
|
|
||||||
|
|
||||||
describe('NotFoundComponent', () => {
|
|
||||||
let component: NotFoundComponent;
|
|
||||||
let fixture: ComponentFixture<NotFoundComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ NotFoundComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(NotFoundComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,15 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-not-found',
|
|
||||||
templateUrl: './not-found.component.html',
|
|
||||||
styleUrls: ['./not-found.component.scss']
|
|
||||||
})
|
|
||||||
export class NotFoundComponent implements OnInit {
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
<div class="scroll-row mb-5">
|
|
||||||
<div class="people-container" #scrollView (scroll)="onScroll()">
|
|
||||||
<a class="people" *ngFor="let people of this.people" routerLink="/people/{{people.slug}}" href="/people/{{people.slug}}">
|
|
||||||
<div matRipple [style.background-image]="getPeopleIcon(people.slug)"> </div>
|
|
||||||
<h6 class="name">{{people.name}}</h6>
|
|
||||||
<p class="role">{{people.role}}</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
|
||||||
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
|
|
||||||
</div>
|
|
@ -1,141 +0,0 @@
|
|||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
@import "~bootstrap/scss/mixins/breakpoints";
|
|
||||||
|
|
||||||
.people-container
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-width: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-direction: row;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #999 transparent;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar
|
|
||||||
{
|
|
||||||
height: 4px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb
|
|
||||||
{
|
|
||||||
background-color: #999;
|
|
||||||
border-radius: 90px;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
background-color: rgb(134, 127, 127);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.people
|
|
||||||
{
|
|
||||||
visibility: visible;
|
|
||||||
margin: .25rem;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
outline: none;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
width: 33%;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
width: 22%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(lg)
|
|
||||||
{
|
|
||||||
width: 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(xl)
|
|
||||||
{
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
padding-top: 147.0588%;
|
|
||||||
background-size: cover;
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
> p, h6
|
|
||||||
{
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
|
|
||||||
&.role
|
|
||||||
{
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
> img
|
|
||||||
{
|
|
||||||
outline: solid var(--accentColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.name
|
|
||||||
{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-row
|
|
||||||
{
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
.scrollBtn
|
|
||||||
{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollBtn
|
|
||||||
{
|
|
||||||
padding: 0;
|
|
||||||
outline: none;
|
|
||||||
min-width: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 30%;
|
|
||||||
bottom: 40%;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&.leftBtn
|
|
||||||
{
|
|
||||||
left: 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.rightBtn
|
|
||||||
{
|
|
||||||
right: 0;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-left: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { PeopleListComponent } from './people-list.component';
|
|
||||||
|
|
||||||
describe('PeopleListComponent', () => {
|
|
||||||
let component: PeopleListComponent;
|
|
||||||
let fixture: ComponentFixture<PeopleListComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ PeopleListComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(PeopleListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,48 +0,0 @@
|
|||||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
|
|
||||||
import { MatButton } from "@angular/material/button";
|
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
|
||||||
import { People } from "../../models/people";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-people-list',
|
|
||||||
templateUrl: './people-list.component.html',
|
|
||||||
styleUrls: ['./people-list.component.scss']
|
|
||||||
})
|
|
||||||
export class PeopleListComponent
|
|
||||||
{
|
|
||||||
@Input() people: People[];
|
|
||||||
@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
|
|
||||||
@ViewChild("leftBtn", { static: false }) private leftBtn: MatButton;
|
|
||||||
@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
|
|
||||||
|
|
||||||
constructor(private sanitizer: DomSanitizer) { }
|
|
||||||
|
|
||||||
scrollLeft()
|
|
||||||
{
|
|
||||||
let scroll: number = this.scrollView.nativeElement.offsetWidth * 0.80;
|
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollRight()
|
|
||||||
{
|
|
||||||
let scroll: number = this.scrollView.nativeElement.offsetWidth * 0.80;
|
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll()
|
|
||||||
{
|
|
||||||
if (this.scrollView.nativeElement.scrollLeft <= 0)
|
|
||||||
this.leftBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.leftBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
if (this.scrollView.nativeElement.scrollLeft >= this.scrollView.nativeElement.scrollWidth - this.scrollView.nativeElement.clientWidth)
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
}
|
|
||||||
|
|
||||||
getPeopleIcon(slug: string)
|
|
||||||
{
|
|
||||||
return this.sanitizer.bypassSecurityTrustStyle("url(/peopleimg/" + slug + ")");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
<div id="root">
|
|
||||||
<div class="player data-vjs-player">
|
|
||||||
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="videoClicked()">
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="loadIndicator">
|
|
||||||
<div class="spinner-border align-self-center" role="status"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-card class="d-none w-25 m-5" [ngClass]="{'d-block': this.displayStats}">
|
|
||||||
<mat-card-header style="margin-bottom: 0.5rem;">
|
|
||||||
<h4 style="align-self: center; margin-bottom: 0;">Stats</h4>
|
|
||||||
<div style="flex: 1 1 auto"></div>
|
|
||||||
<button mat-icon-button aria-label="Close" (click)="this.displayStats = false" style="outline: none;">
|
|
||||||
<mat-icon>close</mat-icon>
|
|
||||||
</button>
|
|
||||||
</mat-card-header>
|
|
||||||
<mat-card-content>
|
|
||||||
Play method: <span style="float: right">{{this.playMethod}}</span>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
Video Container: <span style="float: right">{{this.item.container}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("container")}}</i></span>
|
|
||||||
<br />
|
|
||||||
Video Codec: <span style="float: right">{{this.item.video.codec}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("video")}}</i></span>
|
|
||||||
<br />
|
|
||||||
Audio Codec: <span style="float: right">{{this.item.audios[0].codec}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("audio")}}</i></span>
|
|
||||||
<br />
|
|
||||||
Subtitle Codec: <span style="float: right">{{this.selectedSubtitle ? this.selectedSubtitle.codec : "none"}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("subtitle")}}</i></span>
|
|
||||||
<br />
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
|
|
||||||
<div id="hover">
|
|
||||||
<div class="back">
|
|
||||||
<a mat-icon-button matTooltipPosition="below" matTooltip="Back" (click)="back()">
|
|
||||||
<mat-icon>arrow_back</mat-icon>
|
|
||||||
</a>
|
|
||||||
<h5>{{this.item.showTitle}}</h5>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="controller container-fluid" id="controller">
|
|
||||||
<div class="img d-none d-sm-block">
|
|
||||||
<img src="poster/{{this.item.showSlug}}" />
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h3>S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
|
|
||||||
|
|
||||||
<div id="progress-bar">
|
|
||||||
<div class="seek-bar">
|
|
||||||
<div id="buffered"></div>
|
|
||||||
<div id="progress"></div>
|
|
||||||
</div>
|
|
||||||
<div id="thumb"><div></div></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttons">
|
|
||||||
<div class="left">
|
|
||||||
<a *ngIf="this.item.previousEpisode" mat-icon-button matTooltipPosition="above" matTooltip="Previous" (click)="previous()">
|
|
||||||
<mat-icon>skip_previous</mat-icon>
|
|
||||||
</a>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" [matTooltip]="playTooltip" id="play" (click)="tooglePlayback()">
|
|
||||||
<mat-icon>{{this.playIcon}}</mat-icon>
|
|
||||||
</button>
|
|
||||||
<a mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" (click)="next()">
|
|
||||||
<mat-icon>skip_next</mat-icon>
|
|
||||||
|
|
||||||
<div id="next">
|
|
||||||
<div id="main">
|
|
||||||
<img src="{{this.item.nextEpisode.thumb}}" />
|
|
||||||
</div>
|
|
||||||
<div id="overview">
|
|
||||||
<h6>S{{this.item.nextEpisode.seasonNumber}}:E{{this.item.nextEpisode.episodeNumber}} - {{this.item.nextEpisode.title}}</h6>
|
|
||||||
<p>{{this.item.nextEpisode.overview}}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div id="volume">
|
|
||||||
<button mat-icon-button matTooltipPosition="above" matTooltip="Volume" (click)="toogleMute()">
|
|
||||||
<mat-icon>{{this.volumeIcon}}</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<mat-slider [value]="volume" (input)="changeVolume($event.value)"></mat-slider>
|
|
||||||
</div>
|
|
||||||
<p *ngIf="this.maxHours; else elseBlock">{{this.hours | number: '2.0-0'}}:{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / {{this.maxHours | number: '2.0-0'}}:{{this.maxMinutes | number: '2.0-0'}}:{{this.maxSeconds | number: '2.0-0'}}</p>
|
|
||||||
<ng-template #elseBlock><p>{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / {{this.maxMinutes | number: '2.0-0'}}:{{this.maxSeconds | number: '2.0-0'}}</p></ng-template>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<button id="volume" *ngIf="this.item.audios.length > 1" mat-icon-button matTooltipPosition="above" matTooltip="Select audio track">
|
|
||||||
<mat-icon>music_note</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" matTooltipPosition="above" matTooltip="Select subtitle track">
|
|
||||||
<mat-icon>closed_caption</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" matTooltip="Cast">
|
|
||||||
<mat-icon>cast</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" [matMenuTriggerFor]="settings" matTooltip="Settings">
|
|
||||||
<mat-icon>settings</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" [matTooltip]="fullscreenTooltip" id="fullscreen" (click)="fullscreen()">
|
|
||||||
<mat-icon>{{fullscreenIcon}}</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-menu #subtitles="matMenu">
|
|
||||||
<ng-template matMenuContent>
|
|
||||||
<button [ngClass]="{'selected': this.selectedSubtitle == null}" mat-menu-item (click)="selectSubtitle(null)">
|
|
||||||
<span>None</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div *ngFor="let subtitle of this.item.subtitles">
|
|
||||||
<button [ngClass]="{'selected': this.selectedSubtitle == subtitle}" mat-menu-item *ngIf="subtitle.codec == 'ass' || subtitle.codec == 'subrip'; else elseBlock" (click)="selectSubtitle(subtitle)">
|
|
||||||
<span>{{subtitle.displayName}}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<ng-template #elseBlock>
|
|
||||||
<button mat-menu-item disabled>
|
|
||||||
<span>{{subtitle.displayName}} ({{subtitle.codec}})</span>
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</mat-menu>
|
|
||||||
|
|
||||||
<mat-menu #settings="matMenu">
|
|
||||||
<ng-template matMenuContent>
|
|
||||||
<button mat-menu-item (click)="this.displayStats = !this.displayStats">
|
|
||||||
<span>Stats</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item [matMenuTriggerFor]="method">
|
|
||||||
<span>Method</span>
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
</mat-menu>
|
|
||||||
|
|
||||||
<mat-menu #method="matMenu">
|
|
||||||
<ng-template matMenuContent>
|
|
||||||
<button mat-menu-item (click)="selectPlayMethod(methodType.direct)">
|
|
||||||
<span>Direct Play</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectPlayMethod(methodType.transmux)">
|
|
||||||
<span>Transmux</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectPlayMethod(methodType.transcode)">
|
|
||||||
<span>Transcode</span>
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
</mat-menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,341 +0,0 @@
|
|||||||
@import "../../libraries/subtitles";
|
|
||||||
@import "./vtt-subtitles";
|
|
||||||
|
|
||||||
.player
|
|
||||||
{
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: #000;
|
|
||||||
|
|
||||||
> video
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#hover
|
|
||||||
{
|
|
||||||
transition: opacity .2s linear;
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
|
|
||||||
&.idle
|
|
||||||
{
|
|
||||||
transition: opacity .6s linear, visibility 0s .6s;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.back
|
|
||||||
{
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.6);
|
|
||||||
padding: .33%;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> a
|
|
||||||
{
|
|
||||||
outline: none;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> h5
|
|
||||||
{
|
|
||||||
margin: 0;
|
|
||||||
margin-left: .5rem;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.controller
|
|
||||||
{
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.6);
|
|
||||||
display: flex;
|
|
||||||
padding: 1%;
|
|
||||||
|
|
||||||
.img
|
|
||||||
{
|
|
||||||
width: 15%;
|
|
||||||
position: relative;
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
> img
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
bottom: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
margin-left: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.buttons
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
&.left
|
|
||||||
{
|
|
||||||
align-self: start;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> p
|
|
||||||
{
|
|
||||||
margin: 0;
|
|
||||||
margin-left: 1rem;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.right
|
|
||||||
{
|
|
||||||
align-self: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
> button
|
|
||||||
{
|
|
||||||
margin-left: .3rem;
|
|
||||||
margin-right: .3rem;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> a
|
|
||||||
{
|
|
||||||
margin-left: .3rem;
|
|
||||||
margin-right: .3rem;
|
|
||||||
outline: none;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#progress-bar
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.seek-bar
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 4px;
|
|
||||||
position: relative;
|
|
||||||
background-color: rgba(255, 255, 255, .2);
|
|
||||||
transform: scaleY(.6);
|
|
||||||
|
|
||||||
#progress
|
|
||||||
{
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
background-color: var(--accentColor);
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#buffered
|
|
||||||
{
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(255, 255, 255, .5);
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#thumb
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 12px;
|
|
||||||
position: absolute;
|
|
||||||
left: -6px;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
margin: auto;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: var(--accentColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hoverEnabled &:hover, &.seeking
|
|
||||||
{
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.seek-bar
|
|
||||||
{
|
|
||||||
transform: scaleY(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#thumb
|
|
||||||
{
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#nextBtn
|
|
||||||
{
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.hoverEnabled &:hover
|
|
||||||
{
|
|
||||||
#next
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#next
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 100%;
|
|
||||||
display: none;
|
|
||||||
background-color: #212121;
|
|
||||||
white-space: normal;
|
|
||||||
line-height: normal;
|
|
||||||
cursor: default;
|
|
||||||
height: 150px;
|
|
||||||
|
|
||||||
#main
|
|
||||||
{
|
|
||||||
width: auto;
|
|
||||||
height: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
|
|
||||||
> img
|
|
||||||
{
|
|
||||||
width: auto;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#overview
|
|
||||||
{
|
|
||||||
padding: 1%;
|
|
||||||
width: 50%;
|
|
||||||
min-width: 300px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
> p
|
|
||||||
{
|
|
||||||
text-align: justify;
|
|
||||||
font-weight: 300;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#volume
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> button
|
|
||||||
{
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hoverEnabled &:hover, &:focus-within
|
|
||||||
{
|
|
||||||
> mat-slider
|
|
||||||
{
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> mat-slider
|
|
||||||
{
|
|
||||||
width: 0px;
|
|
||||||
min-width: 0px;
|
|
||||||
padding: 0;
|
|
||||||
height: 40px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: width .2s cubic-bezier(0.4,0, 1, 1);
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
top: 19px;
|
|
||||||
left: 10px;
|
|
||||||
right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-menu-item
|
|
||||||
{
|
|
||||||
outline: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected
|
|
||||||
{
|
|
||||||
background: #595959 !important;
|
|
||||||
color: var(--accentColor);
|
|
||||||
font-weight: 900;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadIndicator
|
|
||||||
{
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.volume
|
|
||||||
{
|
|
||||||
min-width: 0px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-panel
|
|
||||||
{
|
|
||||||
min-width: 250px !important;
|
|
||||||
max-width: 300px !important;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { PlayerComponent } from './player.component';
|
|
||||||
|
|
||||||
describe('PlayerComponent', () => {
|
|
||||||
let component: PlayerComponent;
|
|
||||||
let fixture: ComponentFixture<PlayerComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ PlayerComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(PlayerComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,644 +0,0 @@
|
|||||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
|
||||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
|
||||||
import { DomSanitizer, Title } from "@angular/platform-browser";
|
|
||||||
import { ActivatedRoute, Event, NavigationCancel, NavigationEnd, NavigationStart, Router } from "@angular/router";
|
|
||||||
import { Track, WatchItem } from "../../models/watch-item";
|
|
||||||
import { Location } from "@angular/common";
|
|
||||||
import * as Hls from "hls.js"
|
|
||||||
import { getPlaybackMethod, method, getWhatIsSupported, SupportList } from "../../videoSupport/playbackMethodDetector";
|
|
||||||
|
|
||||||
declare var SubtitleManager: any;
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-player',
|
|
||||||
templateUrl: './player.component.html',
|
|
||||||
styleUrls: ['./player.component.scss'],
|
|
||||||
encapsulation: ViewEncapsulation.None
|
|
||||||
})
|
|
||||||
export class PlayerComponent implements OnInit
|
|
||||||
{
|
|
||||||
item: WatchItem;
|
|
||||||
|
|
||||||
volume: number = 100;
|
|
||||||
seeking: boolean = false;
|
|
||||||
videoHider;
|
|
||||||
controllerHovered: boolean = false;
|
|
||||||
selectedSubtitle: Track;
|
|
||||||
|
|
||||||
hours: number;
|
|
||||||
minutes: number = 0;
|
|
||||||
seconds: number = 0;
|
|
||||||
|
|
||||||
maxHours: number;
|
|
||||||
maxMinutes: number;
|
|
||||||
maxSeconds: number;
|
|
||||||
|
|
||||||
playIcon: string = "pause"; //Icon used by the play btn.
|
|
||||||
volumeIcon: string = "volume_up"; //Icon used by the volume btn.
|
|
||||||
fullscreenIcon: string = "fullscreen"; //Icon used by the fullscreen btn.
|
|
||||||
|
|
||||||
playTooltip: string = "Pause"; //Text used in the play tooltip
|
|
||||||
fullscreenTooltip: string = "Fullscreen"; //Text used in the fullscreen tooltip
|
|
||||||
|
|
||||||
playMethod: method = method.direct;
|
|
||||||
methodType = method;
|
|
||||||
|
|
||||||
displayStats: boolean = false;
|
|
||||||
supportList: SupportList;
|
|
||||||
|
|
||||||
private player: HTMLVideoElement;
|
|
||||||
private hlsPlayer: Hls = new Hls();
|
|
||||||
private thumb: HTMLElement;
|
|
||||||
private progress: HTMLElement;
|
|
||||||
private buffered: HTMLElement;
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private snackBar: MatSnackBar, private title: Title, private router: Router, private location: Location) { }
|
|
||||||
|
|
||||||
ngOnInit()
|
|
||||||
{
|
|
||||||
document.getElementById("nav").classList.add("d-none");
|
|
||||||
this.route.data.subscribe((data) =>
|
|
||||||
{
|
|
||||||
this.item = data.item;
|
|
||||||
this.item.duration = 20 * 60;
|
|
||||||
|
|
||||||
if (this.player)
|
|
||||||
{
|
|
||||||
this.player.load();
|
|
||||||
this.initPlayBtn();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setDuration(this.item.duration);
|
|
||||||
|
|
||||||
this.title.setTitle(this.item.showTitle + " S" + this.item.seasonNumber + ":E" + this.item.episodeNumber + " - Kyoo");
|
|
||||||
|
|
||||||
if (navigator.userAgent.match(/Mobi/) && document.fullscreenElement == null)
|
|
||||||
{
|
|
||||||
this.fullscreen();
|
|
||||||
screen.orientation.lock("landscape");
|
|
||||||
$("#fullscreen").addClass("d-none");
|
|
||||||
$("#volume").addClass("d-none");
|
|
||||||
}
|
|
||||||
setTimeout(() =>
|
|
||||||
{
|
|
||||||
if (this.player)
|
|
||||||
this.init();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterViewInit()
|
|
||||||
{
|
|
||||||
this.player = document.getElementById("player") as HTMLVideoElement;
|
|
||||||
this.thumb = document.getElementById("thumb") as HTMLElement;
|
|
||||||
this.progress = document.getElementById("progress") as HTMLElement;
|
|
||||||
this.buffered = document.getElementById("buffered") as HTMLElement;
|
|
||||||
this.player.controls = false;
|
|
||||||
|
|
||||||
this.player.onplay = () =>
|
|
||||||
{
|
|
||||||
this.initPlayBtn();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player.onpause = () =>
|
|
||||||
{
|
|
||||||
this.playIcon = "play_arrow"
|
|
||||||
this.playTooltip = "Play";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player.ontimeupdate = () =>
|
|
||||||
{
|
|
||||||
if (!this.seeking)
|
|
||||||
this.updateTime(this.player.currentTime);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.player.onprogress = () =>
|
|
||||||
{
|
|
||||||
if (this.player.buffered.length > 0)
|
|
||||||
this.buffered.style.width = (this.player.buffered.end(this.player.buffered.length - 1) / this.item.duration * 100) + "%";
|
|
||||||
|
|
||||||
if (this.player.duration != undefined && this.player.duration != Infinity)
|
|
||||||
this.setDuration(this.player.duration);
|
|
||||||
};
|
|
||||||
|
|
||||||
let loadIndicator: HTMLElement = document.getElementById("loadIndicator") as HTMLElement;
|
|
||||||
this.player.onwaiting = () =>
|
|
||||||
{
|
|
||||||
loadIndicator.classList.remove("d-none");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player.oncanplay = () =>
|
|
||||||
{
|
|
||||||
loadIndicator.classList.add("d-none");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player.onended = () =>
|
|
||||||
{
|
|
||||||
this.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player.onerror = () =>
|
|
||||||
{
|
|
||||||
if (this.playMethod == method.transcode)
|
|
||||||
{
|
|
||||||
this.snackBar.open("This episode can't be played.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 10000 });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (this.playMethod == method.direct)
|
|
||||||
this.playMethod = method.transmux;
|
|
||||||
else
|
|
||||||
this.playMethod = method.transcode;
|
|
||||||
this.selectPlayMethod(this.playMethod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let progressBar: HTMLElement = document.getElementById("progress-bar") as HTMLElement;
|
|
||||||
$(progressBar).click((event) =>
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.pageX);
|
|
||||||
this.player.currentTime = time;
|
|
||||||
this.updateTime(time);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!navigator.userAgent.match(/Mobi/))
|
|
||||||
{
|
|
||||||
$(document).mousemove((event) =>
|
|
||||||
{
|
|
||||||
if (this.seeking)
|
|
||||||
{
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.pageX);
|
|
||||||
this.updateTime(time);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
document.getElementById("hover").classList.remove("idle");
|
|
||||||
document.documentElement.style.cursor = "";
|
|
||||||
|
|
||||||
clearTimeout(this.videoHider);
|
|
||||||
|
|
||||||
this.videoHider = setTimeout((ev: MouseEvent) =>
|
|
||||||
{
|
|
||||||
if (!this.player.paused && !this.controllerHovered)
|
|
||||||
{
|
|
||||||
document.getElementById("hover").classList.add("idle");
|
|
||||||
document.documentElement.style.cursor = "none";
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(progressBar).mousedown((event) =>
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
this.seeking = true;
|
|
||||||
progressBar.classList.add("seeking");
|
|
||||||
this.player.pause();
|
|
||||||
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.pageX);
|
|
||||||
this.updateTime(time);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).mouseup((event) =>
|
|
||||||
{
|
|
||||||
if (this.seeking)
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
this.seeking = false;
|
|
||||||
progressBar.classList.remove("seeking");
|
|
||||||
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.pageX);
|
|
||||||
this.player.currentTime = time;
|
|
||||||
this.player.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#controller").mouseenter(() => { this.controllerHovered = true; });
|
|
||||||
$("#controller").mouseleave(() => { this.controllerHovered = false; });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$(progressBar).on("touchstart", (event) =>
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
this.seeking = true;
|
|
||||||
progressBar.classList.add("seeking");
|
|
||||||
this.player.pause();
|
|
||||||
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.changedTouches[0].pageX);
|
|
||||||
this.updateTime(time);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("touchend", (event) =>
|
|
||||||
{
|
|
||||||
if (this.seeking)
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
this.seeking = false;
|
|
||||||
progressBar.classList.remove("seeking");
|
|
||||||
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.changedTouches[0].pageX);
|
|
||||||
this.player.currentTime = time;
|
|
||||||
this.player.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("touchmove", (event) =>
|
|
||||||
{
|
|
||||||
if (this.seeking)
|
|
||||||
{
|
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.changedTouches[0].pageX);
|
|
||||||
this.updateTime(time);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Initialize the timout at the document initialization.
|
|
||||||
this.videoHider = setTimeout(() =>
|
|
||||||
{
|
|
||||||
if (!this.player.paused)
|
|
||||||
{
|
|
||||||
document.getElementById("hover").classList.add("idle");
|
|
||||||
document.documentElement.style.cursor = "none";
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
document.addEventListener("fullscreenchange", () =>
|
|
||||||
{
|
|
||||||
if (navigator.userAgent.match(/Mobi/))
|
|
||||||
{
|
|
||||||
if (document.fullscreenElement == null && this.router.url.startsWith("/watch"))
|
|
||||||
this.back();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (document.fullscreenElement != null)
|
|
||||||
{
|
|
||||||
this.fullscreenIcon = "fullscreen_exit";
|
|
||||||
this.fullscreenTooltip = "Exit fullscreen";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.fullscreenIcon = "fullscreen";
|
|
||||||
this.fullscreenTooltip = "Fullscreen";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).keydown((e) =>
|
|
||||||
{
|
|
||||||
switch (e.keyCode)
|
|
||||||
{
|
|
||||||
case 32: //space
|
|
||||||
this.tooglePlayback();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 38: //Key up
|
|
||||||
this.changeVolume(this.volume + 5);
|
|
||||||
this.snackBar.open(this.volume + "%", null, { verticalPosition: "top", horizontalPosition: "right", duration: 300, panelClass: "volume" });
|
|
||||||
break;
|
|
||||||
case 40: //Key down
|
|
||||||
this.changeVolume(this.volume - 5);
|
|
||||||
this.snackBar.open(this.volume + "%", null, { verticalPosition: "top", horizontalPosition: "right", duration: 300, panelClass: "volume" });
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 86: //V key
|
|
||||||
let subtitleIndex: number = this.item.subtitles.indexOf(this.selectedSubtitle);
|
|
||||||
let nextSub: Track;
|
|
||||||
if (subtitleIndex + 1 <= this.item.subtitles.length)
|
|
||||||
nextSub = this.item.subtitles[subtitleIndex + 1];
|
|
||||||
else
|
|
||||||
nextSub = this.item.subtitles[0];
|
|
||||||
|
|
||||||
this.selectSubtitle(nextSub);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 70: //F key
|
|
||||||
this.fullscreen();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 77: //M key
|
|
||||||
this.toogleMute();
|
|
||||||
if (this.player.muted)
|
|
||||||
this.snackBar.open("Sound muted.", null, { verticalPosition: "top", horizontalPosition: "right", duration: 750, panelClass: "info-panel" });
|
|
||||||
else
|
|
||||||
this.snackBar.open("Sound unmuted.", null, { verticalPosition: "top", horizontalPosition: "right", duration: 750, panelClass: "info-panel" });
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 78: //N key
|
|
||||||
this.next();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 80: //P key
|
|
||||||
this.previous();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.router.events.subscribe((event: Event) =>
|
|
||||||
{
|
|
||||||
switch (true)
|
|
||||||
{
|
|
||||||
case event instanceof NavigationStart:
|
|
||||||
{
|
|
||||||
loadIndicator.classList.remove("d-none");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case event instanceof NavigationEnd:
|
|
||||||
case event instanceof NavigationCancel:
|
|
||||||
{
|
|
||||||
loadIndicator.classList.add("d-none");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() =>
|
|
||||||
{
|
|
||||||
this.init();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
init()
|
|
||||||
{
|
|
||||||
let queryMethod: string = this.route.snapshot.queryParams["method"];
|
|
||||||
if (queryMethod)
|
|
||||||
this.playMethod = method[queryMethod];
|
|
||||||
else
|
|
||||||
this.playMethod = getPlaybackMethod(this.player, this.item);
|
|
||||||
|
|
||||||
this.selectPlayMethod(this.playMethod);
|
|
||||||
|
|
||||||
let sub: string = this.route.snapshot.queryParams["sub"];
|
|
||||||
if (sub != null)
|
|
||||||
{
|
|
||||||
let languageCode: string = sub.substring(0, 3);
|
|
||||||
let forced: boolean = sub.length > 3 && sub.substring(4) == "for";
|
|
||||||
|
|
||||||
this.selectSubtitle(this.item.subtitles.find(x => x.language == languageCode && x.isForced == forced), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.supportList = getWhatIsSupported(this.player, this.item);
|
|
||||||
|
|
||||||
setTimeout(() =>
|
|
||||||
{
|
|
||||||
this.snackBar.open("Playing: " + this.item.showTitle + " S" + this.item.seasonNumber + ":E" + this.item.episodeNumber, null, { verticalPosition: "top", horizontalPosition: "right", duration: 2000, panelClass: "info-panel" });
|
|
||||||
}, 750);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectPlayMethod(playMethod: method)
|
|
||||||
{
|
|
||||||
this.playMethod = playMethod;
|
|
||||||
if (this.playMethod == method.direct)
|
|
||||||
{
|
|
||||||
this.player.src = "/video/" + this.item.link;
|
|
||||||
}
|
|
||||||
else if (this.playMethod == method.transmux)
|
|
||||||
{
|
|
||||||
this.hlsPlayer.loadSource("/video/transmux/" + this.item.link + "/");
|
|
||||||
this.hlsPlayer.attachMedia(this.player);
|
|
||||||
this.hlsPlayer.on(Hls.Events.MANIFEST_LOADED, () =>
|
|
||||||
{
|
|
||||||
this.player.play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.hlsPlayer.loadSource("/video/transcode/" + this.item.link + "/");
|
|
||||||
this.hlsPlayer.attachMedia(this.player);
|
|
||||||
this.hlsPlayer.on(Hls.Events.MANIFEST_LOADED, () =>
|
|
||||||
{
|
|
||||||
this.player.play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
back()
|
|
||||||
{
|
|
||||||
this.location.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
{
|
|
||||||
if (this.item.nextEpisode != null)
|
|
||||||
this.router.navigate(["/watch/" + this.item.nextEpisode.link], { queryParamsHandling: "merge", replaceUrl: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
previous()
|
|
||||||
{
|
|
||||||
if (this.item.previousEpisode != null)
|
|
||||||
this.router.navigate(["/watch/" + this.item.previousEpisode], { queryParamsHandling: "merge", replaceUrl: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
getTimeFromSeekbar(progressBar: HTMLElement, pageX: number)
|
|
||||||
{
|
|
||||||
return Math.max(0, Math.min((pageX - progressBar.offsetLeft) / progressBar.clientWidth, 1)) * this.item.duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
setDuration(duration: number)
|
|
||||||
{
|
|
||||||
this.maxSeconds = Math.round(duration % 60);
|
|
||||||
this.maxMinutes = Math.round(duration / 60 % 60);
|
|
||||||
this.maxHours = Math.round(duration / 3600);
|
|
||||||
|
|
||||||
this.item.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTime(time: number)
|
|
||||||
{
|
|
||||||
this.hours = Math.round(time / 60 % 60);
|
|
||||||
this.seconds = Math.round(time % 60);
|
|
||||||
this.minutes = Math.round(time / 60);
|
|
||||||
|
|
||||||
this.thumb.style.transform = "translateX(" + (time / this.item.duration * 100) + "%)";
|
|
||||||
this.progress.style.width = (time / this.item.duration * 100) + "%";
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy()
|
|
||||||
{
|
|
||||||
if (document.fullscreen)
|
|
||||||
document.exitFullscreen();
|
|
||||||
|
|
||||||
document.getElementById("nav").classList.remove("d-none");
|
|
||||||
this.title.setTitle("Kyoo");
|
|
||||||
|
|
||||||
$(document).unbind();
|
|
||||||
$(window).unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
videoClicked()
|
|
||||||
{
|
|
||||||
if (!navigator.userAgent.match(/Mobi/))
|
|
||||||
this.tooglePlayback();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($("#hover").hasClass("idle"))
|
|
||||||
{
|
|
||||||
$("#hover").removeClass("idle");
|
|
||||||
clearTimeout(this.videoHider);
|
|
||||||
this.videoHider = setTimeout((ev: MouseEvent) =>
|
|
||||||
{
|
|
||||||
if (!this.player.paused)
|
|
||||||
{
|
|
||||||
document.getElementById("hover").classList.add("idle");
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$("#hover").addClass("idle");
|
|
||||||
clearTimeout(this.videoHider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tooglePlayback()
|
|
||||||
{
|
|
||||||
if (this.player.paused)
|
|
||||||
this.player.play();
|
|
||||||
else
|
|
||||||
this.player.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
toogleMute()
|
|
||||||
{
|
|
||||||
if (this.player.muted)
|
|
||||||
this.player.muted = false;
|
|
||||||
else
|
|
||||||
this.player.muted = true;
|
|
||||||
|
|
||||||
this.updateVolumeBtn()
|
|
||||||
}
|
|
||||||
|
|
||||||
initPlayBtn()
|
|
||||||
{
|
|
||||||
this.playIcon = "pause";
|
|
||||||
this.playTooltip = "Pause";
|
|
||||||
}
|
|
||||||
|
|
||||||
fullscreen()
|
|
||||||
{
|
|
||||||
if (document.fullscreenElement == null)
|
|
||||||
document.body.requestFullscreen();
|
|
||||||
else
|
|
||||||
document.exitFullscreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Value from 0 to 100
|
|
||||||
changeVolume(value: number)
|
|
||||||
{
|
|
||||||
value = Math.max(0, Math.min(value, 100));
|
|
||||||
|
|
||||||
this.player.muted = false;
|
|
||||||
this.player.volume = value / 100;
|
|
||||||
this.volume = value;
|
|
||||||
|
|
||||||
this.updateVolumeBtn();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateVolumeBtn()
|
|
||||||
{
|
|
||||||
if (this.player.muted)
|
|
||||||
{
|
|
||||||
this.volumeIcon = "volume_off"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (this.volume == 0)
|
|
||||||
this.volumeIcon = "volume_off";
|
|
||||||
else if (this.volume < 25)
|
|
||||||
this.volumeIcon = "volume_mute";
|
|
||||||
else if (this.volume < 65)
|
|
||||||
this.volumeIcon = "volume_down";
|
|
||||||
else
|
|
||||||
this.volumeIcon = "volume_up";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectSubtitle(subtitle: Track, changeUrl: boolean = true)
|
|
||||||
{
|
|
||||||
if (changeUrl)
|
|
||||||
{
|
|
||||||
let subSlug: string;
|
|
||||||
if (subtitle != null)
|
|
||||||
{
|
|
||||||
subSlug = subtitle.language;
|
|
||||||
if (subtitle.isForced)
|
|
||||||
subSlug += "-for";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.router.navigate([], { relativeTo: this.route, queryParams: { sub: subSlug }, replaceUrl: true, queryParamsHandling: "merge" });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedSubtitle = subtitle;
|
|
||||||
|
|
||||||
if (subtitle == null)
|
|
||||||
{
|
|
||||||
this.snackBar.open("Subtitle removed.", null, { verticalPosition: "top", horizontalPosition: "right", duration: 750, panelClass: "info-panel" });
|
|
||||||
SubtitleManager.remove(this.player);
|
|
||||||
this.removeHtmlTrack();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.snackBar.open(subtitle.displayName + " subtitle loaded.", null, { verticalPosition: "top", horizontalPosition: "right", duration: 750, panelClass: "info-panel" });
|
|
||||||
this.removeHtmlTrack();
|
|
||||||
|
|
||||||
if (subtitle.codec == "ass")
|
|
||||||
SubtitleManager.add(this.player, subtitle.link, true);
|
|
||||||
|
|
||||||
else if (subtitle.codec == "subrip")
|
|
||||||
{
|
|
||||||
SubtitleManager.remove(this.player);
|
|
||||||
|
|
||||||
let track = document.createElement("track");
|
|
||||||
track.kind = "subtitles";
|
|
||||||
track.label = subtitle.displayName;
|
|
||||||
track.srclang = subtitle.language;
|
|
||||||
track.src = subtitle.link.replace(".srt", ".vtt");
|
|
||||||
track.classList.add("subtitle_container");
|
|
||||||
track.default = true;
|
|
||||||
track.onload = () =>
|
|
||||||
{
|
|
||||||
this.player.textTracks[0].mode = "showing";
|
|
||||||
};
|
|
||||||
this.player.appendChild(track);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSupportedFeature(feature: string) : string
|
|
||||||
{
|
|
||||||
if (!this.supportList)
|
|
||||||
return "help";
|
|
||||||
switch (feature)
|
|
||||||
{
|
|
||||||
case "container":
|
|
||||||
return this.supportList.container ? "check_circle" : "cancel";
|
|
||||||
case "video":
|
|
||||||
return this.supportList.videoCodec ? "check_circle" : "cancel";
|
|
||||||
case "audio":
|
|
||||||
return this.supportList.audioCodec ? "check_circle" : "cancel";
|
|
||||||
default:
|
|
||||||
return "help";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeHtmlTrack()
|
|
||||||
{
|
|
||||||
let elements = this.player.getElementsByTagName("track");
|
|
||||||
if (elements.length > 0)
|
|
||||||
elements.item(0).remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
getThumb(url: string)
|
|
||||||
{
|
|
||||||
return this.sanitizer.bypassSecurityTrustStyle("url(" + url + ")");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
::cue
|
|
||||||
{
|
|
||||||
background-color: transparent;
|
|
||||||
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<div *ngIf="items.shows.length > 0" class="container-fluid mt-3">
|
|
||||||
<h3>Shows</h3>
|
|
||||||
</div>
|
|
||||||
<app-shows-list [shows]="items.shows"></app-shows-list>
|
|
||||||
<div *ngIf="items.episodes.length > 0" class="container-fluid mt-5">
|
|
||||||
<h3>Episodes</h3>
|
|
||||||
</div>
|
|
||||||
<app-episodes-list displayShowTitle="true" [episodes]="items.episodes"></app-episodes-list>
|
|
||||||
<div *ngIf="items.people.length > 0" class="container-fluid mt-5">
|
|
||||||
<h3>People</h3>
|
|
||||||
</div>
|
|
||||||
<app-people-list [people]="items.people"></app-people-list>
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { SearchComponent } from './search.component';
|
|
||||||
|
|
||||||
describe('SearchComponent', () => {
|
|
||||||
let component: SearchComponent;
|
|
||||||
let fixture: ComponentFixture<SearchComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ SearchComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(SearchComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,39 +0,0 @@
|
|||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
|
||||||
import { SearchResut } from "../../models/search-result";
|
|
||||||
import { Title } from "@angular/platform-browser";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-search',
|
|
||||||
templateUrl: './search.component.html',
|
|
||||||
styleUrls: ['./search.component.scss']
|
|
||||||
})
|
|
||||||
export class SearchComponent implements OnInit, OnDestroy
|
|
||||||
{
|
|
||||||
items: SearchResut;
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private title: Title) { }
|
|
||||||
|
|
||||||
ngOnInit()
|
|
||||||
{
|
|
||||||
this.route.data.subscribe((data) =>
|
|
||||||
{
|
|
||||||
this.items = data.items;
|
|
||||||
this.title.setTitle(this.items.query + " - Kyoo");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterViewInit()
|
|
||||||
{
|
|
||||||
let searchBar: HTMLInputElement = <HTMLInputElement>document.getElementById("search");
|
|
||||||
searchBar.classList.add("searching");
|
|
||||||
searchBar.value = this.items.query;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy()
|
|
||||||
{
|
|
||||||
let searchBar: HTMLInputElement = <HTMLInputElement>document.getElementById("search");
|
|
||||||
searchBar.classList.remove("searching");
|
|
||||||
searchBar.value = "";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators'
|
|
||||||
import { Collection } from "../../models/collection";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class CollectionResolverService implements Resolve<Collection>
|
|
||||||
{
|
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): Collection | Observable<Collection> | Promise<Collection>
|
|
||||||
{
|
|
||||||
let collection: string = route.paramMap.get("collection-slug");
|
|
||||||
return this.http.get<Collection>("api/collection/" + collection).pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
if (error.status == 404)
|
|
||||||
{
|
|
||||||
this.snackBar.open("Collection \"" + collection + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { Show } from "../../models/show";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class LibraryResolverService implements Resolve<Show[]>
|
|
||||||
{
|
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): Show[] | Observable<Show[]> | Promise<Show[]>
|
|
||||||
{
|
|
||||||
let slug: string = route.paramMap.get("library-slug");
|
|
||||||
|
|
||||||
if (slug == null)
|
|
||||||
{
|
|
||||||
return this.http.get<Show[]>("api/shows").pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return this.http.get<Show[]>("api/libraries/" + slug).pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
if (error.status == 404)
|
|
||||||
{
|
|
||||||
this.snackBar.open("Library \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { Collection } from "../../models/collection";
|
|
||||||
import { People } from "../../models/people";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class PeopleResolverService implements Resolve<Collection>
|
|
||||||
{
|
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): Collection | Observable<Collection> | Promise<Collection>
|
|
||||||
{
|
|
||||||
let people: string = route.paramMap.get("people-slug");
|
|
||||||
return this.http.get<Collection>("api/people/" + people).pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
if (error.status == 404)
|
|
||||||
{
|
|
||||||
this.snackBar.open("People \"" + people + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { SearchResut } from "../../models/search-result";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class SearchResolverService implements Resolve<SearchResut>
|
|
||||||
{
|
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): SearchResut | Observable<SearchResut> | Promise<SearchResut>
|
|
||||||
{
|
|
||||||
let query: string = route.paramMap.get("query");
|
|
||||||
return this.http.get<SearchResut>("api/search/" + query).pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { Show } from "../../models/show";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ShowResolverService implements Resolve<Show>
|
|
||||||
{
|
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): Show | Observable<Show> | Promise<Show>
|
|
||||||
{
|
|
||||||
let slug: string = route.paramMap.get("show-slug");
|
|
||||||
return this.http.get<Show>("api/shows/" + slug).pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
if (error.status == 404)
|
|
||||||
{
|
|
||||||
this.snackBar.open("Show \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { WatchItem } from "../../models/watch-item";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class StreamResolverService implements Resolve<WatchItem>
|
|
||||||
{
|
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): WatchItem | Observable<WatchItem> | Promise<WatchItem>
|
|
||||||
{
|
|
||||||
let item: string = route.paramMap.get("item");
|
|
||||||
return this.http.get<WatchItem>("api/watch/" + item).pipe(catchError((error: HttpErrorResponse) =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
if (error.status == 404)
|
|
||||||
{
|
|
||||||
this.snackBar.open("Episode \"" + item + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
}
|
|
||||||
return EMPTY;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
<div class="backdrop">
|
|
||||||
<img id="backdrop" src="backdrop/{{this.show.slug}}" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="header container pt-sm-5">
|
|
||||||
<div class="row">
|
|
||||||
<img class="poster d-none d-sm-block" src="poster/{{this.show.slug}}" />
|
|
||||||
<div class="main col">
|
|
||||||
<div class="info">
|
|
||||||
<h1 class="title">{{this.show.title}}</h1>
|
|
||||||
<h2 class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</h2>
|
|
||||||
<ng-template #elseBlock><h2 class="date">{{show.startYear}}</h2></ng-template>
|
|
||||||
</div>
|
|
||||||
<div class="buttons">
|
|
||||||
<button mat-mini-fab matTooltipPosition="above" matTooltip="Play" class="mr-3">
|
|
||||||
<mat-icon>play_arrow</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="this.show.trailerUrl" mat-icon-button matTooltipPosition="above" matTooltip="Trailer">
|
|
||||||
<mat-icon>local_movies</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" matTooltip="Download">
|
|
||||||
<mat-icon>cloud_download</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" matTooltip="Watched">
|
|
||||||
<mat-icon>done</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-icon-button matTooltipPosition="above" matTooltip="More">
|
|
||||||
<mat-icon>more_horiz</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-3 secondary d-none d-md-block">
|
|
||||||
<img src="logo/{{this.show.slug}}" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p>Studio: <b><a routerLink="/studio/{{this.show.studio.slug}}">{{this.show.studio.name}}</a></b></p>
|
|
||||||
<p>Directors: <b></b></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row pt-3 d-md-none">
|
|
||||||
<div class="col">
|
|
||||||
<p class="mr-1 d-inline-block">Studio: <b><a routerLink="/studio/{{this.show.studio.slug}}">{{this.show.studio.name}}</a></b></p>
|
|
||||||
<p class="d-inline-block">Directors: <b></b></p>
|
|
||||||
<div class="d-sm-none">
|
|
||||||
<p>Genres: <span *ngFor="let genre of this.show.genres; let isLast = last"><b><a routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b>{{isLast ? "" : ", "}}</span></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row pt-3">
|
|
||||||
<div class="col">
|
|
||||||
<p class="text-justify overview">{{this.show.overview}}</p>
|
|
||||||
</div>
|
|
||||||
<hr class="d-none d-sm-block">
|
|
||||||
<div class="col-3 d-none d-sm-block">
|
|
||||||
<h3 style="opacity: .8;">Genres</h3>
|
|
||||||
<ul>
|
|
||||||
<li *ngFor="let genre of this.show.genres"><b><a class="genre" routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container-fluid mt-3">
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-label>Season</mat-label>
|
|
||||||
<mat-select [(value)]="season" (selectionChange)="getEpisodes()">
|
|
||||||
<mat-option *ngFor="let season of this.show.seasons" [value]="season.seasonNumber">{{season.title}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<app-episodes-list [episodes]="episodes"></app-episodes-list>
|
|
||||||
|
|
||||||
<div class="container-fluid mt-5">
|
|
||||||
<h3>Staff</h3>
|
|
||||||
</div>
|
|
||||||
<app-people-list [people]="show.people"></app-people-list>
|
|
@ -1,152 +0,0 @@
|
|||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
@import "~bootstrap/scss/mixins/breakpoints";
|
|
||||||
|
|
||||||
a
|
|
||||||
{
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.backdrop
|
|
||||||
{
|
|
||||||
margin-top: -68px;
|
|
||||||
position: relative;
|
|
||||||
z-index: -1;
|
|
||||||
|
|
||||||
> img
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
max-height: 75vh;
|
|
||||||
object-fit: cover;
|
|
||||||
min-height: 20vh;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
min-height: 60vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after
|
|
||||||
{
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.6) 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header
|
|
||||||
{
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
margin-top: -12rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
margin-top: -13rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(lg)
|
|
||||||
{
|
|
||||||
margin-top: -19rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(xl)
|
|
||||||
{
|
|
||||||
margin-top: -23rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.poster
|
|
||||||
{
|
|
||||||
width: 33%;
|
|
||||||
background-color: #333333;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main
|
|
||||||
{
|
|
||||||
align-self: center;
|
|
||||||
padding-left: 2.5em;
|
|
||||||
|
|
||||||
.info
|
|
||||||
{
|
|
||||||
margin-top: -3.25rem;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title
|
|
||||||
{
|
|
||||||
font-weight: 900 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date
|
|
||||||
{
|
|
||||||
font-weight: 300 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons
|
|
||||||
{
|
|
||||||
> button
|
|
||||||
{
|
|
||||||
outline: none;
|
|
||||||
margin: .3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary
|
|
||||||
{
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
> img
|
|
||||||
{
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div > p
|
|
||||||
{
|
|
||||||
opacity: .87;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.overview
|
|
||||||
{
|
|
||||||
opacity: .87;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
padding-top: 2.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr
|
|
||||||
{
|
|
||||||
margin: 0 10px 0 10px;
|
|
||||||
border-right: 1px solid rgba(255, 255, 255, .60);
|
|
||||||
border-top: 0;
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.genre
|
|
||||||
{
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ShowDetailsComponent } from './show-details.component';
|
|
||||||
|
|
||||||
describe('ShowDetailsComponent', () => {
|
|
||||||
let component: ShowDetailsComponent;
|
|
||||||
let fixture: ComponentFixture<ShowDetailsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ ShowDetailsComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ShowDetailsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,82 +0,0 @@
|
|||||||
import { HttpClient } from "@angular/common/http";
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
|
||||||
import { Title } from '@angular/platform-browser';
|
|
||||||
import { ActivatedRoute } from '@angular/router';
|
|
||||||
import { Episode } from "../../models/episode";
|
|
||||||
import { Show } from "../../models/show";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-show-details',
|
|
||||||
templateUrl: './show-details.component.html',
|
|
||||||
styleUrls: ['./show-details.component.scss']
|
|
||||||
})
|
|
||||||
export class ShowDetailsComponent implements OnInit
|
|
||||||
{
|
|
||||||
show: Show;
|
|
||||||
episodes: Episode[] = null;
|
|
||||||
season: number;
|
|
||||||
|
|
||||||
private toolbar: HTMLElement;
|
|
||||||
private backdrop: HTMLElement;
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private http: HttpClient, private snackBar: MatSnackBar, private title: Title)
|
|
||||||
{
|
|
||||||
this.route.queryParams.subscribe(params =>
|
|
||||||
{
|
|
||||||
this.season = params["season"];
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route.data.subscribe(data =>
|
|
||||||
{
|
|
||||||
this.show = data.show;
|
|
||||||
this.title.setTitle(this.show.title + " - Kyoo");
|
|
||||||
|
|
||||||
if (this.season == null || this.show.seasons.find(x => x.seasonNumber == this.season) == null)
|
|
||||||
this.season = 1;
|
|
||||||
|
|
||||||
this.getEpisodes();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit()
|
|
||||||
{
|
|
||||||
this.toolbar = document.getElementById("toolbar");
|
|
||||||
this.backdrop = document.getElementById("backdrop");
|
|
||||||
window.addEventListener("scroll", this.scroll, true);
|
|
||||||
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, 0) !important`);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy()
|
|
||||||
{
|
|
||||||
window.removeEventListener("scroll", this.scroll, true);
|
|
||||||
this.title.setTitle("Kyoo");
|
|
||||||
this.toolbar.setAttribute("style", `background-color: #000000 !important`);
|
|
||||||
}
|
|
||||||
|
|
||||||
scroll = () =>
|
|
||||||
{
|
|
||||||
let opacity: number = 2 * window.scrollY / this.backdrop.clientHeight;
|
|
||||||
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, ${opacity}) !important`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getEpisodes()
|
|
||||||
{
|
|
||||||
if (this.show == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.show.seasons.find(x => x.seasonNumber == this.season).episodes != null)
|
|
||||||
this.episodes = this.show.seasons.find(x => x.seasonNumber == this.season).episodes;
|
|
||||||
|
|
||||||
|
|
||||||
this.http.get<Episode[]>("api/episodes/" + this.show.slug + "/season/" + this.season).subscribe((episodes: Episode[]) =>
|
|
||||||
{
|
|
||||||
this.show.seasons.find(x => x.seasonNumber == this.season).episodes = episodes;
|
|
||||||
this.episodes = episodes;
|
|
||||||
}, error =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
this.snackBar.open("An unknow error occured while getting episodes.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<div class="scroll-row mb-5">
|
|
||||||
<div class="shows-container" #scrollView (scroll)="onScroll()">
|
|
||||||
<a class="show" *ngFor="let show of this.shows" routerLink="/show/{{show.slug}}" href="/show/{{show.slug}}">
|
|
||||||
<div matRipple [style.background-image]="getThumb(show.slug)"> </div>
|
|
||||||
<p class="title">{{show.title}}</p>
|
|
||||||
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
|
|
||||||
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
|
|
||||||
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
|
|
||||||
</div>
|
|
@ -1,148 +0,0 @@
|
|||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
@import "~bootstrap/scss/mixins/breakpoints";
|
|
||||||
|
|
||||||
.shows-container
|
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-width: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar
|
|
||||||
{
|
|
||||||
height: 4px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb
|
|
||||||
{
|
|
||||||
background-color: #999;
|
|
||||||
border-radius: 90px;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
background-color: rgb(134, 127, 127);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.show
|
|
||||||
{
|
|
||||||
width: 33%;
|
|
||||||
min-width: 120px;
|
|
||||||
max-width: 200px;
|
|
||||||
list-style: none;
|
|
||||||
padding: .5em;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
outline: none;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm)
|
|
||||||
{
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md)
|
|
||||||
{
|
|
||||||
width: 20%;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(lg)
|
|
||||||
{
|
|
||||||
width: 18%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(xl)
|
|
||||||
{
|
|
||||||
width: 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&:focus, &:hover
|
|
||||||
{
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
outline: solid var(--accentColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .title
|
|
||||||
{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> div
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
padding-top: 147.0588%;
|
|
||||||
background-size: cover;
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
> p
|
|
||||||
{
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
&.date
|
|
||||||
{
|
|
||||||
opacity: 0.8;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-row
|
|
||||||
{
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:host-context(.hoverEnabled) &:hover
|
|
||||||
{
|
|
||||||
.scrollBtn
|
|
||||||
{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollBtn
|
|
||||||
{
|
|
||||||
padding: 0;
|
|
||||||
outline: none;
|
|
||||||
min-width: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 30%;
|
|
||||||
bottom: 40%;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&.leftBtn
|
|
||||||
{
|
|
||||||
left: 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.rightBtn
|
|
||||||
{
|
|
||||||
right: 0;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-left: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ShowsListComponent } from './shows-list.component';
|
|
||||||
|
|
||||||
describe('ShowsListComponent', () => {
|
|
||||||
let component: ShowsListComponent;
|
|
||||||
let fixture: ComponentFixture<ShowsListComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ ShowsListComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ShowsListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,48 +0,0 @@
|
|||||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
|
|
||||||
import { MatButton } from "@angular/material/button";
|
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
|
||||||
import { Show } from "../../models/show";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-shows-list',
|
|
||||||
templateUrl: './shows-list.component.html',
|
|
||||||
styleUrls: ['./shows-list.component.scss']
|
|
||||||
})
|
|
||||||
export class ShowsListComponent
|
|
||||||
{
|
|
||||||
@Input() shows: Show[];
|
|
||||||
@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
|
|
||||||
@ViewChild("leftBtn", { static: false }) private leftBtn: MatButton;
|
|
||||||
@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
|
|
||||||
|
|
||||||
constructor(private sanitizer: DomSanitizer) { }
|
|
||||||
|
|
||||||
scrollLeft()
|
|
||||||
{
|
|
||||||
let scroll: number = this.scrollView.nativeElement.offsetWidth * 0.80;
|
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollRight()
|
|
||||||
{
|
|
||||||
let scroll: number = this.scrollView.nativeElement.offsetWidth * 0.80;
|
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll()
|
|
||||||
{
|
|
||||||
if (this.scrollView.nativeElement.scrollLeft <= 0)
|
|
||||||
this.leftBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.leftBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
if (this.scrollView.nativeElement.scrollLeft >= this.scrollView.nativeElement.scrollWidth - this.scrollView.nativeElement.clientWidth)
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
}
|
|
||||||
|
|
||||||
getThumb(slug: string)
|
|
||||||
{
|
|
||||||
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
export const environment = {
|
|
||||||
production: true
|
|
||||||
};
|
|
@ -1,16 +0,0 @@
|
|||||||
// This file can be replaced during build by using the `fileReplacements` array.
|
|
||||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
|
||||||
// The list of file replacements can be found in `angular.json`.
|
|
||||||
|
|
||||||
export const environment = {
|
|
||||||
production: false
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For easier debugging in development mode, you can import the following file
|
|
||||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
|
||||||
*
|
|
||||||
* This import should be commented out in production mode because it will have a negative impact
|
|
||||||
* on performance if an error is thrown.
|
|
||||||
*/
|
|
||||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
|
@ -1,15 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Kyoo</title>
|
|
||||||
<base href="/">
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<app-root></app-root>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,23 +0,0 @@
|
|||||||
.subtitle_container {
|
|
||||||
line-height: normal;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
transform-origin: 0 0 0;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.subtitle_container text, .subtitle_container path {
|
|
||||||
dominant-baseline: text-before-edge;
|
|
||||||
text-anchor: start;
|
|
||||||
transform-box: view-box;
|
|
||||||
paint-order: stroke;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.subtitle_container tspan {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
.subtitle_container mask path {
|
|
||||||
fill: white;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +0,0 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
|
||||||
import { environment } from './environments/environment';
|
|
||||||
import "hammerjs"
|
|
||||||
|
|
||||||
if (environment.production) {
|
|
||||||
enableProdMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
|
||||||
.catch(err => console.error(err));
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=collection.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"collection.js","sourceRoot":"","sources":["collection.ts"],"names":[],"mappings":""}
|
|
@ -1,12 +0,0 @@
|
|||||||
import { Show } from "./show";
|
|
||||||
|
|
||||||
export interface Collection
|
|
||||||
{
|
|
||||||
slug: string;
|
|
||||||
name: string;
|
|
||||||
poster: string;
|
|
||||||
overview: string;
|
|
||||||
startYear: number,
|
|
||||||
endYear: number,
|
|
||||||
shows: Show[];
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=episode.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"episode.js","sourceRoot":"","sources":["episode.ts"],"names":[],"mappings":""}
|
|
@ -1,13 +0,0 @@
|
|||||||
export interface Episode
|
|
||||||
{
|
|
||||||
seasonNumber: number;
|
|
||||||
episodeNumber: number;
|
|
||||||
title: string;
|
|
||||||
thumb: string;
|
|
||||||
link: string;
|
|
||||||
overview: string;
|
|
||||||
releaseDate;
|
|
||||||
runtime: number;
|
|
||||||
externalIDs: string;
|
|
||||||
showTitle: string;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=genre.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"genre.js","sourceRoot":"","sources":["genre.ts"],"names":[],"mappings":""}
|
|
@ -1,5 +0,0 @@
|
|||||||
export interface Genre
|
|
||||||
{
|
|
||||||
slug: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=people.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"people.js","sourceRoot":"","sources":["people.ts"],"names":[],"mappings":""}
|
|
@ -1,9 +0,0 @@
|
|||||||
export interface People
|
|
||||||
{
|
|
||||||
slug: string;
|
|
||||||
name: string;
|
|
||||||
role: string;
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
externalIDs: string;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=search-result.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"search-result.js","sourceRoot":"","sources":["search-result.ts"],"names":[],"mappings":""}
|
|
@ -1,15 +0,0 @@
|
|||||||
import { Show } from "./show";
|
|
||||||
import { Episode } from "./episode";
|
|
||||||
import { People } from "./people";
|
|
||||||
import { Studio } from "./studio";
|
|
||||||
import { Genre } from "./genre";
|
|
||||||
|
|
||||||
export interface SearchResut
|
|
||||||
{
|
|
||||||
query: string;
|
|
||||||
shows: Show[];
|
|
||||||
episodes: Episode[];
|
|
||||||
people: People[];
|
|
||||||
genrwes: Genre[];
|
|
||||||
studios: Studio[];
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=season.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"season.js","sourceRoot":"","sources":["season.ts"],"names":[],"mappings":""}
|
|
@ -1,9 +0,0 @@
|
|||||||
import { Episode } from "./episode";
|
|
||||||
|
|
||||||
export interface Season
|
|
||||||
{
|
|
||||||
seasonNumber: number;
|
|
||||||
title: string;
|
|
||||||
overview: string;
|
|
||||||
episodes: Episode[];
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=show.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"show.js","sourceRoot":"","sources":["show.ts"],"names":[],"mappings":""}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { Season } from "./season";
|
|
||||||
import { Genre } from "./genre";
|
|
||||||
import { People } from "./people";
|
|
||||||
import { Studio } from "./studio";
|
|
||||||
|
|
||||||
export interface Show
|
|
||||||
{
|
|
||||||
slug: string;
|
|
||||||
title: string;
|
|
||||||
Aliases: string[];
|
|
||||||
overview: string;
|
|
||||||
genres: Genre[];
|
|
||||||
status: string;
|
|
||||||
studio: Studio;
|
|
||||||
directors: People[];
|
|
||||||
people: People[];
|
|
||||||
seasons: Season[];
|
|
||||||
trailerUrl: string;
|
|
||||||
isCollection: boolean;
|
|
||||||
|
|
||||||
startYear: number;
|
|
||||||
endYear : number;
|
|
||||||
|
|
||||||
externalIDs: string;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=studio.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"studio.js","sourceRoot":"","sources":["studio.ts"],"names":[],"mappings":""}
|
|
@ -1,5 +0,0 @@
|
|||||||
export interface Studio
|
|
||||||
{
|
|
||||||
slug: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
//# sourceMappingURL=watch-item.js.map
|
|
@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"watch-item.js","sourceRoot":"","sources":["watch-item.ts"],"names":[],"mappings":""}
|
|
@ -1,32 +0,0 @@
|
|||||||
import { Episode } from "./episode";
|
|
||||||
|
|
||||||
export interface WatchItem
|
|
||||||
{
|
|
||||||
showTitle: string;
|
|
||||||
showSlug: string;
|
|
||||||
seasonNumber: number;
|
|
||||||
episodeNumber: number;
|
|
||||||
title: string;
|
|
||||||
link: string;
|
|
||||||
duration: number;
|
|
||||||
releaseDate;
|
|
||||||
|
|
||||||
previousEpisode: string;
|
|
||||||
nextEpisode: Episode;
|
|
||||||
|
|
||||||
container: string;
|
|
||||||
video: Track;
|
|
||||||
audios: Track[];
|
|
||||||
subtitles: Track[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Track
|
|
||||||
{
|
|
||||||
displayName: string;
|
|
||||||
title: string;
|
|
||||||
language: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
isForced: boolean;
|
|
||||||
codec: string;
|
|
||||||
link: string;
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
|
||||||
* You can add your own extra polyfills to this file.
|
|
||||||
*
|
|
||||||
* This file is divided into 2 sections:
|
|
||||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
|
||||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
|
||||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
|
||||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
|
||||||
*
|
|
||||||
* Learn more in https://angular.io/guide/browser-support
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* BROWSER POLYFILLS
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
|
||||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Web Animations `@angular/platform-browser/animations`
|
|
||||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
|
||||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
|
||||||
*/
|
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
|
||||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
|
||||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
|
||||||
* will put import in the top of bundle, so user need to create a separate file
|
|
||||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
|
||||||
* into that file, and then add the following code before importing zone.js.
|
|
||||||
* import './zone-flags.ts';
|
|
||||||
*
|
|
||||||
* The flags allowed in zone-flags.ts are listed here.
|
|
||||||
*
|
|
||||||
* The following flags will work for all browsers.
|
|
||||||
*
|
|
||||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
|
||||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
|
||||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
|
||||||
*
|
|
||||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
|
||||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
|
||||||
*
|
|
||||||
* (window as any).__Zone_enable_cross_context_check = true;
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* Zone JS is required by default for Angular itself.
|
|
||||||
*/
|
|
||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* APPLICATION IMPORTS
|
|
||||||
*/
|
|
@ -1,105 +0,0 @@
|
|||||||
//Roboto font
|
|
||||||
@import "../../wwwroot/roboto/sass/roboto.scss";
|
|
||||||
|
|
||||||
//Bootstrap configuration
|
|
||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
|
|
||||||
$theme-colors: (
|
|
||||||
"primary": #0a1128,
|
|
||||||
"secondary": #000000,
|
|
||||||
"accentColor": #e23c00,
|
|
||||||
"textPrimary": #ffffff
|
|
||||||
);
|
|
||||||
|
|
||||||
$body-bg: theme-color("primary");
|
|
||||||
$body-color: theme-color("textPrimary");
|
|
||||||
$font-family-base: "Roboto", Arial, sans-serif;
|
|
||||||
|
|
||||||
p
|
|
||||||
{
|
|
||||||
opacity: .6;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6
|
|
||||||
{
|
|
||||||
opacity: .87;
|
|
||||||
}
|
|
||||||
|
|
||||||
@import "~bootstrap/scss/bootstrap";
|
|
||||||
|
|
||||||
|
|
||||||
//Material Angular Configuration
|
|
||||||
@import '~@angular/material/theming';
|
|
||||||
@include mat-core();
|
|
||||||
|
|
||||||
// Define the default theme (same as the example above).
|
|
||||||
$primary: (default: #0a1128);
|
|
||||||
$accent: (default: #e23c00, lighter: #ff9149);
|
|
||||||
$theme: mat-dark-theme($primary, $accent);
|
|
||||||
|
|
||||||
// Include the default theme styles.
|
|
||||||
@include angular-material-theme($theme);
|
|
||||||
|
|
||||||
.mat-ripple-element
|
|
||||||
{
|
|
||||||
background-color: rgba(255, 255, 255, .3) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-card-header-text
|
|
||||||
{
|
|
||||||
margin: 0 5px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Material Icons
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Material Icons';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
src: url(/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
|
|
||||||
src: local('Material Icons'),
|
|
||||||
local('MaterialIcons-Regular'),
|
|
||||||
url(/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
|
|
||||||
url(/iconfont/MaterialIcons-Regular.woff) format('woff'),
|
|
||||||
url(/iconfont/MaterialIcons-Regular.ttf) format('truetype');
|
|
||||||
}
|
|
||||||
|
|
||||||
.material-icons
|
|
||||||
{
|
|
||||||
font-family: 'Material Icons';
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 24px; /* Preferred icon size */
|
|
||||||
display: inline-block;
|
|
||||||
line-height: 1;
|
|
||||||
text-transform: none;
|
|
||||||
letter-spacing: normal;
|
|
||||||
word-wrap: normal;
|
|
||||||
white-space: nowrap;
|
|
||||||
direction: ltr;
|
|
||||||
/* Support for all WebKit browsers. */
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
/* Support for Safari and Chrome. */
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
/* Support for Firefox. */
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
/* Support for IE. */
|
|
||||||
font-feature-settings: 'liga';
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-icon
|
|
||||||
{
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackError
|
|
||||||
{
|
|
||||||
background-color: theme-color("accentColor");
|
|
||||||
color: theme-color("textPrimary");
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-row
|
|
||||||
{
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
||||||
|
|
||||||
import 'zone.js/dist/zone-testing';
|
|
||||||
import { getTestBed } from '@angular/core/testing';
|
|
||||||
import {
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting
|
|
||||||
} from '@angular/platform-browser-dynamic/testing';
|
|
||||||
|
|
||||||
declare const require: any;
|
|
||||||
|
|
||||||
// First, initialize the Angular testing environment.
|
|
||||||
getTestBed().initTestEnvironment(
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting()
|
|
||||||
);
|
|
||||||
// Then we find all the tests.
|
|
||||||
const context = require.context('./', true, /\.spec\.ts$/);
|
|
||||||
// And load the modules.
|
|
||||||
context.keys().map(context);
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user