Add Main.hs

This commit is contained in:
千住柱間 2025-07-01 05:42:18 +00:00
commit 82e49a4701

66
Main.hs Normal file
View file

@ -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]