Skip to content

Commit

Permalink
Fixes pesky bug with youtube stats and vids
Browse files Browse the repository at this point in the history
Signed-off-by: Cole Gentry <[email protected]>
  • Loading branch information
SomethingNew71 committed Dec 8, 2023
1 parent 0b16533 commit aef448d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 139 deletions.
111 changes: 32 additions & 79 deletions components/RecentVideos.vue
Original file line number Diff line number Diff line change
@@ -1,88 +1,41 @@
<template>
<div>
<v-row>
<h3 class="fancy-font-bold is-size-3 has-text-centered pb-5">Recent Videos</h3>

<template v-if="isLoading">
<div class="columns is-multiline">
<div v-for="(item, index) in 2" :key="index" class="column is-half">
<div class="card">
<div class="card-image">
<o-skeleton height="100px"></o-skeleton>
</div>
<div class="card-content">
<div class="content">
<p class="title is-6">
<o-skeleton></o-skeleton>
</p>
<p class="subtitle is-7 pt-2">
<o-skeleton></o-skeleton>
</p>
</div>
</div>
</div>
</div>
</div>
<template v-if="pending">
<v-row>
<v-col cols="12" md="3" v-for="item in 3">
<v-skeleton-loader class="mx-auto border" max-width="300" type="image, article"></v-skeleton-loader>
</v-col>
</v-row>
</template>

<div class="columns is-multiline">
<div class="column is-one-third-desktop" v-for="video in videos">
<div class="card">
<div class="card-image">
<figure class="image">
<a :href="video.videoUrl" target="_blank">
<picture :alt="video.title + ' thumbnail'">
<source media="(max-width: 799px)" :srcset="video.thumbs.medium" />
<source media="(max-width: 992px)" :srcset="video.thumbs.high" />
<source media="(max-width: 1200px)" :srcset="video.thumbs.standard" />
<img loading="lazy" :src="video.thumbs.maxres" :alt="'Thumbnail for ' + videos.title" />
</picture>
</a>
</figure>
</div>
<div class="card-content">
<div class="content">
<p class="title is-6">
{{ video.title }}
</p>
<p class="subtitle is-7 pt-2">published on {{ video.publishedOn }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
<template v-else-if="error">
<p>Unable to load youtube data</p>
</template>

<v-row v-else-if="videos">
<v-col cols="12" md="4" v-for="video in videos">
<v-card class="mx-auto" max-width="344" :href="video.videoUrl">
<v-img :src="video.thumbnails.maxres" height="250px">
<template #sources>
<source media="(max-width: 799px)" :srcset="video.thumbnails.medium" />
<source media="(max-width: 992px)" :srcset="video.thumbnails.high" />
<source media="(max-width: 1200px)" :srcset="video.thumbnails.standard" />
</template>
</v-img>
<v-card-title>{{ video.title }}</v-card-title>
<v-card-subtitle class="pb-5"> published on {{ video.publishedOn }} </v-card-subtitle>
</v-card>
</v-col>
</v-row>
</v-row>
</template>

<script setup lang="ts">
let videos: any;
let isLoading = true;
await useFetch('/api/videos')
.then((response: any) => {
if (response?.data?._rawValue) {
videos = response.data._rawValue.map((video: any) => {
return {
title: video.title,
thumbs: video.thumbnails,
publishedOn: DateTime.fromISO(video.publishedOn).toFormat('LLL dd, yyyy'),
videoUrl: video.videoUrl,
};
});
} else {
videos = [];
}
})
.finally(() => {
isLoading = false;
});
import { VCol, VRow } from 'vuetify/components/VGrid';
import { VCard, VCardTitle, VCardSubtitle } from 'vuetify/components/VCard';
import { VImg } from 'vuetify/components/VImg';
import { VSkeletonLoader } from 'vuetify/components/VSkeletonLoader';
const { data: videos, pending, error } = await useFetch('/api/videos');
</script>

<script lang="ts">
import { DateTime } from 'luxon';
export default defineComponent({});
</script>

<style lang="scss" scoped>
.has-shadow {
box-shadow: none;
}
</style>
32 changes: 16 additions & 16 deletions components/Stats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,48 @@
<div class="level-item has-text-centered">
<div>
<p class="heading">Minutes Watched</p>
<p v-if="stats.views" class="title">
<p v-if="pending"></p>
<p v-else-if="error">??</p>
<p v-else-if="stats && stats.views" class="title">
{{ stats.views }}
</p>
<p v-else>??</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Subscribers</p>
<p v-if="stats.subscribers" class="title">
<p v-if="pending"></p>
<p v-else-if="error">??</p>
<p v-else-if="stats && stats.subscribers" class="title">
{{ stats.subscribers }}
</p>
<p v-else>??</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">videos</p>
<p v-if="stats.videos" class="title">
<p v-if="pending"></p>
<p v-else-if="error">??</p>
<p v-else-if="stats && stats.videos" class="title">
{{ stats.videos }}
</p>
<p v-else>??</p>
</div>
</div>
</nav>
</div>
</template>

<script lang="ts" setup>
let stats: any;
await useFetch('/api/stats')
.then((response: any) => {
stats = { ...response.data._rawValue };
})
.catch(() => {
stats = {
views: undefined,
subscribers: undefined,
videos: undefined,
};
});
const { data: stats, pending, error } = await useFetch('/api/stats');
</script>

<style lang="scss" scoped>
.card {
box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.4), 0 0px 0 1px rgba(10, 10, 10, 0.02);
box-shadow:
0 0.5em 1em -0.125em rgba(10, 10, 10, 0.4),
0 0px 0 1px rgba(10, 10, 10, 0.02);
}
</style>
12 changes: 12 additions & 0 deletions data/models/youtube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export interface YoutubeStatsResponse {
items: YoutubeStatsItem[];
}

