Compare commits
No commits in common. "96aafce53f67b2f72ce63e0fce4c29824170989f" and "f7763199ff8c47cb26ac011ab6e485637eb767f6" have entirely different histories.
96aafce53f
...
f7763199ff
4 changed files with 1 additions and 88 deletions
|
|
@ -1,24 +0,0 @@
|
||||||
"""
|
|
||||||
Migration to add youtube_link column to show, song, and performance tables.
|
|
||||||
"""
|
|
||||||
from sqlmodel import Session, create_engine, text
|
|
||||||
from database import DATABASE_URL
|
|
||||||
|
|
||||||
def add_youtube_link_columns():
|
|
||||||
engine = create_engine(DATABASE_URL)
|
|
||||||
|
|
||||||
tables = ['show', 'song', 'performance']
|
|
||||||
|
|
||||||
with Session(engine) as session:
|
|
||||||
for table in tables:
|
|
||||||
try:
|
|
||||||
session.exec(text(f"""
|
|
||||||
ALTER TABLE "{table}" ADD COLUMN IF NOT EXISTS youtube_link VARCHAR
|
|
||||||
"""))
|
|
||||||
session.commit()
|
|
||||||
print(f"✅ Added youtube_link to {table}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ {table}: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
add_youtube_link_columns()
|
|
||||||
|
|
@ -13,7 +13,6 @@ class Performance(SQLModel, table=True):
|
||||||
segue: bool = Field(default=False, description="Transition to next song >")
|
segue: bool = Field(default=False, description="Transition to next song >")
|
||||||
notes: Optional[str] = Field(default=None)
|
notes: Optional[str] = Field(default=None)
|
||||||
track_url: Optional[str] = Field(default=None, description="Deep link to track audio")
|
track_url: Optional[str] = Field(default=None, description="Deep link to track audio")
|
||||||
youtube_link: Optional[str] = Field(default=None, description="YouTube video URL")
|
|
||||||
|
|
||||||
nicknames: List["PerformanceNickname"] = Relationship(back_populates="performance")
|
nicknames: List["PerformanceNickname"] = Relationship(back_populates="performance")
|
||||||
show: "Show" = Relationship(back_populates="performances")
|
show: "Show" = Relationship(back_populates="performances")
|
||||||
|
|
@ -98,7 +97,6 @@ class Show(SQLModel, table=True):
|
||||||
# External Links
|
# External Links
|
||||||
bandcamp_link: Optional[str] = Field(default=None)
|
bandcamp_link: Optional[str] = Field(default=None)
|
||||||
nugs_link: Optional[str] = Field(default=None)
|
nugs_link: Optional[str] = Field(default=None)
|
||||||
youtube_link: Optional[str] = Field(default=None)
|
|
||||||
|
|
||||||
vertical: Vertical = Relationship(back_populates="shows")
|
vertical: Vertical = Relationship(back_populates="shows")
|
||||||
venue: Optional[Venue] = Relationship(back_populates="shows")
|
venue: Optional[Venue] = Relationship(back_populates="shows")
|
||||||
|
|
@ -112,7 +110,6 @@ class Song(SQLModel, table=True):
|
||||||
original_artist: Optional[str] = Field(default=None)
|
original_artist: Optional[str] = Field(default=None)
|
||||||
vertical_id: int = Field(foreign_key="vertical.id")
|
vertical_id: int = Field(foreign_key="vertical.id")
|
||||||
notes: Optional[str] = Field(default=None)
|
notes: Optional[str] = Field(default=None)
|
||||||
youtube_link: Optional[str] = Field(default=None)
|
|
||||||
|
|
||||||
vertical: Vertical = Relationship(back_populates="songs")
|
vertical: Vertical = Relationship(back_populates="songs")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { ArrowLeft, Calendar, MapPin, Music2, Disc, PlayCircle, ExternalLink, Youtube } from "lucide-react"
|
import { ArrowLeft, Calendar, MapPin, Music2, Disc, PlayCircle, ExternalLink } from "lucide-react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { CommentSection } from "@/components/social/comment-section"
|
import { CommentSection } from "@/components/social/comment-section"
|
||||||
import { EntityRating } from "@/components/social/entity-rating"
|
import { EntityRating } from "@/components/social/entity-rating"
|
||||||
|
|
@ -13,7 +13,6 @@ import { notFound } from "next/navigation"
|
||||||
import { SuggestNicknameDialog } from "@/components/shows/suggest-nickname-dialog"
|
import { SuggestNicknameDialog } from "@/components/shows/suggest-nickname-dialog"
|
||||||
import { EntityReviews } from "@/components/reviews/entity-reviews"
|
import { EntityReviews } from "@/components/reviews/entity-reviews"
|
||||||
import { getApiUrl } from "@/lib/api-config"
|
import { getApiUrl } from "@/lib/api-config"
|
||||||
import { YouTubeEmbed } from "@/components/ui/youtube-embed"
|
|
||||||
|
|
||||||
async function getShow(id: string) {
|
async function getShow(id: string) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -137,20 +136,6 @@ export default async function ShowDetailPage({ params }: { params: Promise<{ id:
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{show.youtube_link && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Youtube className="h-5 w-5 text-red-500" />
|
|
||||||
Video
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<YouTubeEmbed url={show.youtube_link} title={`${show.date?.split('T')[0]} - ${show.venue?.name}`} />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-[2fr_1fr]">
|
<div className="grid gap-6 md:grid-cols-[2fr_1fr]">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
"use client"
|
|
||||||
|
|
||||||
import { useMemo } from "react"
|
|
||||||
|
|
||||||
interface YouTubeEmbedProps {
|
|
||||||
url: string
|
|
||||||
title?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractVideoId(url: string): string | null {
|
|
||||||
// Handle various YouTube URL formats
|
|
||||||
const patterns = [
|
|
||||||
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&?/]+)/,
|
|
||||||
/youtube\.com\/v\/([^&?/]+)/,
|
|
||||||
/youtube\.com\/shorts\/([^&?/]+)/
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const pattern of patterns) {
|
|
||||||
const match = url.match(pattern)
|
|
||||||
if (match && match[1]) {
|
|
||||||
return match[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
export function YouTubeEmbed({ url, title = "YouTube video" }: YouTubeEmbedProps) {
|
|
||||||
const videoId = useMemo(() => extractVideoId(url), [url])
|
|
||||||
|
|
||||||
if (!videoId) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative w-full aspect-video rounded-lg overflow-hidden bg-muted">
|
|
||||||
<iframe
|
|
||||||
src={`https://www.youtube.com/embed/${videoId}`}
|
|
||||||
title={title}
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
||||||
allowFullScreen
|
|
||||||
className="absolute inset-0 w-full h-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue