- - -
- - - Rocket Ship - - - - - - - - - - {{ title }} app is running! - - - Rocket Ship Smoke - - - + + + +
+
+ +
+
+

El agua no tiene nombre

+
+
+
+
- -

Resources

-

Here are some links to help you get started:

- -
- - - Learn Angular - - - - - CLI Documentation - - - - - - Angular Material - - - - - - Angular Blog - - - - - - Angular DevTools - - - +
+
+
+
+

Gracias a las personas que han hecho posible la elaboración de este podcast desde la delegación de CEAR Navarra.

+
+
+
- -

Next Steps

-

What do you want to do next with your app?

- - - -
- - - - - - - - - - - + +
+
+
+
+
+
+ - -
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build
+
+
+
+
+

Podcast {{n.title}}

+
+
- - -
+ +
+
+
+
+
+
+ - - - - - Gray Clouds Background - - - -
- - - - - - - - - - +
+
+
+
+

Gracias a las personas que han hecho posible la elaboración de este podcast desde la delegación de CEAR Navarra.

+

Este programa contó con la colaboración de Audio Laborategia elkartea para la realización de la pieza sonora a través de talleres de escucha y de creación colectiva.

+

En la realización sonora: Xabier Erkizia, Luca Rullo, Iñigo Telletxea y Juan Arnal.

+

El guion ha sido escrito colaborativamente junto a las mismas personas que han puesto su voz en la narración.

+
+
+
+
+ \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 28259a8..608376c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,42 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { PlayerService } from './player.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent { +export class AppComponent implements OnInit{ title = 'cear-rio'; + start:boolean = false; + randomPos:number = 0; + news:any[] = [{title:'1',random:0},{title:'1',random:0},{title:'1',random:0},{title:'1',random:0},{title:'1',random:0}] + + constructor( + public playerService:PlayerService + ) { + + playerService.init_app$ + .subscribe( + (res:any) => { + console.log('jsjs') + this.start = res; + } + ) + } + + ngOnInit(): void { + this.news.map( (n) => { + n.random = this.randomPos=this.randomPosition(); + }) + + } + + randomPosition():number { + let seed = Math.random(); + let max=100 + let min=-100 + // return (Math.floor(seed * 100)*(-1*Math.floor(seed*2))); + return Math.floor(Math.random() * (max - min + 1) + min) + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b1c6c96..d72c2ef 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,15 +4,23 @@ import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { PlayerService } from './player.service'; +import { HttpClientModule } from '@angular/common/http'; +import { HeaderComponent } from './header/header.component'; + @NgModule({ declarations: [ - AppComponent + AppComponent, + HeaderComponent ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + HttpClientModule + ], + providers: [ + PlayerService ], - providers: [], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html new file mode 100644 index 0000000..880de54 --- /dev/null +++ b/src/app/header/header.component.html @@ -0,0 +1,23 @@ +
+
+ +
+
+

El agua no tiene nombre

+
+
+ +
+
+
+
+ + + + + + stop + {{currentTime}}/{{duration}} + Soinumapa.net +
+
\ No newline at end of file diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss new file mode 100644 index 0000000..10ff3b6 --- /dev/null +++ b/src/app/header/header.component.scss @@ -0,0 +1,22 @@ +section.header { + position:fixed; + z-index:20; + + &.player-container { + width:100%; + display: flex; + align-items: center; + flex-direction: column; + } + + ul { + list-style: none; + padding:0; + margin:0; + } + div.player { + padding:12px; + span { margin:0 12px;} + a { cursor:pointer;} + } +} \ No newline at end of file diff --git a/src/app/header/header.component.spec.ts b/src/app/header/header.component.spec.ts new file mode 100644 index 0000000..f8d8ed5 --- /dev/null +++ b/src/app/header/header.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeaderComponent } from './header.component'; + +describe('HeaderComponent', () => { + let component: HeaderComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [HeaderComponent] + }); + fixture = TestBed.createComponent(HeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts new file mode 100644 index 0000000..d67f91a --- /dev/null +++ b/src/app/header/header.component.ts @@ -0,0 +1,86 @@ +import { Component, OnInit} from '@angular/core'; +import { PlayerService } from '../player.service'; +import { Subject,Observable } from 'rxjs'; + +@Component({ + selector: 'app-header', + templateUrl: './header.component.html', + styleUrls: ['./header.component.scss'] +}) +export class HeaderComponent implements OnInit { + + start?:boolean; + delete:boolean = false; + isPlay:boolean = false; + isStop:boolean = false; + icy_metadata:string = ""; + title:string = ""; + duration = ""; + currentTime = ""; + init:boolean = false; + clicked:boolean = false; + + constructor( + public playerService:PlayerService + ) { + + playerService.init_app$ + .subscribe( + (res:any) => { + this.start = res; + } + ) + + /* subscribe to metada info */ + playerService.icy_metadata$ + .subscribe( + (res:any) => { + this.icy_metadata=res; + console.log('asdf') + } + ) + + /* subscribe isPlaying */ + playerService.audio_is_playing$ + .subscribe( + (res:any) => { + this.isPlay=res; + } + ) + /* subscribe timer */ + playerService.audio_current_time$ + .subscribe( + (res:any) => { + this.duration = res.duration; + this.currentTime = res.currentTime; + } + ) + } + + ngOnInit(): void { + } + + click():void { + this.clicked = true; + this.playerService.play("https://brba.audio-lab.org/stream/ura.mp3"); + } + + mute() { + this.playerService.mute() + this.delete = !this.delete; + } + + play() { + this.playerService.pause(); + } + + stop() { + this.playerService.restart(); + this.title=""; + this.icy_metadata=""; + this.currentTime=""; + this.duration=""; + } + +} + diff --git a/src/app/player.service.spec.ts b/src/app/player.service.spec.ts new file mode 100644 index 0000000..5355445 --- /dev/null +++ b/src/app/player.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PlayerService } from './player.service'; + +describe('PlayerService', () => { + let service: PlayerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PlayerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/player.service.ts b/src/app/player.service.ts new file mode 100644 index 0000000..53bf3de --- /dev/null +++ b/src/app/player.service.ts @@ -0,0 +1,325 @@ +import { Injectable, OnInit } from '@angular/core'; +import { HttpClient, HttpParams, HttpHeaders, HttpErrorResponse} from '@angular/common/http'; +import { Subject,Observable } from 'rxjs'; +import {environment} from '../environments/environment'; + +@Injectable() +export class PlayerService implements OnInit { + + audio; + audio_paused; + + audio_id?:any; + audio_title?:any; + audio_position?:any; + audio_status:string = "load"; + audio_elapsed?:any; + audio_duration?:any; + audio_url?:any; + audio_stopped; + audio_autoplay:boolean = false; + audio_buffered:number=-1; + public audio_icy="loading data..." + + metadataTimeout?:any; + + public player_minimized:boolean = false; + + /* initApp */ + private _init_app = new Subject(); + init_app$ = this._init_app.asObservable(); + + /* Metadata */ + private _icy_metadata = new Subject(); + icy_metadata$ = this._icy_metadata.asObservable(); + last_icy_metadata?:any; + + /* Playing */ + private _audio_is_playing = new Subject(); + public audio_is_playing$ = this._audio_is_playing.asObservable(); + + /* Time */ + private _audio_current_time = new Subject(); + public audio_current_time$ = this._audio_current_time.asObservable(); + + private _podcast = new Subject(); + podcast$ = this._podcast.asObservable(); + posts$?: Observable; + + constructor( + public http:HttpClient, + ) { + this.audio = new Audio(); + this.audio_paused = true; //audio not playing + this._audio_is_playing.next(this.audio_paused); + this.audio_stopped = true; + } + + ngOnInit() { + // https://developers.google.com/web/updates/2017/09/autoplay-policy-changes + //end audio play + this.audio_status = "init" + this.audio.onended = this.handleEnded.bind(this); + this.audio.oncanplay= this.handledCanPlay.bind(this); + //play updates + this.audio.ontimeupdate = this.handleTimeUpdate.bind(this); + this.audio.onwaiting = this.handleWaiting.bind(this); + this.audio.onloadedmetadata = this.handleLoadMetadata.bind(this); + } + + load(url:string) { + + this.audio.src = url; + this.audio.load(); + this.audio.ontimeupdate = this.handleTimeUpdate.bind(this); + this.audio.onloadstart = () => {this.audio_buffered = 0;}; + this.audio.onended = () => { this.play(environment.urlStream) }; + this.audio.onprogress = () => { this.onProgress() }; + + console.log(environment.urlStream) + + this.audio.oncanplay = () => { + // init player + this._init_app.next(true); + + this.audio_paused=false; + this._audio_is_playing.next(this.audio_paused); + + if (url===environment.urlStream) { + this.getStreamMetadata() + this.metadataTimeout = setInterval( () => { this.getStreamMetadata()}, 5000 ); + } else { + try { + clearInterval(this.metadataTimeout) + } catch (e) { + console.error("Clear Interval Error: "+e) + } + //console.log(this.audio) + // this._icy_metadata.next(); + // this._icy_metadata.next("Brba Podcast") + } + this.canPlay(); + }; + } + + public getStreamMetadata() { + this.http.get(environment.urlStreamMetadata) + .subscribe( (res:any) => { + let index + try { index = res['icestats']['source'].findIndex( (source:any) => source.server_name == "ura.mp3" ) } + catch (e) { index=0 } + + if (res['icestats']['source'][index]) { + //console.log(res.icestats.source[index].title) + this.audio_icy = res.icestats.source[index].title; + this._icy_metadata.next(res.icestats.source[index].title); + this.last_icy_metadata=res.icestats.source[index].title; + } + }) + } + + + public podcast(p:any) { + //console.log(p) + this._icy_metadata.next(p[0].acf.icy_metadata_identifier); + this._podcast.next(p) + } + + public getIcyMetadata() { + return this.audio_icy; + } + + public getBuffered() { + return Math.round(this.audio_buffered / this.audio.duration * 100); + } + + onProgress() { + + var duration = this.audio.duration; + + if (duration > 0) { + for (var i=0; i0) { + // //si le volvemos a enviar el id comprobamos si hay que cambiar + // if (!!id) { + // if (id!=this.audio_id) { + // this.audio_id=id; + // this.audio_title=title; + // this.audio_url=url.replace(/^http:\/\//i, 'https://'); + // this.load(this.audio_url); + // } + // else { this.canPlay(); } + // // si no hay id reconstruimos el objeto de audio + // } else { + // url = this.audio_url; + // title = this.audio_title; + // id = this.audio_id; + // this.canPlay(); + // } + // // no existe un audio en uso + // } else if (this.audio_url=="" || id!=this.audio_id) { + // //reload sound + // this.audio_id=id; + // this.audio_title=title; + // this.audio_url=url; + // this.load(url); + // } else { + // this.canPlay(); + // } + // } + + canPlay() { + this.audio_status = "canplay"; + var a = this.audio.play(); + try { + if (a!==undefined) { + a.then(_ => { + this.initPlayer(this.audio_title,this.audio_id); + }).catch(error=>{ + console.log(error) + console.log("not possible to play") + // show play button!!! + }); + } + } catch (e) { + /* firefox & old browsers not use Observale*/ + this.initPlayer(this.audio_title,this.audio_id); + } + } + + initPlayer(title:string,id:number) { + this.audio_title=title; + this.audio_stopped = false; + this.audio_paused = false; + this.audio_id = id; + //this.setAutoplay(); + } + + pause() { + + if (!this.audio_paused) { + this.audio.pause(); + } else { + this.audio.play(); + } + this.audio_paused = !this.audio_paused; + this._audio_is_playing.next(this.audio_paused); + + } + + mute() { + this.audio.muted = !this.audio.muted; + } + + stop() { + this.audio.onloadstart = null; + this.audio.oncanplay = null + this.audio.onprogress = null; + this.audio.pause(); + this.audio_buffered=-1 + this.audio.currentTime = 0; + this.audio.removeAttribute('src'); + this.audio.load(); + this.audio_paused = true; + this.audio_stopped = true; + this.audio_id = 0; + this.audio_url=""; + // Always playing + this.play(environment.urlStream) + } + + public isStopped():boolean { + return this.audio_stopped; + } + + public isPaused(id:number):boolean { + if (id==this.audio_id) { + return this.audio_paused; + } else { + return true; + } + } + + public getAudioID():number { + return this.audio_id; + } + + formatTime(seconds:number) { + let minutes:any = Math.floor(seconds / 60); + minutes = (minutes >= 10) ? minutes : "0" + minutes; + seconds = Math.floor(seconds % 60); + seconds = (seconds >= 10) ? seconds : 0 + seconds; + return minutes + ":" + seconds; + } + + setAutoplay() { + this.audio.autoplay = true; + } + + public getPercentDuration():number { + return (this.audio.currentTime / this.audio.duration) * 100; + } + + public setSeekPosition(rTime:number) { + this.audio.currentTime = this.audio.duration * rTime; + } + + handleStop() { + this.stop(); + } + + handleEnded(e:any) { + this.stop(); + this.audio_status = "finish"; + } + + handleTimeUpdate(e:any) { + this.audio_status = "playing"; + let elapsed = this.audio.currentTime; + let duration = this.audio.duration; + + + if (Number.isNaN(duration) || duration == Infinity || this.audio.src === environment.urlStream) duration=0; + if (elapsed) { + this.audio_position = elapsed / duration; + this.audio_elapsed = this.formatTime(elapsed); + this.audio_duration = this.formatTime(duration); + this._audio_current_time.next({currentTime: this.formatTime(elapsed), duration: this.formatTime(duration)}); + } + } + + handledCanPlay(e:any) { + console.log(e) + //play + } + + handleWaiting() { + this.audio_status = "waiting"; + } + + handleLoadMetadata(e:any) { + console.log(e) + } + + restart() { + this._icy_metadata.next(true) + this.play(environment.urlStream); + + } + +} \ No newline at end of file diff --git a/src/assets/img/island001.png b/src/assets/img/island001.png new file mode 100644 index 0000000..2c04d6d Binary files /dev/null and b/src/assets/img/island001.png differ diff --git a/src/assets/img/island001.svg b/src/assets/img/island001.svg new file mode 100644 index 0000000..0894ef0 --- /dev/null +++ b/src/assets/img/island001.svg @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/src/assets/img/island001b.svg b/src/assets/img/island001b.svg new file mode 100644 index 0000000..6987dd1 --- /dev/null +++ b/src/assets/img/island001b.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/island002.svg b/src/assets/img/island002.svg new file mode 100644 index 0000000..43d6f0a --- /dev/null +++ b/src/assets/img/island002.svg @@ -0,0 +1,67 @@ + + + + diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000..1c9a8ae --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,11 @@ +export const environment = { + production: true, + uriDomain: "https://brba.audio-lab.org", + uriAPI: "https://brba.audio-lab.org/wp-json/wp/v2", + apiUrlUserActivation: 'https://brba.audio-lab.org/wp-json/ahum/v1/activation', + apiUrlUserSubscriptions: 'https://brba.audio-lab.org/wp-json/ahum/v1/subscriptions', + apiToken:'https://brba.audio-lab.org/wp-json/jwt-auth/v1/token', + apiAddSubscription: 'https://brba.audio-lab.org/wp-json/ahum/v1/subscription/add', + urlStreamMetadata: "https://brba.audio-lab.org/stream/status-json.xsl", + urlStream: "http://stream.piperrak.cc/ura.mp3", +}; \ No newline at end of file diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..856379e --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,24 @@ +// 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, + uriDomain: "https://brba.audio-lab.org", + uriAPI: "https://brba.audio-lab.org/wp-json/wp/v2", + apiUrlUserActivation: 'https://brba.audio-lab.org/wp-json/ahum/v1/activation', + apiUrlUserSubscriptions: 'https://brba.audio-lab.org/wp-json/ahum/v1/subscriptions', + apiToken:'https://brba.audio-lab.org/wp-json/jwt-auth/v1/token', + apiAddSubscription: 'https://brba.audio-lab.org/wp-json/ahum/v1/subscription/add', + urlStreamMetadata: "https://brba.audio-lab.org/stream/status-json.xsl", + urlStream: "http://stream.piperrak.cc/ura.mp3", +}; + +/* + * 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/plugins/zone-error'; // Included with Angular CLI. \ No newline at end of file diff --git a/src/index.html b/src/index.html index 6afaa39..ab71672 100644 --- a/src/index.html +++ b/src/index.html @@ -1,8 +1,8 @@ - + - CearRio + El agua no tiene nombre | Soinumapa diff --git a/src/styles.scss b/src/styles.scss index 90d4ee0..6944afa 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1 +1,101 @@ /* You can add global styles to this file, and also import other style files */ +@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;900&family=Poppins&display=swap'); + + +body { + font-family:"Playfair Display",sans-serif; + color:#fff; + background:#29296d; + margin:0; + + h1 {font-weight: 600;} +} + + + +section.container { + &.big { min-height:100vh;} + &.middle { min-height: 50vh;} + &.fixed { + position:fixed; + width:100vw; + height:100vh; + top:0; + left:0; + padding:0; + margin:0; + z-index:-10; + } + &.header { + background:#29296d; + } + &.text { + background:#fff; + color:#29296d; + } + display:flex; + align-items: center; + position:sticky; + div.content { + flex:1; + display:flex; + align-items:center; + flex-direction: row; + div.col { + flex:1; + text-align: center; + flex-direction: column; + align-items: center; + display:flex; + &.priority { + div.button { + width:80%; + max-width:280px; + h1 { font-size:2rem;} + } + } + &.intro { + flex:2; + font-size:1.5rem; + } + } + div.button { + transition-duration: 1s; + + &:hover { + background:rgba(115, 124, 207,1); + transform: scale(1.2); + cursor:pointer; + } + + &.clicked { + transform: scale(20); + background:#fff; + transition-duration:2s; + * { opacity:0;} + &.hover { + } + } + + &.title { + //background-image:url('/assets/img/island002.svg'); + cursor:pointer; + } + background:rgba(115, 124, 207,0.5); + //background-color:transparent; + //background-image:url('/assets/img/island001.svg'); + background-size:contain; + background-repeat: no-repeat; + background-position: center center; + align-items: center; + aspect-ratio: 1; + max-width:60%; + &.big { max-width:auto;} + border-radius:100%; + display:flex; + text-align: center; + * { flex:1;} + h1 { padding:24px; font-size:1.2rem; } + } + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ed966d4..b555532 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,3 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "compileOnSave": false, "compilerOptions": {