diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-08-04 02:59:07 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-08-04 02:59:07 +0200 |
| commit | acc2089d0d6975d9cede833b083f9d1d2f78504d (patch) | |
| tree | 4697e16e1c3e92711e1bb92e8504b2634c6d6d46 /apps/web | |
| parent | 35ade4b25f417e0d7c4d155b8afc51fb94ebcdd7 (diff) | |
| download | opencut-acc2089d0d6975d9cede833b083f9d1d2f78504d.tar.gz | |
feat: add license filtering for sound effects
Diffstat (limited to 'apps/web')
| -rw-r--r-- | apps/web/src/app/api/sounds/search/route.ts | 11 | ||||
| -rw-r--r-- | apps/web/src/components/editor/media-panel/views/sounds.tsx | 62 | ||||
| -rw-r--r-- | apps/web/src/components/ui/dropdown-menu.tsx | 10 | ||||
| -rw-r--r-- | apps/web/src/components/ui/input.tsx | 4 | ||||
| -rw-r--r-- | apps/web/src/hooks/use-sound-search.ts | 3 | ||||
| -rw-r--r-- | apps/web/src/stores/sounds-store.ts | 9 |
6 files changed, 84 insertions, 15 deletions
diff --git a/apps/web/src/app/api/sounds/search/route.ts b/apps/web/src/app/api/sounds/search/route.ts index 15954140..c89bc76c 100644 --- a/apps/web/src/app/api/sounds/search/route.ts +++ b/apps/web/src/app/api/sounds/search/route.ts @@ -12,6 +12,7 @@ const searchParamsSchema = z.object({ .enum(["downloads", "rating", "created", "score"])
.default("downloads"),
min_rating: z.coerce.number().min(0).max(5).default(3),
+ commercial_only: z.coerce.boolean().default(true),
});
const freesoundResultSchema = z.object({
@@ -124,6 +125,7 @@ export async function GET(request: NextRequest) { page_size: pageSize,
sort,
min_rating,
+ commercial_only,
} = validationResult.data;
if (type === "songs") {
@@ -160,6 +162,15 @@ export async function GET(request: NextRequest) { if (type === "effects" || !type) {
params.append("filter", "duration:[* TO 30.0]");
params.append("filter", `avg_rating:[${min_rating} TO *]`);
+
+ // Filter by license if commercial_only is true
+ if (commercial_only) {
+ params.append(
+ "filter",
+ 'license:("Attribution" OR "Creative Commons 0" OR "Attribution Noncommercial" OR "Attribution Commercial")'
+ );
+ }
+
params.append(
"filter",
"tag:sound-effect OR tag:sfx OR tag:foley OR tag:ambient OR tag:nature OR tag:mechanical OR tag:electronic OR tag:impact OR tag:whoosh OR tag:explosion"
diff --git a/apps/web/src/components/editor/media-panel/views/sounds.tsx b/apps/web/src/components/editor/media-panel/views/sounds.tsx index 5ffa51a9..ed0db8ab 100644 --- a/apps/web/src/components/editor/media-panel/views/sounds.tsx +++ b/apps/web/src/components/editor/media-panel/views/sounds.tsx @@ -5,12 +5,24 @@ import { useState, useMemo, useRef, useEffect } from "react"; import { Separator } from "@/components/ui/separator"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { PlayIcon, PauseIcon, HeartIcon, PlusIcon } from "lucide-react"; +import { + PlayIcon, + PauseIcon, + HeartIcon, + PlusIcon, + ListFilter, +} from "lucide-react"; import { useSoundsStore } from "@/stores/sounds-store"; import { useSoundSearch } from "@/hooks/use-sound-search"; import type { SoundEffect, SavedSound } from "@/types/sounds"; import { Button } from "@/components/ui/button"; import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuCheckboxItem, +} from "@/components/ui/dropdown-menu"; +import { Dialog, DialogContent, DialogDescription, @@ -19,6 +31,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import { cn } from "@/lib/utils"; export function SoundsView() { return ( @@ -66,6 +79,8 @@ function SoundEffectsView() { loadSavedSounds, isSoundSaved, toggleSavedSound, + showCommercialOnly, + toggleCommercialFilter, } = useSoundsStore(); const { results: searchResults, @@ -73,7 +88,7 @@ function SoundEffectsView() { loadMore, hasNextPage, isLoadingMore, - } = useSoundSearch(searchQuery); + } = useSoundSearch(searchQuery, showCommercialOnly); // Audio playback state const [playingId, setPlayingId] = useState<number | null>(null); @@ -144,14 +159,41 @@ function SoundEffectsView() { return ( <div className="flex flex-col gap-5 mt-1 h-full"> - <Input - placeholder="Search sound effects" - className="bg-panel-accent" - value={searchQuery} - onChange={(e) => setSearchQuery(e.target.value)} - showClearIcon - onClear={() => setSearchQuery("")} - /> + <div className="flex items-center gap-3"> + <Input + placeholder="Search sound effects" + className="bg-panel-accent w-full" + containerClassName="w-full" + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} + showClearIcon + onClear={() => setSearchQuery("")} + /> + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button + variant="text" + size="icon" + className={cn(showCommercialOnly && "text-primary")} + > + <ListFilter className="w-4 h-4" /> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="end" className="w-56"> + <DropdownMenuCheckboxItem + checked={showCommercialOnly} + onCheckedChange={toggleCommercialFilter} + > + Show only commercially licensed + </DropdownMenuCheckboxItem> + <div className="px-2 py-1.5 text-xs text-muted-foreground"> + {showCommercialOnly + ? "Only showing sounds licensed for commercial use" + : "Showing all sounds regardless of license"} + </div> + </DropdownMenuContent> + </DropdownMenu> + </div> <div className="relative h-full overflow-hidden"> <ScrollArea diff --git a/apps/web/src/components/ui/dropdown-menu.tsx b/apps/web/src/components/ui/dropdown-menu.tsx index bdd86d58..823d48a3 100644 --- a/apps/web/src/components/ui/dropdown-menu.tsx +++ b/apps/web/src/components/ui/dropdown-menu.tsx @@ -126,20 +126,24 @@ const DropdownMenuCheckboxItem = React.forwardRef< ref={ref} className={cn( dropdownMenuItemVariants({ variant }), - "pl-8 pr-2", + "pl-2 pr-8", className )} checked={checked} + onSelect={(e) => { + e.preventDefault(); + }} {...props} > - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + {children} + <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center"> <DropdownMenuPrimitive.ItemIndicator> <Check className="h-4 w-4" /> </DropdownMenuPrimitive.ItemIndicator> </span> - {children} </DropdownMenuPrimitive.CheckboxItem> )); + DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; diff --git a/apps/web/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx index 4fb0f5b4..440191f2 100644 --- a/apps/web/src/components/ui/input.tsx +++ b/apps/web/src/components/ui/input.tsx @@ -9,6 +9,7 @@ interface InputProps extends React.ComponentProps<"input"> { onShowPasswordChange?: (show: boolean) => void; showClearIcon?: boolean; onClear?: () => void; + containerClassName?: string; } const Input = React.forwardRef<HTMLInputElement, InputProps>( @@ -16,6 +17,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>( { className, type, + containerClassName, showPassword, onShowPasswordChange, showClearIcon, @@ -45,7 +47,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>( iconCount === 2 ? "pr-20" : iconCount === 1 ? "pr-10" : ""; return ( - <div className={hasIcons ? "relative w-full" : ""}> + <div className={cn(hasIcons ? "relative w-full" : "", containerClassName)}> <input type={inputType} className={cn( diff --git a/apps/web/src/hooks/use-sound-search.ts b/apps/web/src/hooks/use-sound-search.ts index e815a668..db9a7e8e 100644 --- a/apps/web/src/hooks/use-sound-search.ts +++ b/apps/web/src/hooks/use-sound-search.ts @@ -9,7 +9,7 @@ import { useSoundsStore } from "@/stores/sounds-store"; * - Proper error handling
*/
-export function useSoundSearch(query: string) {
+export function useSoundSearch(query: string, commercialOnly: boolean) {
const {
searchResults,
isSearching,
@@ -49,6 +49,7 @@ export function useSoundSearch(query: string) { searchParams.set("q", query);
}
+ searchParams.set("commercial_only", commercialOnly.toString());
const response = await fetch(
`/api/sounds/search?${searchParams.toString()}`
);
diff --git a/apps/web/src/stores/sounds-store.ts b/apps/web/src/stores/sounds-store.ts index 3771c07e..215253fb 100644 --- a/apps/web/src/stores/sounds-store.ts +++ b/apps/web/src/stores/sounds-store.ts @@ -13,6 +13,10 @@ interface SoundsStore { error: string | null;
hasLoaded: boolean;
+ // Filter state
+ showCommercialOnly: boolean;
+ toggleCommercialFilter: () => void;
+
// Search state
searchQuery: string;
searchResults: SoundEffect[];
@@ -72,6 +76,11 @@ export const useSoundsStore = create<SoundsStore>((set, get) => ({ isLoading: false,
error: null,
hasLoaded: false,
+ showCommercialOnly: true,
+
+ toggleCommercialFilter: () => {
+ set((state) => ({ showCommercialOnly: !state.showCommercialOnly }));
+ },
// Search state
searchQuery: "",
|
