From 82e49a4701030d3d59d77c7385553a84393d9643 Mon Sep 17 00:00:00 2001 From: hashirama Date: Tue, 1 Jul 2025 05:42:18 +0000 Subject: [PATCH] Add Main.hs --- Main.hs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Main.hs diff --git a/Main.hs b/Main.hs new file mode 100644 index 0000000..8b9728f --- /dev/null +++ b/Main.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE OverloadedStrings #-} +import Conduit +import qualified Data.Conduit.Combinators as CC +import System.Environment (getArgs) +import System.FilePath (takeExtension, takeBaseName) +import Data.Char (toLower) +import qualified Data.Map.Strict as Map +import System.Random (randomRIO) +import Control.Monad (forM_) +import Data.Foldable (toList) +import System.Process (callProcess) + +-- | Check if a file extension denotes a video. +isVideoFile :: FilePath -> Bool +isVideoFile path = + let ext = map toLower (takeExtension path) + in ext `elem` [".mp4", ".mkv", ".avi", ".mov", ".webm"] + +-- | Split a filename (base name) into tokens by delimiters (-, _, ., space). +splitTokens :: String -> [String] +splitTokens "" = [] +splitTokens s = + let (tok, rest) = break (`elem` ("-_. " :: String)) s + in case tok of + "" -> splitTokens (dropWhile (`elem` ("-_. " :: String)) rest) + _ -> tok : splitTokens rest + +-- | Compute prefix1 (first token) and prefix2 (first two tokens) for grouping. +makePrefixes :: FilePath -> (String, Maybe String) +makePrefixes fp = + let name = map toLower (takeBaseName fp) -- base name without extension + tokens = splitTokens name + in case tokens of + [] -> ("", Nothing) + (x:[]) -> (x, Nothing) + (x:y:_) -> (x, Just (x ++ " " ++ y)) + +main :: IO () +main = do + args <- getArgs + let dirs = if null args then ["."] + else args + + filePaths <- runConduitRes $ + yieldMany dirs + .| concatMapMC (\dir -> runConduitRes $ sourceDirectoryDeep False dir .| sinkList) + .| CC.filter isVideoFile + .| sinkList + + let prefixes = map makePrefixes filePaths + countMap1 = Map.fromListWith (+) [ (p1, 1 :: Int) | (p1, _) <- prefixes, p1 /= "" ] + countMap2 = Map.fromListWith (+) [ (p2, 1 :: Int) | (_, Just p2) <- prefixes ] + isStandalone fp = + let (p1, mp2) = makePrefixes fp + c1 = Map.findWithDefault 0 p1 countMap1 + c2 = maybe 0 (\p2 -> Map.findWithDefault 0 p2 countMap2) mp2 + in c1 == 1 && c2 == 1 + candidates = filter isStandalone filePaths + + if null candidates + then putStrLn "No standalone movie found." + else do + idx <- randomRIO (0, length candidates - 1) + let choice = candidates !! idx + putStrLn $ "🎬 Playing movie: " ++ choice + callProcess "mpv" [choice]