├── src ├── assets │ ├── .gitkeep │ ├── images │ │ ├── background.jpg │ │ ├── download.png │ │ ├── rss-icon.png │ │ ├── image-spinner.gif │ │ ├── banner-quality │ │ │ └── banner1080p.png │ │ ├── icon-search.svg │ │ └── logo-YTS.svg │ ├── fonts │ │ └── arimo │ │ │ ├── Arimo-Bold.ttf │ │ │ ├── Arimo-Italic.ttf │ │ │ ├── Arimo-Regular.ttf │ │ │ └── Arimo-BoldItalic.ttf │ └── css │ │ ├── footer.css │ │ ├── header.css │ │ └── main.css ├── app │ ├── components │ │ ├── home │ │ │ ├── home.component.css │ │ │ ├── home.component.spec.ts │ │ │ ├── home.component.ts │ │ │ └── home.component.html │ │ └── treading-movies │ │ │ ├── treading-movies.component.css │ │ │ ├── treading-movies.component.spec.ts │ │ │ ├── treading-movies.component.ts │ │ │ └── treading-movies.component.html │ ├── layout │ │ ├── footer │ │ │ ├── footer.component.css │ │ │ ├── footer.component.ts │ │ │ ├── footer.component.spec.ts │ │ │ └── footer.component.html │ │ └── header │ │ │ ├── header.component.css │ │ │ ├── header.component.ts │ │ │ ├── header.component.spec.ts │ │ │ └── header.component.html │ ├── app.component.html │ ├── app.component.spec.ts │ ├── core │ │ ├── interceptor │ │ │ ├── interceptor.interceptor.spec.ts │ │ │ └── interceptor.interceptor.ts │ │ └── spiner │ │ │ ├── spiner.service.spec.ts │ │ │ └── spiner.service.ts │ ├── app-routing.module.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── shared │ │ └── Interfaces.ts │ └── server │ │ └── services │ │ ├── movies.service.ts │ │ └── movies.service.spec.ts ├── favicon.ico ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── main.ts ├── index.html ├── styles.css ├── test.ts └── polyfills.ts ├── gitAddCommit.sh ├── .editorconfig ├── tsconfig.app.json ├── tsconfig.spec.json ├── .browserslistrc ├── .gitignore ├── tsconfig.json ├── README.md ├── package.json ├── karma.conf.js └── angular.json /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/layout/footer/footer.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/layout/header/header.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/components/treading-movies/treading-movies.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/images/background.jpg -------------------------------------------------------------------------------- /src/assets/images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/images/download.png -------------------------------------------------------------------------------- /src/assets/images/rss-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/images/rss-icon.png -------------------------------------------------------------------------------- /src/assets/fonts/arimo/Arimo-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/fonts/arimo/Arimo-Bold.ttf -------------------------------------------------------------------------------- /src/assets/images/image-spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/images/image-spinner.gif -------------------------------------------------------------------------------- /src/assets/fonts/arimo/Arimo-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/fonts/arimo/Arimo-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/arimo/Arimo-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/fonts/arimo/Arimo-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/arimo/Arimo-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/fonts/arimo/Arimo-BoldItalic.ttf -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | BASE_URL_SERVER_YTS_API:'https://yts.mx/api/v2/' 4 | }; 5 | -------------------------------------------------------------------------------- /src/assets/images/banner-quality/banner1080p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bacarPereira/YTS.MX-Clone-Angular/HEAD/src/assets/images/banner-quality/banner1080p.png -------------------------------------------------------------------------------- /gitAddCommit.sh: -------------------------------------------------------------------------------- 1 | state=true 2 | while [ $state ]; do 3 | echo 'Enter the commit message ✍:' 4 | read commitMessage 5 | git add . 6 | git commit -m "$commitMessage" 7 | echo 'Commit was add' 8 | done 9 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/app/layout/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.css'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/layout/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-header', 5 | templateUrl: './header.component.html', 6 | styleUrls: ['./header.component.css'] 7 | }) 8 | export class HeaderComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Official Home of YIFY Movies 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | }); 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /src/app/core/interceptor/interceptor.interceptor.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { InterceptorInterceptor } from './interceptor.interceptor'; 4 | 5 | describe('InterceptorInterceptor', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | providers: [ 8 | InterceptorInterceptor 9 | ] 10 | })); 11 | 12 | it('should be created', () => { 13 | const interceptor: InterceptorInterceptor = TestBed.inject(InterceptorInterceptor); 14 | expect(interceptor).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import '~font-awesome/css/font-awesome.min.css'; 2 | @import '~bootstrap/dist/css/bootstrap.css'; 3 | @import url(./assets/css/main.css); 4 | 5 | body{ 6 | text-align: center; 7 | background: #171717; 8 | } 9 | 10 | .spiner-wrapper { 11 | z-index: 99999999; 12 | position: fixed; 13 | top: 0; 14 | left: 0; 15 | bottom: 0; 16 | background: #4f525248; 17 | right: 0; 18 | opacity: 1; 19 | width: 100%; 20 | height: 100%; 21 | overflow: hidden 22 | } 23 | 24 | .image-spiner { 25 | width: 50px; 26 | height: 50px; 27 | text-align: center; 28 | margin-top: 25%; 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/css/footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | background: #171717; 3 | padding-top: 15px; 4 | padding-bottom: 15px; 5 | border-top: 1px solid #2f2f2f; 6 | } 7 | 8 | footer ul { 9 | list-style-type: none; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | .footer-menu li{ 15 | display: inline; 16 | font-family: 'Arimo-Bold', sans-serif; 17 | font-weight: bold; 18 | padding: 0px 6px 0px 6px; 19 | } 20 | 21 | .footer-menu li a { 22 | color: #919191; 23 | text-decoration: none !important; 24 | } 25 | 26 | .agreement-text{ 27 | color: #919191; 28 | text-decoration: none !important; 29 | } 30 | 31 | .agreement-text:hover{ 32 | color: #ffffff; 33 | } 34 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { HomeComponent } from './components/home/home.component'; 4 | import { TreadingMoviesComponent } from './components/treading-movies/treading-movies.component'; 5 | 6 | const routes: Routes = [ 7 | { path: 'home', component: HomeComponent }, 8 | { path: 'treading', component: TreadingMoviesComponent }, 9 | { path: '', pathMatch: 'full', redirectTo: '/home'}, 10 | { path: '**', pathMatch: 'full', redirectTo: '/home' } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forRoot(routes)], 15 | exports: [RouterModule] 16 | }) 17 | export class AppRoutingModule { } 18 | -------------------------------------------------------------------------------- /src/app/core/spiner/spiner.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SpinerService } from './spiner.service'; 4 | 5 | describe('SpinerService', () => { 6 | let service: SpinerService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(SpinerService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | 17 | it('should show spiner()', () => { 18 | service.showSpiner(); 19 | expect(service.isLoading).toBeTruthy(); 20 | }); 21 | 22 | it('should hide spiner()', () => { 23 | service.hideSpiner(); 24 | expect(service.isLoading).toBeFalsy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/layout/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/layout/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HeaderComponent } from './header.component'; 4 | 5 | describe('HeaderComponent', () => { 6 | let component: HeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ HeaderComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HeaderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 18 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { takeUntil } from 'rxjs/operators'; 4 | import { SpinerService } from './core/spiner/spiner.service'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html' 9 | }) 10 | export class AppComponent { 11 | public isLoading:boolean = false; 12 | private ngUnsubscribe = new Subject(); 13 | 14 | constructor( 15 | private spinerService:SpinerService, 16 | private cdr: ChangeDetectorRef 17 | ){ 18 | this.spinerService.isLoadingChange.pipe(takeUntil(this.ngUnsubscribe)).subscribe((stateLoadSpiner) => { 19 | this.isLoading = stateLoadSpiner; 20 | this.cdr.detectChanges(); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | .c9/ 21 | *.launch 22 | .settings/ 23 | *.sublime-workspace 24 | 25 | # IDE - VSCode 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | .history/* 32 | 33 | # misc 34 | /.sass-cache 35 | /connect.lock 36 | /coverage 37 | /libpeerconnection.log 38 | npm-debug.log 39 | yarn-error.log 40 | testem.log 41 | /typings 42 | 43 | # System Files 44 | .DS_Store 45 | Thumbs.db 46 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | BASE_URL_SERVER_YTS_API:'https://yts.mx/api/v2/list_movies.json' 8 | }; 9 | 10 | /* 11 | * For easier debugging in development mode, you can import the following file 12 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 13 | * 14 | * This import should be commented out in production mode because it will have a negative impact 15 | * on performance if an error is thrown. 16 | */ 17 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 18 | -------------------------------------------------------------------------------- /src/app/components/treading-movies/treading-movies.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TreadingMoviesComponent } from './treading-movies.component'; 4 | 5 | describe('TreadingMoviesComponent', () => { 6 | let component: TreadingMoviesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ TreadingMoviesComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TreadingMoviesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/treading-movies/treading-movies.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { MoviesService } from 'src/app/server/services/movies.service'; 3 | import { IMovie } from 'src/app/shared/Interfaces'; 4 | 5 | @Component({ 6 | selector: 'app-treading-movies', 7 | templateUrl: './treading-movies.component.html', 8 | styleUrls: ['./treading-movies.component.css'] 9 | }) 10 | export class TreadingMoviesComponent implements OnInit { 11 | 12 | treadingMovies:IMovie; 13 | 14 | constructor(private moviesService:MoviesService) { } 15 | 16 | ngOnInit(): void { 17 | this.getTreadingMoviesHttp(); 18 | } 19 | 20 | getTreadingMoviesHttp(){ 21 | this.moviesService.getTreadingMoviesHttp().subscribe(treadingMoviesResponse =>{ 22 | this.treadingMovies = treadingMoviesResponse; 23 | }) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | 4 | import { HomeComponent } from './home.component'; 5 | 6 | describe('HomeComponent', () => { 7 | let component: HomeComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | imports: [HttpClientTestingModule], 13 | declarations: [ HomeComponent ] 14 | }) 15 | .compileComponents(); 16 | }); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(HomeComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting(), 21 | { teardown: { destroyAfterEach: true }}, 22 | ); 23 | 24 | // Then we find all the tests. 25 | const context = require.context('./', true, /\.spec\.ts$/); 26 | // And load the modules. 27 | context.keys().map(context); 28 | -------------------------------------------------------------------------------- /src/app/core/interceptor/interceptor.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | HttpRequest, 4 | HttpHandler, 5 | HttpEvent, 6 | HttpInterceptor, 7 | HttpErrorResponse 8 | } from '@angular/common/http'; 9 | import { Observable, throwError } from 'rxjs'; 10 | import { SpinerService } from '../spiner/spiner.service'; 11 | import { catchError, finalize } from 'rxjs/operators'; 12 | 13 | @Injectable() 14 | export class InterceptorInterceptor implements HttpInterceptor { 15 | 16 | constructor( 17 | private spinerService:SpinerService 18 | ) {} 19 | 20 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 21 | this.spinerService.showSpiner(); 22 | 23 | return next.handle(request).pipe(catchError((err:HttpErrorResponse) => { 24 | return throwError(err); 25 | }), 26 | finalize(()=> this.spinerService.hideSpiner()) 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/images/icon-search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "noImplicitReturns": false, 8 | "strictPropertyInitialization": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "sourceMap": true, 13 | "declaration": false, 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "node", 17 | "importHelpers": true, 18 | "target": "es2017", 19 | "module": "es2020", 20 | "lib": [ 21 | "es2018", 22 | "dom" 23 | ] 24 | }, 25 | "angularCompilerOptions": { 26 | "enableI18nLegacyMessageIdFormat": false, 27 | "strictInjectionParameters": true, 28 | "strictInputAccessModifiers": true, 29 | "strictTemplates": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/core/spiner/spiner.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class SpinerService { 8 | 9 | public stateLoadingSpiner:StateLoadingSpiner; 10 | public isLoading:boolean = false; 11 | isLoadingChange: Subject = new Subject(); 12 | 13 | constructor() { 14 | this.isLoadingChange.subscribe((value) => { 15 | this.isLoading = value; 16 | }); 17 | this.stateLoadingSpiner = new StateLoadingSpiner(); 18 | } 19 | 20 | showSpiner(){ 21 | this.isLoadingChange.next(true) 22 | this.stateLoadingSpiner = new StateLoadingSpiner(this.isLoading); 23 | } 24 | 25 | hideSpiner(){ 26 | this.isLoadingChange.next(false) 27 | this.stateLoadingSpiner = new StateLoadingSpiner(this.isLoading); 28 | } 29 | } 30 | 31 | export class StateLoadingSpiner { 32 | public isLoading: boolean; 33 | constructor(isLoading?:boolean){ 34 | this.isLoading = isLoading == null ? false:isLoading; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YtsMx 2 | 3 | This project use the official **Yts.mx** API and shows several core features of Angular including: 4 | 5 | - Components 6 | - Data Binding 7 | - Communication between components 8 | - Services 9 | - Routing 10 | - HttpInterceptor 11 | - rxjs 12 | 13 | Captura de ecrã 2021-10-29, às 13 28 08 14 | 15 | Captura de ecrã 2021-10-29, às 13 28 39 16 | 17 | ## Running the Project Locally 18 | 1. Install the Angular CLI 19 | > npm install -g @angular/cli 20 | 2. Run `npm install` at the root of this project 21 | 3. Run ng serve -o for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 🔥 22 | 23 | ## Running unit tests 24 | 25 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 26 | 27 | ## Further help 28 | 29 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yts-mx", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "~12.2.0", 14 | "@angular/common": "~12.2.0", 15 | "@angular/compiler": "~12.2.0", 16 | "@angular/core": "~12.2.0", 17 | "@angular/forms": "~12.2.0", 18 | "@angular/platform-browser": "~12.2.0", 19 | "@angular/platform-browser-dynamic": "~12.2.0", 20 | "@angular/router": "~12.2.0", 21 | "bootstrap": "^5.1.3", 22 | "font-awesome": "^4.7.0", 23 | "rxjs": "~6.6.0", 24 | "save": "^2.4.0", 25 | "tslib": "^2.3.0", 26 | "zone.js": "~0.11.4" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "~12.2.10", 30 | "@angular/cli": "~12.2.10", 31 | "@angular/compiler-cli": "~12.2.0", 32 | "@types/jasmine": "~3.8.0", 33 | "@types/node": "^12.11.1", 34 | "jasmine-core": "~3.8.0", 35 | "karma": "~6.3.0", 36 | "karma-chrome-launcher": "~3.1.0", 37 | "karma-coverage": "~2.0.3", 38 | "karma-jasmine": "~4.0.0", 39 | "karma-jasmine-html-reporter": "~1.7.0", 40 | "typescript": "~4.3.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { HeaderComponent } from './layout/header/header.component'; 7 | import { HomeComponent } from './components/home/home.component'; 8 | import { MoviesService } from './server/services/movies.service'; 9 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 10 | import { FooterComponent } from './layout/footer/footer.component'; 11 | import { SpinerService } from './core/spiner/spiner.service'; 12 | import { InterceptorInterceptor } from './core/interceptor/interceptor.interceptor'; 13 | import { TreadingMoviesComponent } from './components/treading-movies/treading-movies.component'; 14 | 15 | @NgModule({ 16 | declarations: [ 17 | AppComponent, 18 | HeaderComponent, 19 | HomeComponent, 20 | FooterComponent, 21 | TreadingMoviesComponent 22 | ], 23 | imports: [ 24 | BrowserModule, 25 | HttpClientModule, 26 | AppRoutingModule 27 | ], 28 | providers: [MoviesService,SpinerService, 29 | {provide: HTTP_INTERCEPTORS,useClass: InterceptorInterceptor,multi: true}], 30 | bootstrap: [AppComponent] 31 | }) 32 | export class AppModule { } 33 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { MoviesService } from '../../server/services/movies.service'; 3 | import { IMovie } from '../../shared/Interfaces'; 4 | 5 | @Component({ 6 | selector: 'app-home', 7 | templateUrl: './home.component.html', 8 | styleUrls: ['./home.component.css'] 9 | }) 10 | export class HomeComponent implements OnInit { 11 | 12 | popularDownloadsMovies:IMovie; 13 | latestMovies:IMovie; 14 | upcomingMovies:IMovie; 15 | 16 | constructor(private moviesService:MoviesService) { } 17 | 18 | ngOnInit(): void { 19 | this.getPopularDowloadsHttp(); 20 | this.getLatestMoviesHttp(); 21 | this.getUpcomingMoviesHttp(); 22 | } 23 | 24 | getPopularDowloadsHttp(){ 25 | this.moviesService.getPopularDowloadsHttp().subscribe(popularDownloadsResponse =>{ 26 | this.popularDownloadsMovies = popularDownloadsResponse; 27 | }) 28 | } 29 | 30 | getLatestMoviesHttp(){ 31 | this.moviesService.getLatestMoviesHttp().subscribe(latestMovieResponse =>{ 32 | this.latestMovies = latestMovieResponse; 33 | }) 34 | } 35 | 36 | getUpcomingMoviesHttp(){ 37 | this.moviesService.getUpcomingMoviesHttp().subscribe(upcomingMovieResponse =>{ 38 | this.upcomingMovies = upcomingMovieResponse; 39 | }) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/app/shared/Interfaces.ts: -------------------------------------------------------------------------------- 1 | export class CMovie implements IMovie{ 2 | status:string; 3 | data:IMovieData; 4 | constructor(status:string){ 5 | this.status = status; 6 | } 7 | } 8 | 9 | export interface IMovie{ 10 | status:string; 11 | data:IMovieData; 12 | } 13 | 14 | export interface IMovieData { 15 | id: number; 16 | title: string; 17 | year: number; 18 | image: string; 19 | ranking: number; 20 | type: string; 21 | category:string; 22 | movie_count:number; 23 | limit:number; 24 | page_number:number 25 | movies:IMovieItem[]; 26 | } 27 | 28 | export interface IMovieItem{ 29 | id:number; 30 | url:string; 31 | title: string; 32 | title_long: string; 33 | slug: string; 34 | year: number; 35 | rating:number; 36 | runtime:number; 37 | genres:string[]; 38 | summary:string; 39 | description_full:string; 40 | synopsis:string; 41 | language:string; 42 | background_image: string, 43 | background_image_original:string; 44 | small_cover_image: string; 45 | medium_cover_image: string; 46 | large_cover_image: string; 47 | state: string; 48 | torrents: ITorrent[]; 49 | date_uploaded: Date; 50 | } 51 | 52 | export interface ITorrent{ 53 | url: string; 54 | quality: string; 55 | type: string; 56 | seeds: number; 57 | peers: number; 58 | size:string; 59 | date_uploaded:Date; 60 | } 61 | -------------------------------------------------------------------------------- /src/app/components/treading-movies/treading-movies.component.html: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/yts-mx'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /src/app/server/services/movies.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable, throwError } from 'rxjs'; 4 | import { map, catchError } from 'rxjs/operators'; 5 | import { IMovie } from 'src/app/shared/Interfaces'; 6 | import { environment } from 'src/environments/environment'; 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class MoviesService { 11 | 12 | private popularDowaloadsEndPonint = '../assets/data/popular-movies.json'; 13 | 14 | constructor(private http: HttpClient) { } 15 | 16 | getPopularDowloadsHttp(): Observable { 17 | return this.http.get(environment.BASE_URL_SERVER_YTS_API+'?sort_by=download_count&sort_by=year&order_by=desc&limit=4&page=1') 18 | .pipe(catchError((error) => this.handlerError(error))); 19 | } 20 | 21 | getLatestMoviesHttp(): Observable { 22 | return this.http.get(environment.BASE_URL_SERVER_YTS_API+'l?sort_by=date_added&order_by=desc&limit=8') 23 | .pipe(catchError((error) => this.handlerError(error))); 24 | } 25 | 26 | getUpcomingMoviesHttp(): Observable { 27 | return this.http.get(environment.BASE_URL_SERVER_YTS_API+'?sort_by=date_added&order_by=desc&quality=1080&limit=4&page=4') 28 | .pipe(catchError((error) => this.handlerError(error))); 29 | } 30 | 31 | getTreadingMoviesHttp(): Observable { 32 | return this.http.get(environment.BASE_URL_SERVER_YTS_API+'?sort_by=date_added&order_by=desc&limit=20&page=5') 33 | .pipe(catchError((error) => this.handlerError(error))); 34 | } 35 | 36 | handlerError (error:any){ 37 | if (error.error instanceof Error) { 38 | return throwError(error.error.message); 39 | } 40 | return throwError(error || 'Server error'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/layout/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /src/app/layout/header/header.component.html: -------------------------------------------------------------------------------- 1 | 61 | -------------------------------------------------------------------------------- /src/assets/css/header.css: -------------------------------------------------------------------------------- 1 | .navbar>.container-fluid { 2 | flex-wrap: nowrap; 3 | margin-left: 1%; 4 | margin-right: 1%; 5 | } 6 | 7 | .navbar-top { 8 | width: 100%; 9 | background: #1d1d1d; 10 | color: #919191; 11 | z-index: 99; 12 | border-bottom: 0.1pt solid #919191; 13 | } 14 | 15 | .content-logo-right .header-slogan{ 16 | letter-spacing: -.03em; 17 | font-family: 'Arimo-Regular'; 18 | font-size: 0.9em; 19 | margin-left: 10%; 20 | margin-top: 7px; 21 | } 22 | 23 | .contetn-form{ 24 | display: inline-block; 25 | } 26 | 27 | #navbar-search { 28 | margin-top: 5px; 29 | width: 200px; 30 | height: 35px; 31 | } 32 | 33 | .border-input-search { 34 | border-radius: 50px; 35 | padding: 5px; 36 | background-color: #1d1d1d; 37 | border: solid #919191 0.2px; 38 | } 39 | 40 | .border-input-search .input-group-prepend span { 41 | background-color: #1d1d1d; 42 | border: none; 43 | } 44 | 45 | .border-input-search .input-group-prepend img { 46 | width: 20px; 47 | } 48 | 49 | #navbar-search .form-control { 50 | background: #1d1d1d; 51 | color: white; 52 | border-radius: 50px; 53 | border: none; 54 | } 55 | 56 | .navbar-menu li{ 57 | display: inline; 58 | font-family: 'Arimo-Bold', sans-serif; 59 | font-weight: bold; 60 | padding: 0px 6px 0px 6px; 61 | } 62 | 63 | .navbar-menu .default-anchor{ 64 | color: #919191; 65 | text-decoration: none !important; 66 | } 67 | 68 | .navbar-menu li.active >a { 69 | color: white; 70 | text-decoration: none !important; 71 | } 72 | 73 | .navbar-menu li:first-of-type{ 74 | padding: 0px 8px 0px 0px; 75 | } 76 | 77 | .navbar-menu li:last-of-type{ 78 | color: white; 79 | } 80 | 81 | 82 | .navbar-menu-mobile li{ 83 | display: inline; 84 | padding: 0px 8px 0px 8px; 85 | } 86 | 87 | @media only screen and (min-width: 991px) { 88 | .navbar-menu-mobile {display: none} 89 | } 90 | 91 | @media only screen and (max-width: 767px) { 92 | .header-slogan {display: none} 93 | } 94 | 95 | @media only screen and (max-width: 991px) { 96 | .navbar-menu {display: none} 97 | .contetn-form {display: none} 98 | } 99 | 100 | .navbar-menu-mobile li a i{ 101 | color: white; 102 | } 103 | 104 | .item-menu-4k{ 105 | color: #6AC045; 106 | text-decoration: none !important; 107 | font-size: 22px; 108 | pointer-events:none; /*remove hover efect*/ 109 | } 110 | 111 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * IE11 requires the following for NgClass support on SVG elements 23 | */ 24 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 25 | 26 | /** 27 | * Web Animations `@angular/platform-browser/animations` 28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 30 | */ 31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 32 | 33 | /** 34 | * By default, zone.js will patch all possible macroTask and DomEvents 35 | * user can disable parts of macroTask/DomEvents patch by setting following flags 36 | * because those flags need to be set before `zone.js` being loaded, and webpack 37 | * will put import in the top of bundle, so user need to create a separate file 38 | * in this directory (for example: zone-flags.ts), and put the following flags 39 | * into that file, and then add the following code before importing zone.js. 40 | * import './zone-flags'; 41 | * 42 | * The flags allowed in zone-flags.ts are listed here. 43 | * 44 | * The following flags will work for all browsers. 45 | * 46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 49 | * 50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 52 | * 53 | * (window as any).__Zone_enable_cross_context_check = true; 54 | * 55 | */ 56 | 57 | /*************************************************************************************************** 58 | * Zone JS is required by default for Angular itself. 59 | */ 60 | import 'zone.js'; // Included with Angular CLI. 61 | 62 | 63 | /*************************************************************************************************** 64 | * APPLICATION IMPORTS 65 | */ 66 | -------------------------------------------------------------------------------- /src/app/server/services/movies.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientTestingModule, HttpTestingController, TestRequest } from '@angular/common/http/testing'; 2 | import { TestBed } from '@angular/core/testing'; 3 | import { CMovie, IMovie } from 'src/app/shared/Interfaces'; 4 | import { environment } from 'src/environments/environment'; 5 | 6 | import { MoviesService } from './movies.service'; 7 | 8 | describe('MoviesService', () => { 9 | let service: MoviesService; 10 | let httpTestingController:HttpTestingController; 11 | let movieMockRequest:TestRequest; 12 | 13 | beforeEach(() => { 14 | TestBed.configureTestingModule({ 15 | imports:[HttpClientTestingModule] 16 | }); 17 | service = TestBed.inject(MoviesService); 18 | httpTestingController = TestBed.inject(HttpTestingController); 19 | }); 20 | 21 | afterEach(()=>{ 22 | httpTestingController.verify(); 23 | }); 24 | 25 | it('should be created', () => { 26 | expect(service).toBeTruthy(); 27 | }); 28 | 29 | it('should getPopularDowloadsHttp',() =>{ 30 | service.getPopularDowloadsHttp().subscribe((responseMovieData:IMovie)=>{ 31 | expect(responseMovieData.status).toEqual('ok'); 32 | expect(responseMovieData).toBeTruthy(); 33 | }); 34 | movieMockRequest = httpTestingController.expectOne(environment.BASE_URL_SERVER_YTS_API+'?sort_by=download_count&sort_by=year&order_by=desc&limit=4&page=1'); 35 | movieMockRequest.flush(new CMovie('ok')); 36 | expect(movieMockRequest.request.method).toEqual('GET'); 37 | }); 38 | 39 | it('should getLatestMoviesHttp',() =>{ 40 | service.getLatestMoviesHttp().subscribe((responseMovieData:IMovie)=>{ 41 | expect(responseMovieData.status).toEqual('ok'); 42 | expect(responseMovieData).toBeTruthy(); 43 | }); 44 | movieMockRequest = httpTestingController.expectOne(environment.BASE_URL_SERVER_YTS_API+'l?sort_by=date_added&order_by=desc&limit=8'); 45 | movieMockRequest.flush(new CMovie('ok')); 46 | expect(movieMockRequest.request.method).toEqual('GET'); 47 | }); 48 | 49 | it('should getUpcomingMoviesHttp',() =>{ 50 | service.getUpcomingMoviesHttp().subscribe((responseMovieData:IMovie)=>{ 51 | expect(responseMovieData.status).toEqual('ok'); 52 | expect(responseMovieData).toBeTruthy(); 53 | }); 54 | movieMockRequest = httpTestingController.expectOne(environment.BASE_URL_SERVER_YTS_API+'?sort_by=date_added&order_by=desc&quality=1080&limit=4&page=4'); 55 | movieMockRequest.flush(new CMovie('ok')); 56 | expect(movieMockRequest.request.method).toEqual('GET'); 57 | }); 58 | 59 | it('should getTreadingMoviesHttp',() =>{ 60 | service.getTreadingMoviesHttp().subscribe((responseMovieData:IMovie)=>{ 61 | expect(responseMovieData.status).toEqual('ok'); 62 | expect(responseMovieData).toBeTruthy(); 63 | }); 64 | movieMockRequest = httpTestingController.expectOne(environment.BASE_URL_SERVER_YTS_API+'?sort_by=date_added&order_by=desc&limit=20&page=5'); 65 | movieMockRequest.flush(new CMovie('ok')); 66 | expect(movieMockRequest.request.method).toEqual('GET'); 67 | }); 68 | 69 | it('should handlerError',() =>{ 70 | let error = Error('Bad request') 71 | service.handlerError(error).subscribe(()=> {},(error) =>{ 72 | expect(error.message).toEqual('Bad request'); 73 | }) 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "cli": { 4 | "analytics": false 5 | }, 6 | "version": 1, 7 | "newProjectRoot": "projects", 8 | "projects": { 9 | "yts-mx": { 10 | "projectType": "application", 11 | "schematics": { 12 | "@schematics/angular:application": { 13 | "strict": true 14 | } 15 | }, 16 | "root": "", 17 | "sourceRoot": "src", 18 | "prefix": "app", 19 | "architect": { 20 | "build": { 21 | "builder": "@angular-devkit/build-angular:browser", 22 | "options": { 23 | "outputPath": "dist/yts-mx", 24 | "index": "src/index.html", 25 | "main": "src/main.ts", 26 | "polyfills": "src/polyfills.ts", 27 | "tsConfig": "tsconfig.app.json", 28 | "assets": [ 29 | "src/favicon.ico", 30 | "src/assets" 31 | ], 32 | "styles": [ 33 | "src/styles.css", 34 | "node_modules/font-awesome/css/font-awesome.css" 35 | ], 36 | "scripts": [] 37 | }, 38 | "configurations": { 39 | "production": { 40 | "budgets": [ 41 | { 42 | "type": "initial", 43 | "maximumWarning": "500kb", 44 | "maximumError": "1mb" 45 | }, 46 | { 47 | "type": "anyComponentStyle", 48 | "maximumWarning": "2kb", 49 | "maximumError": "4kb" 50 | } 51 | ], 52 | "fileReplacements": [ 53 | { 54 | "replace": "src/environments/environment.ts", 55 | "with": "src/environments/environment.prod.ts" 56 | } 57 | ], 58 | "outputHashing": "all" 59 | }, 60 | "development": { 61 | "buildOptimizer": false, 62 | "optimization": false, 63 | "vendorChunk": true, 64 | "extractLicenses": false, 65 | "sourceMap": true, 66 | "namedChunks": true 67 | } 68 | }, 69 | "defaultConfiguration": "production" 70 | }, 71 | "serve": { 72 | "builder": "@angular-devkit/build-angular:dev-server", 73 | "configurations": { 74 | "production": { 75 | "browserTarget": "yts-mx:build:production" 76 | }, 77 | "development": { 78 | "browserTarget": "yts-mx:build:development" 79 | } 80 | }, 81 | "defaultConfiguration": "development" 82 | }, 83 | "extract-i18n": { 84 | "builder": "@angular-devkit/build-angular:extract-i18n", 85 | "options": { 86 | "browserTarget": "yts-mx:build" 87 | } 88 | }, 89 | "test": { 90 | "builder": "@angular-devkit/build-angular:karma", 91 | "options": { 92 | "main": "src/test.ts", 93 | "polyfills": "src/polyfills.ts", 94 | "tsConfig": "tsconfig.spec.json", 95 | "karmaConfig": "karma.conf.js", 96 | "assets": [ 97 | "src/favicon.ico", 98 | "src/assets" 99 | ], 100 | "styles": [ 101 | "src/styles.css", 102 | "node_modules/font-awesome/css/font-awesome.css" 103 | ], 104 | "scripts": [] 105 | } 106 | } 107 | } 108 | } 109 | }, 110 | "defaultProject": "yts-mx" 111 | } 112 | -------------------------------------------------------------------------------- /src/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import url(header.css); 2 | @import url(footer.css); 3 | 4 | @font-face { 5 | font-family: 'Arimo'; 6 | src: url('../../assets/fonts/arimo/Arimo-Regular.ttf') format('embedded-opentype'), 7 | /* Internet Explorer */ 8 | url('../../assets/fonts/arimo/Arimo-Regular.ttf') format('woff2'), 9 | /* Super Modern Browsers */ 10 | url('../../assets/fonts/arimo/Arimo-Regular.ttf') format('woff'), 11 | /* Pretty Modern Browsers */ 12 | url('../../assets/fonts/arimo/Arimo-Regular.ttf') format('truetype'), 13 | /* Safari, Android, iOS */ 14 | url('../../assets/fonts/arimo/Arimo-Regular.ttf') format('svg'); 15 | /* Legacy iOS */ 16 | } 17 | 18 | body { 19 | font-family: 'Arimo', Arial, Helvetica, "Lucida Grande", sans-serif; 20 | font-size: 16px; 21 | } 22 | 23 | @media only screen and (max-width: 767px) { 24 | .hidden-xs { 25 | display: none 26 | } 27 | } 28 | 29 | 30 | @media only screen and (max-width: 991px) { 31 | .hidden-sm { 32 | display: none 33 | } 34 | 35 | .show-sm { 36 | display: inline-block 37 | } 38 | 39 | } 40 | 41 | 42 | @media (min-width: 992px) and (max-width: 1450px) { 43 | .hidden-md { 44 | display: none !important 45 | } 46 | } 47 | 48 | @media (min-width: 992px) { 49 | .image-movie:hover { 50 | opacity: 0.2; 51 | } 52 | 53 | .browse-movie-link:hover { 54 | border: 5px solid #6AC045; 55 | } 56 | 57 | .image-movie:hover+.info-movie { 58 | display: inline-block; 59 | position: absolute; 60 | color: white; 61 | transform: translateX(0px) translateY(40px); 62 | transition: border .25s ease-in-out; 63 | -moz-transition: border .25s ease-in-out; 64 | -webkit-transition: border .25s ease-in-out; 65 | font-family: 'Arimo-Bold', sans-serif; 66 | margin: 1px -43px 50% -210px; 67 | width: 210px; 68 | } 69 | } 70 | 71 | @media (min-width: 320px) and (max-width: 991px) { 72 | .image-movie { 73 | width: 150px; 74 | } 75 | } 76 | 77 | 78 | .content-popular-dowloads { 79 | background-image: url(../../assets/images/background.jpg), linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)); 80 | background-blend-mode: overlay; 81 | background-position: center; 82 | background-size: cover; 83 | background-repeat: no-repeat; 84 | 85 | } 86 | 87 | .content-popular-dowloads { 88 | color: white; 89 | } 90 | 91 | .title-component-dowloads { 92 | font-size: 2.75em; 93 | font-weight: 500; 94 | color: white; 95 | font-family: 'Arimo-Bold', sans-serif; 96 | text-shadow: 0 2px 2px rgba(0, 0, 0, .4); 97 | margin-bottom: 15px; 98 | margin-top: 0px; 99 | padding-top: 50px; 100 | } 101 | 102 | .text-welcome-yts { 103 | font-family: 'Arimo', Arial, Helvetica, "Lucida Grande", sans-serif; 104 | letter-spacing: -.03em; 105 | font-size: 16px; 106 | text-align: center; 107 | -webkit-font-smoothing: antialiased; 108 | color: #c3bdbd; 109 | } 110 | 111 | .warning-domain { 112 | text-decoration: none !important; 113 | color: #428bca; 114 | text-decoration: none; 115 | } 116 | 117 | .warning-domain:hover { 118 | color: #215a8d; 119 | } 120 | 121 | .more-rss-feed { 122 | text-decoration: none !important; 123 | color: #428bca; 124 | text-decoration: none; 125 | } 126 | 127 | .more-rss-feed:hover { 128 | color: #215a8d; 129 | } 130 | 131 | .star-color-green { 132 | color: #6AC045; 133 | ; 134 | } 135 | 136 | .browse-movie-link { 137 | display: inline-block; 138 | max-width: 220px; 139 | max-height: 325px; 140 | border: 5px solid #fff; 141 | border-radius: 4px; 142 | -webkit-border-radius: 4px; 143 | -moz-border-radius: 4px; 144 | } 145 | 146 | .info-movie { 147 | display: none; 148 | } 149 | 150 | .browse-movie-link figure img.quality-banner { 151 | position: relative; 152 | top: -310px; 153 | display: block; 154 | max-width: 100%; 155 | left: 0; 156 | width: 120px; 157 | height: 90px; 158 | } 159 | 160 | .info-movie h4 { 161 | font-family: 'Arimo-Regular', sans-serif; 162 | letter-spacing: -.03em; 163 | font-size: 28px; 164 | } 165 | 166 | .button-green-download2-big { 167 | background: #6ac045; 168 | border-radius: 3px; 169 | -webkit-border-radius: 3px; 170 | -moz-border-radius: 3px; 171 | display: block; 172 | font-weight: bold; 173 | color: #fff; 174 | width: 130px; 175 | font-family: 'Arimo-Regular', sans-serif; 176 | font-size: 16px; 177 | padding: 7px 0; 178 | margin: 0 auto; 179 | } 180 | 181 | .browse-movie-title { 182 | text-decoration: none !important; 183 | color: white; 184 | font-weight: bold; 185 | pointer-events: none; 186 | /*remove hover efect*/ 187 | } 188 | 189 | .browse-movie-year { 190 | text-decoration: none !important; 191 | color: #919191; 192 | font-weight: bold; 193 | pointer-events: none; 194 | /*remove hover efect*/ 195 | } 196 | 197 | .warning-vpn { 198 | background-color: black; 199 | border: 5px solid #75c74e; 200 | padding: 0 35px 5px 35px; 201 | max-width: 1140px; 202 | margin-left: auto; 203 | margin-right: auto; 204 | } 205 | 206 | .warning-vpn .button { 207 | background-color: #75c74e; 208 | width: 250px; 209 | margin-left: auto; 210 | margin-right: auto; 211 | margin-top: 15px; 212 | border-radius: 5px; 213 | } 214 | 215 | .warning-vpn .button p { 216 | color: white; 217 | text-decoration: none; 218 | } 219 | 220 | .text-blue { 221 | color: #428bca; 222 | } 223 | 224 | .content-dark-black { 225 | background: #000; 226 | } 227 | 228 | .content-dark-grey { 229 | background: #171717; 230 | } 231 | 232 | .home-content .home-movies h2 { 233 | font-size: 1.15em; 234 | font-weight: 700; 235 | padding: 0 10px; 236 | display: inline-block; 237 | margin-bottom: 25px; 238 | text-align: left; 239 | color: white; 240 | } 241 | 242 | .home-content .home-movies .find-more { 243 | float: right; 244 | font-size: 14px; 245 | font-weight: bold; 246 | color: #919191; 247 | text-decoration: none; 248 | transition: color .25s ease-in-out; 249 | -moz-transition: color .25s ease-in-out; 250 | -webkit-transition: color .25s ease-in-out; 251 | } 252 | 253 | .tile-content { 254 | color: #6ac045; 255 | font-size: 1.4em; 256 | margin-top: 10px; 257 | } 258 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.html: -------------------------------------------------------------------------------- 1 | 82 | 83 |
84 |
85 |
86 | 114 |
115 |
116 |
117 | 118 |
119 |
120 |
121 |
122 |
123 |
124 |

Upcoming YIFY Movies

125 | Request a Movie 126 |
127 | 149 |
150 |
151 |
152 |
153 |
154 | -------------------------------------------------------------------------------- /src/assets/images/logo-YTS.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml 403 | 406 | 411 | 417 | 423 | 429 | 435 | 437 | 444 | 451 | 458 | 459 | 464 | .MX 475 | 476 | 477 | 478 | 479 | --------------------------------------------------------------------------------