styling, video title, disable autoplay
This commit is contained in:
parent
c54698a4aa
commit
607b845abc
2 changed files with 100 additions and 14 deletions
12
src/App.vue
12
src/App.vue
|
@ -35,8 +35,20 @@ export default class App extends Vue {
|
|||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.channels {
|
||||
display: flex;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.channel {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<template>
|
||||
<div :class="['channel', `channel-${state}`]">
|
||||
<div class="volume-wrapper">
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<input class="volume" :disabled="state === 'unloaded'"
|
||||
type="range" min="0" max="100" v-model="volume"/>
|
||||
</div>
|
||||
<div class="title">{{state === "loading" ? "Loading..." : title}}</div>
|
||||
<div class="youtube-player" ref="ytpl"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -34,6 +37,7 @@ export default class Channel extends Vue {
|
|||
private animateVolumeStart?: Date;
|
||||
private animateVolumeInterval?: number;
|
||||
private state = ChannelState.UNLOADED;
|
||||
private title = "";
|
||||
private volume: number = 50;
|
||||
|
||||
|
||||
|
@ -45,6 +49,15 @@ export default class Channel extends Vue {
|
|||
}
|
||||
}
|
||||
|
||||
private get ytVideoId() {
|
||||
if (this.url) {
|
||||
const match = this.url.match(/v=([\w_\-]+)/);
|
||||
if (match !== null && match.length == 2) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (this.youtubePlayer) {
|
||||
this.youtubePlayer.setVolume(this.volume);
|
||||
|
@ -58,21 +71,33 @@ export default class Channel extends Vue {
|
|||
private onUrlChange() {
|
||||
if (this.url !== undefined) {
|
||||
if (this.youtubePlayer === undefined) {
|
||||
this.youtubePlayer = YouTubePlayerFactory(this.$refs.ytpl as HTMLDivElement);
|
||||
this.youtubePlayer = YouTubePlayerFactory(this.$refs.ytpl as HTMLDivElement, {
|
||||
playerVars: {"autoplay": 0},
|
||||
});
|
||||
this.youtubePlayer.on("stateChange", (event) => {
|
||||
if (event.data == PlayerStates.BUFFERING) {
|
||||
this.state = ChannelState.READY;
|
||||
}
|
||||
});
|
||||
}
|
||||
const videoId = this.url.match(/v=([\w_\-]+)/);
|
||||
if (videoId !== null && videoId.length == 2) {
|
||||
const videoIdMatch = this.url.match(/v=([\w_\-]+)/);
|
||||
if (videoIdMatch !== null && videoIdMatch.length == 2) {
|
||||
const videoId = videoIdMatch[1];
|
||||
this.state = ChannelState.LOADING;
|
||||
console.log(`Loading YouTube video "${videoId[1]}"`);
|
||||
this.youtubePlayer.loadVideoById(videoId[1]);
|
||||
console.log(`Loading YouTube video "${videoId}"`);
|
||||
this.youtubePlayer.loadVideoById(videoId);
|
||||
this.title = "Loading...";
|
||||
Channel.fetchYoutubeTitle(videoId).then((title) => {
|
||||
this.title = title;
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
this.title = "ERR";
|
||||
});
|
||||
} else {
|
||||
console.error(`Something went wrong trying to parse ${this.url}`);
|
||||
}
|
||||
} else {
|
||||
this.title = "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,15 +123,46 @@ export default class Channel extends Vue {
|
|||
}
|
||||
}, 1000 / 60);
|
||||
}
|
||||
|
||||
private static fetchYoutubeTitle(videoId: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let url = `https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${videoId}`;
|
||||
if (process.env.VUE_APP_GOOGLE_API_KEY) {
|
||||
url += `&key=${process.env.VUE_APP_GOOGLE_API_KEY}`;
|
||||
} else {
|
||||
console.warn("Using unauthenticated Google API...");
|
||||
}
|
||||
fetch(url).then((resp) => {
|
||||
resp.json().then((jsonResp) => {
|
||||
if (jsonResp["error"]) {
|
||||
reject(jsonResp["error"]);
|
||||
} else {
|
||||
console.debug(jsonResp);
|
||||
resolve(jsonResp.items[0].snippet.title);
|
||||
}
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--suppress CssUnusedSymbol -->
|
||||
<style scoped>
|
||||
.channel {
|
||||
width: 128px;
|
||||
height: 256px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-family: monospace;
|
||||
font-size: 8px;
|
||||
border: 1px solid black;
|
||||
width: 20px;
|
||||
height: 170px;
|
||||
}
|
||||
|
||||
.channel-loading {
|
||||
|
@ -117,14 +173,32 @@ export default class Channel extends Vue {
|
|||
background: lightgray;
|
||||
}
|
||||
|
||||
|
||||
.volume-wrapper {
|
||||
--height: 200px;
|
||||
--width: 30px;
|
||||
height: var(--height);
|
||||
width: var(--width);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.volume {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
height: 15px;
|
||||
top: calc(150px / 2);
|
||||
left: calc(150px / 2 * -1 + 15px / 2);
|
||||
width: var(--height);
|
||||
height: var(--width);
|
||||
top: calc(var(--height) / 2);
|
||||
left: calc(var(--height) / 2 * -1 + var(--width) / 2);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: .25em 1em;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
|
Loading…
Reference in a new issue