export interface YoutubeStatsFEResponse {
views: number | string;
subscribers: number | string;
videos: number | string;
}

export interface YoutubeStatsItem {
kind: string;
etag: string;
Expand Down Expand Up @@ -52,6 +58,12 @@ export interface YoutubeVideoItem {
id: string;
snippet: YoutubeSnippet;
}
export interface YoutubeVideoItemParsed {
title: string;
thumbnails: YoutubeThumbnailsParsed;
publishedOn: string;
videoUrl: string;
}

export interface YoutubeSnippet {
publishedAt: string;
Expand Down
32 changes: 11 additions & 21 deletions server/api/stats.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from 'axios';
import type { YoutubeStatsResponse } from '~/data/models/youtube';
import type { YoutubeStatsFEResponse, YoutubeStatsResponse } from '~/data/models/youtube';

export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
Expand All @@ -8,26 +8,16 @@ export default defineEventHandler(async (event) => {
const details = 'snippet,contentDetails,statistics';
const feed = `${baseURL}?key=${config.app.youtubeAPIKey}&id=${id}&part=${details}`;
try {
return axios
.get<YoutubeStatsResponse>(feed)
.then((response) => {
const items = response.data.items[0].statistics;
const niceResponse = {
views: Number(items.viewCount).toLocaleString(),
subscribers: Number(items.subscriberCount).toLocaleString(),
videos: Number(items.videoCount).toLocaleString(),
};
return { ...niceResponse };
})
.catch((error) => {
throw new Error(error);
});
return axios.get<YoutubeStatsResponse>(feed).then((response): YoutubeStatsFEResponse => {
const items = response.data.items[0].statistics;
const niceResponse: YoutubeStatsFEResponse = {
views: Number(items.viewCount).toLocaleString(),
subscribers: Number(items.subscriberCount).toLocaleString(),
videos: Number(items.videoCount).toLocaleString(),
};
return { ...niceResponse };
});
} catch (error) {
return {
error,
views: '?',
subscribers: '?',
videos: '?',
};
throw new Error(`Error with youtube stats API - ${error}`);
}
});
41 changes: 18 additions & 23 deletions server/api/videos.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import axios from 'axios';
import type { YoutubeDataResponse, YoutubeThumbnails, YoutubeThumbnailsParsed } from '~/data/models/youtube';
import type {
YoutubeDataResponse,
YoutubeThumbnails,
YoutubeThumbnailsParsed,
YoutubeVideoItemParsed,
} from '~/data/models/youtube';
import * as _ from 'lodash';
import { DateTime } from 'luxon';

export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
Expand All @@ -9,30 +15,19 @@ export default defineEventHandler(async (event) => {
const details = 'snippet';
const feed = `${baseURL}?key=${config.app.youtubeAPIKey}&playlistId=${id}&part=${details}`;
try {
return await axios
.get<YoutubeDataResponse>(feed)
.then((response) => {
const items = response.data.items.map((item) => {
return {
title: item.snippet.title,
thumbnails: organizeThumbnails(item.snippet.thumbnails),
publishedOn: item.snippet.publishedAt,
videoUrl: `http://www.youtube.com/watch?v=${item.snippet.resourceId.videoId}`,
};
});
return [...items.slice(0, 3)];
})
.catch((error) => {
throw new Error(error);
return await axios.get<YoutubeDataResponse>(feed).then((response): YoutubeVideoItemParsed[] => {
const items = response.data.items.map((item) => {
return {
title: item.snippet.title,
thumbnails: organizeThumbnails(item.snippet.thumbnails),
publishedOn: DateTime.fromISO(item.snippet.publishedAt).toFormat('LLL dd, yyyy'),
videoUrl: `http://www.youtube.com/watch?v=${item.snippet.resourceId.videoId}`,
};
});
return [...items.slice(0, 3)];
});
} catch (error) {
return {
error,
title: '?',
thumbnails: [],
publishedOn: '?',
videoUrl: `?`,
};
throw new Error(`Error with youtube API - ${error}`);
}

function organizeThumbnails(thumbs: YoutubeThumbnails): YoutubeThumbnailsParsed {
Expand Down

0 comments on commit aef448d

Please sign in to comment.