diff --git a/src/api.ts b/src/api.ts index 59a164c..45df67a 100644 --- a/src/api.ts +++ b/src/api.ts @@ -43,6 +43,7 @@ export interface ApiMediaItem { director: string | null; writer: string | null; releaseDate: string | null; + source?: string | null; createdAt: string; updatedAt: string; genres?: string[]; @@ -53,7 +54,6 @@ export interface ApiMediaItem { platforms?: string[]; developers?: string[]; completionStatus?: string; - source?: string; playCount?: number; lastActivity?: string | null; playtime?: number; @@ -87,6 +87,7 @@ export interface CreateMediaInput { director?: string | null; writer?: string | null; releaseDate?: string | null; + source?: string | null; genres?: string[]; tags?: string[]; studios?: string[]; @@ -309,6 +310,7 @@ export function convertApiToMedia(apiItem: ApiMediaItem): Media { tags: apiItem.tags || [], studios: apiItem.studios, type: mediaType, + source: apiItem.source || undefined, status: mediaStatus, staff: staff.length > 0 ? staff : undefined, aspectRatio: aspectRatio, @@ -316,7 +318,6 @@ export function convertApiToMedia(apiItem: ApiMediaItem): Media { platforms: apiItem.platforms, developers: apiItem.developers, completionStatus: apiItem.completionStatus, - source: apiItem.source, playCount: apiItem.playCount, lastActivity: apiItem.lastActivity, playtime: apiItem.playtime diff --git a/src/components/AddMediaView.tsx b/src/components/AddMediaView.tsx index 0494160..46e8d01 100644 --- a/src/components/AddMediaView.tsx +++ b/src/components/AddMediaView.tsx @@ -31,6 +31,7 @@ export default function AddMediaView({ activeCategory, enabledCategories, onAddC director: '', writer: '', releaseDate: '', + source: '' as string, genres: '' as string, tags: '' as string, studios: '' as string @@ -129,6 +130,7 @@ export default function AddMediaView({ activeCategory, enabledCategories, onAddC director: newMedia.director || null, writer: newMedia.writer || null, releaseDate: newMedia.releaseDate || null, + source: newMedia.source || null, genres: newMedia.genres ? newMedia.genres.split(',').map(g => g.trim()) : [], tags: newMedia.tags ? newMedia.tags.split(',').map(t => t.trim()) : [], studios: newMedia.studios ? newMedia.studios.split(',').map(s => s.trim()) : [], @@ -163,6 +165,7 @@ export default function AddMediaView({ activeCategory, enabledCategories, onAddC director: '', writer: '', releaseDate: '', + source: '', genres: '', tags: '', studios: '' @@ -439,6 +442,16 @@ export default function AddMediaView({ activeCategory, enabledCategories, onAddC className="bg-muted border-border rounded-xl h-11 focus:ring-[#6d28d9]" /> +
+ + setNewMedia(prev => ({ ...prev, source: e.target.value }))} + placeholder="e.g. username, xbvr, stashapp" + className="bg-muted border-border rounded-xl h-11 focus:ring-[#6d28d9]" + /> +
{/* Cast/Staff Section */} {(newMedia.category === 'Anime' || newMedia.category === 'Movies' || newMedia.category === 'TV Series' || newMedia.category === 'Adult') && ( diff --git a/src/components/BrowseView.tsx b/src/components/BrowseView.tsx index dfaf4f2..14f128d 100644 --- a/src/components/BrowseView.tsx +++ b/src/components/BrowseView.tsx @@ -1,7 +1,7 @@ import { Media, MediaCategory } from '@/types'; import MediaCard from './MediaCard'; import MediaListItem from './MediaListItem'; -import { LayoutGrid, List, Star, ChevronLeft, ChevronRight, ArrowUpDown, Search, Monitor, Users, FolderTree } from 'lucide-react'; +import { LayoutGrid, List, Star, ChevronLeft, ChevronRight, ArrowUpDown, Search, Monitor, Users, FolderTree, Tag } from 'lucide-react'; import { Button } from '@/components/ui/button'; import React, { useState, useMemo, useEffect } from 'react'; import { @@ -49,6 +49,7 @@ export default function BrowseView({ mediaList, onMediaClick, activeCategory, it const [selectedPlatform, setSelectedPlatform] = useState(null); const [selectedDeveloper, setSelectedDeveloper] = useState(null); const [selectedCategory, setSelectedCategory] = useState(null); + const [selectedSource, setSelectedSource] = useState(null); // Extract unique values for filters const allGenres = useMemo(() => Array.from(new Set(mediaList.flatMap(m => m.genres || []))), [mediaList]); @@ -56,6 +57,7 @@ export default function BrowseView({ mediaList, onMediaClick, activeCategory, it const allPlatforms = useMemo(() => Array.from(new Set(mediaList.flatMap(m => m.platforms || []))), [mediaList]); const allDevelopers = useMemo(() => Array.from(new Set(mediaList.flatMap(m => m.developers || []))), [mediaList]); const allCategories = useMemo(() => Array.from(new Set(mediaList.flatMap(m => m.categories || []))), [mediaList]); + const allSources = useMemo(() => Array.from(new Set(mediaList.flatMap(m => m.source ? [m.source] : []))), [mediaList]); const filteredMedia = useMemo(() => { return mediaList.filter(media => { @@ -64,9 +66,10 @@ export default function BrowseView({ mediaList, onMediaClick, activeCategory, it if (selectedPlatform && !media.platforms?.includes(selectedPlatform)) return false; if (selectedDeveloper && !media.developers?.includes(selectedDeveloper)) return false; if (selectedCategory && !media.categories?.includes(selectedCategory)) return false; + if (selectedSource && media.source !== selectedSource) return false; return true; }); - }, [mediaList, selectedGenre, selectedStudio, selectedPlatform, selectedDeveloper, selectedCategory]); + }, [mediaList, selectedGenre, selectedStudio, selectedPlatform, selectedDeveloper, selectedCategory, selectedSource]); // Reset to first page when mediaList or filters change useEffect(() => { @@ -208,7 +211,25 @@ export default function BrowseView({ mediaList, onMediaClick, activeCategory, it )} - {(selectedGenre || selectedStudio || selectedPlatform || selectedDeveloper || selectedCategory) && ( + {/* Source Filter */} + {allSources.length > 0 && ( + + + + + + setSelectedSource(null)}>All Sources + {allSources.sort().map(source => ( + setSelectedSource(source)}>{source} + ))} + + + )} + + {(selectedGenre || selectedStudio || selectedPlatform || selectedDeveloper || selectedCategory || selectedSource) && (