Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ model Video {
url String
published_at DateTime
thumbnail String
duration String @default("00:00")
slug String @unique
playlists PlaylistOnVideo[]
shows ShowVideo[]
Expand Down
29 changes: 25 additions & 4 deletions src/lib/videos/PlaylistVideo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

<div>
<a href={`/videos/${playlist.slug}/${video.slug}`}>
<img src={video.thumbnail} class="thumbnail" alt={video.title} />
<div class="thumbnail-wrapper">
<img src={video.thumbnail} class="thumbnail" alt={video.title} />
{#if video.duration}
<span class="duration">{video.duration}</span>
{/if}
</div>
<h3 class="h6">{video.title}</h3>
</a>
</div>
Expand All @@ -23,9 +28,25 @@
margin-top: 0;
}

.thumbnail {
overflow: hidden;
border-radius: var(--brad);

.thumbnail-wrapper {
position: relative;

.thumbnail {
overflow: hidden;
border-radius: var(--brad);
}
.duration {
position: absolute;
display: inline-block;
background: var(--yellow);
color: var(--black);
border-radius: 1rem;
padding: 0.25rem;
right: 1rem;
top: 1rem;
font-weight: 600;
}
}

img {
Expand Down
16 changes: 16 additions & 0 deletions src/lib/videos/parseDuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Match one part of the duration string at a time and pad with 0
// Avoids a complex / hard to understand regex for matching all parts at once
function parseTime(duration: string, part: 'H' | 'M' | 'S') {
const match = duration.match(new RegExp(`(\\d+)${part}`));
return match ? match[1].padStart(1, '0') : '00';
}

// Parses duration from YouTube duration format (ISO 8601) e.g. PT1H14M28S to 01:14:28
export default function parseDuration(duration: string) {
const hours = parseTime(duration, 'H');
const minutes = parseTime(duration, 'M');
const seconds = parseTime(duration, 'S');

const result = `${minutes}:${seconds}`;
return hours !== '00' ? `${hours}:${duration}` : result;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @w3cj should this not be:

return hours !== '00' ? `${hours}:${result}` : result;

Looks like currently you'll prepend the hours to the YT format string.

}
13 changes: 3 additions & 10 deletions src/routes/(site)/videos/[p_slug]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import PlaylistVideo from "$/lib/videos/PlaylistVideo.svelte";

export let data;
$: ({ playlist } = data);
</script>
Expand All @@ -7,21 +9,12 @@
<h1 class="h3">{playlist.title}</h1>
<div class="playlist-grid grid">
{#each playlist.videos as { video }}
<a href={`/videos/${playlist.slug}/${video.slug}`}>
<img src={video.thumbnail} class="thumbnail" alt={video.title} />
<h3 class="h6">{video.title}</h3>
</a>
<PlaylistVideo {playlist} {video} />
{/each}
</div>
{/if}

<style lang="postcss">
img {
width: 100%;
}
.h6 {
margin-bottom: 0;
}
.playlist-grid {
display: grid;
grid-gap: 20px;
Expand Down
30 changes: 15 additions & 15 deletions src/server/video/youtube_api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { YOUTUBE_CHANNEL_ID } from '$/const';
import { prisma_client } from '$/hooks.server';
import parseDuration from '$/lib/videos/parseDuration';
import { YOUTUBE_API_KEY } from '$env/static/private';
import type { Video } from '@prisma/client';
import slug from 'speakingurl';

// Youtube importer
Expand Down Expand Up @@ -147,7 +149,7 @@ export async function import_playlist(playlist_id: string) {

// Fetch video details using the videos endpoint
const videos_response = await fetch(
`https://www.googleapis.com/youtube/v3/videos?part=snippet,status&id=${video_ids.join(',')}&key=${process.env.YOUTUBE_API_KEY}`
`https://www.googleapis.com/youtube/v3/videos?part=snippet,status,contentDetails&id=${video_ids.join(',')}&key=${process.env.YOUTUBE_API_KEY}`
);
const videos_data = await videos_response.json();

Expand All @@ -159,24 +161,22 @@ export async function import_playlist(playlist_id: string) {
}

try {
const duration = parseDuration(item.contentDetails.duration);
const data: Omit<Video, 'id'> = {
title: item.snippet.title,
slug: slug(item.snippet.title),
description: item.snippet.description,
duration,
url: `https://www.youtube.com/watch?v=${item.id}`,
published_at: new Date(item.snippet.publishedAt),
thumbnail: item.snippet.thumbnails.maxres.url
};
const video = await prisma_client.video.upsert({
where: { id: item.id },
update: {
title: item.snippet.title,
slug: slug(item.snippet.title),
description: item.snippet.description,
url: `https://www.youtube.com/watch?v=${item.id}`,
published_at: new Date(item.snippet.publishedAt),
thumbnail: item.snippet.thumbnails.maxres.url
},
update: data,
create: {
id: item.id,
title: item.snippet.title,
slug: slug(item.snippet.title),
description: item.snippet.description,
url: `https://www.youtube.com/watch?v=${item.id}`,
published_at: new Date(item.snippet.publishedAt),
thumbnail: item.snippet.thumbnails.maxres.url
...data
}
});

Expand Down