diff --git a/dotfiles/hypr/.luarc.json b/dotfiles/hypr/.luarc.json
index 16541f8..7a500f3 120000
--- a/dotfiles/hypr/.luarc.json
+++ b/dotfiles/hypr/.luarc.json
@@ -1 +1 @@
-/nix/store/yaksysd8kvxqwyv04sd4g97la1sh762c-home-manager-files/.config/hypr/.luarc.json
\ No newline at end of file
+/nix/store/yrapqnp1lddhxv9d4hc00b4gcnlxa7fk-home-manager-files/.config/hypr/.luarc.json
\ No newline at end of file
diff --git a/dotfiles/hypr/hyprland.lua b/dotfiles/hypr/hyprland.lua
index afe63b4..8a3bdb5 120000
--- a/dotfiles/hypr/hyprland.lua
+++ b/dotfiles/hypr/hyprland.lua
@@ -1 +1 @@
-/nix/store/yaksysd8kvxqwyv04sd4g97la1sh762c-home-manager-files/.config/hypr/hyprland.lua
\ No newline at end of file
+/nix/store/yrapqnp1lddhxv9d4hc00b4gcnlxa7fk-home-manager-files/.config/hypr/hyprland.lua
\ No newline at end of file
diff --git a/dotfiles/inkscape/palettes b/dotfiles/inkscape/palettes
index ce3edd8..ad21020 120000
--- a/dotfiles/inkscape/palettes
+++ b/dotfiles/inkscape/palettes
@@ -1 +1 @@
-/nix/store/yaksysd8kvxqwyv04sd4g97la1sh762c-home-manager-files/.config/inkscape/palettes
\ No newline at end of file
+/nix/store/yrapqnp1lddhxv9d4hc00b4gcnlxa7fk-home-manager-files/.config/inkscape/palettes
\ No newline at end of file
diff --git a/dotfiles/inkscape/templates b/dotfiles/inkscape/templates
index 24055d5..e3b98c5 120000
--- a/dotfiles/inkscape/templates
+++ b/dotfiles/inkscape/templates
@@ -1 +1 @@
-/nix/store/yaksysd8kvxqwyv04sd4g97la1sh762c-home-manager-files/.config/inkscape/templates
\ No newline at end of file
+/nix/store/yrapqnp1lddhxv9d4hc00b4gcnlxa7fk-home-manager-files/.config/inkscape/templates
\ No newline at end of file
diff --git a/dotfiles/kitty/kitty.conf b/dotfiles/kitty/kitty.conf
index 46dc004..0f6cc92 120000
--- a/dotfiles/kitty/kitty.conf
+++ b/dotfiles/kitty/kitty.conf
@@ -1 +1 @@
-/nix/store/yaksysd8kvxqwyv04sd4g97la1sh762c-home-manager-files/.config/kitty/kitty.conf
\ No newline at end of file
+/nix/store/yrapqnp1lddhxv9d4hc00b4gcnlxa7fk-home-manager-files/.config/kitty/kitty.conf
\ No newline at end of file
diff --git a/dotfiles/mpv/feeds.lua b/dotfiles/mpv/feeds.lua
new file mode 100644
index 0000000..8d820b4
--- /dev/null
+++ b/dotfiles/mpv/feeds.lua
@@ -0,0 +1,487 @@
+--[[
+feeds.lua — menu "Abonnements YouTube / Twitch" pour mpv, rendu via uosc.
+
+Lit deux CSV :
+ - le subscriptions.csv de Google Takeout
+ (col. 1 = ID de chaîne, col. 3 = nom de la chaîne)
+ - un twitch.csv maison (col. 1 = login Twitch, col. 2 = libellé, optionnelle)
+
+Trois sources :
+ - Abonnements YouTube : dernières vidéos, via les flux RSS publics (curl)
+ - Parcourir une chaîne : catalogue d'une chaîne, via yt-dlp
+ - Twitch : chaînes suivies, lecture via streamlink
+
+Aucune clé API n'est nécessaire.
+Dépendances : uosc, curl, yt-dlp, streamlink.
+Raccourci par défaut : alt+h
+--]]
+
+local utils = require("mp.utils")
+local options = require("mp.options")
+local msg = require("mp.msg")
+
+local o = {
+ youtube_csv = "~/nixos-config/dotfiles/mpv/youtube.csv",
+ twitch_csv = "~/nixos-config/dotfiles/mpv/twitch.csv",
+ key = "alt+h",
+ per_channel = 3, -- nb max de vidéos gardées par chaîne (flux RSS)
+ max_videos = 150, -- taille max de la liste des abonnements
+ channel_videos = 1000, -- nb de vidéos chargées en parcourant une chaîne
+ twitch_live_check = true, -- détection "en direct" (best-effort, sans clé API)
+}
+options.read_options(o, "feeds")
+
+local cache = { youtube = nil, twitch = nil, channels = {} }
+
+local function expand(path)
+ return mp.command_native({ "expand-path", path })
+end
+
+local function unescape(s)
+ if not s then
+ return ""
+ end
+ s = s:gsub("<", "<"):gsub(">", ">"):gsub(""", '"')
+ s = s:gsub("'", "'"):gsub("'", "'"):gsub("&", "&")
+ return s
+end
+
+local function trim(s)
+ return (s or ""):gsub("^%s+", ""):gsub("%s+$", "")
+end
+
+local function format_duration(sec)
+ if type(sec) ~= "number" or sec <= 0 then
+ return ""
+ end
+ sec = math.floor(sec)
+ local h = math.floor(sec / 3600)
+ local m = math.floor((sec % 3600) / 60)
+ local s = sec % 60
+ if h > 0 then
+ return string.format("%d:%02d:%02d", h, m, s)
+ end
+ return string.format("%d:%02d", m, s)
+end
+
+local function open_menu(m)
+ mp.commandv("script-message-to", "uosc", "open-menu", utils.format_json(m))
+end
+
+local function update_menu(m)
+ mp.commandv("script-message-to", "uosc", "update-menu", utils.format_json(m))
+end
+
+local function loading_menu(type_, title)
+ open_menu({
+ type = type_,
+ title = title,
+ items = { { title = "Chargement…", icon = "spinner", selectable = false, keep_open = true } },
+ })
+end
+
+-- Renvoie les lignes du fichier, débarrassées d'un éventuel \r final (les
+-- exports Takeout peuvent être en CRLF).
+local function read_lines(path)
+ local lines = {}
+ local full = expand(path)
+ local f = io.open(full, "r")
+ if not f then
+ msg.warn("CSV illisible : " .. tostring(full))
+ return lines
+ end
+ for line in f:lines() do
+ lines[#lines + 1] = (line:gsub("\r$", ""))
+ end
+ f:close()
+ return lines
+end
+
+--------------------------------------------------- Lecture
+
+mp.register_script_message("feeds-play", function(url)
+ mp.commandv("loadfile", url, "replace")
+end)
+
+mp.register_script_message("feeds-twitch", function(login)
+ mp.osd_message("Ouverture de " .. login .. " via streamlink…", 3)
+ mp.command_native_async({
+ name = "subprocess",
+ args = { "streamlink", "--player=mpv", "--twitch-low-latency", "twitch.tv/" .. login, "best" },
+ detach = true,
+ -- sans ceci, mpv tue le processus dès qu'aucune vidéo n'est en lecture
+ playback_only = false,
+ }, function() end)
+end)
+
+--------------------------------------------------- CSV YouTube
+
+-- Format Takeout : ID,URL,Titre — l'URL ne contient jamais de virgule, donc
+-- tout ce qui suit la 2e virgule est le titre (éventuellement entre
+-- guillemets s'il contient lui-même une virgule).
+local function read_youtube_channels()
+ local channels = {}
+ for _, line in ipairs(read_lines(o.youtube_csv)) do
+ local id = line:match("^([%w_-]+)")
+ if id and id:match("^UC") then -- écarte l'en-tête du CSV
+ local title = line:match("^[^,]*,[^,]*,(.*)$") or ""
+ title = trim(title)
+ local inner = title:match('^"(.*)"$')
+ if inner then
+ title = inner:gsub('""', '"')
+ end
+ if title == "" then
+ title = id
+ end
+ channels[#channels + 1] = { id = id, title = title }
+ end
+ end
+ table.sort(channels, function(a, b)
+ return a.title:lower() < b.title:lower()
+ end)
+ return channels
+end
+
+--------------------------------------------------- Abonnements (RSS)
+
+local function youtube_menu_data(videos)
+ local items = {}
+ for _, v in ipairs(videos) do
+ items[#items + 1] = {
+ title = v.author .. " — " .. v.title,
+ hint = v.published:sub(1, 10),
+ value = { "script-message", "feeds-play", v.url },
+ }
+ end
+ if #items == 0 then
+ items = {
+ {
+ title = "Aucune vidéo récupérée — voir les logs (--msg-level=feeds=info)",
+ selectable = false,
+ },
+ }
+ end
+ return { type = "yt_feed", title = "Abonnements YouTube", items = items }
+end
+
+local function parse_feeds(blob)
+ local videos, seen_count = {}, {}
+ for entry in blob:gmatch("(.-)") do
+ local id = entry:match("(.-)")
+ local title = entry:match("
(.-)")
+ local author = entry:match(".-(.-)")
+ local published = entry:match("(.-)")
+ if id and title and published then
+ videos[#videos + 1] = {
+ url = "https://www.youtube.com/watch?v=" .. id,
+ title = unescape(title),
+ author = unescape(author or "?"),
+ published = published,
+ }
+ end
+ end
+
+ -- les dates ISO 8601 se trient correctement comme de simples chaînes
+ table.sort(videos, function(a, b)
+ return a.published > b.published
+ end)
+
+ local kept = {}
+ for _, v in ipairs(videos) do
+ local n = (seen_count[v.author] or 0)
+ if n < o.per_channel then
+ seen_count[v.author] = n + 1
+ kept[#kept + 1] = v
+ if #kept >= o.max_videos then
+ break
+ end
+ end
+ end
+ return kept
+end
+
+local function youtube_menu(force)
+ if cache.youtube and not force then
+ open_menu(youtube_menu_data(cache.youtube))
+ return
+ end
+
+ local channels = read_youtube_channels()
+ local args = { "curl", "-s", "--parallel", "--parallel-max", "16", "-m", "25" }
+ for _, c in ipairs(channels) do
+ args[#args + 1] = "https://www.youtube.com/feeds/videos.xml?channel_id=" .. c.id
+ end
+
+ if #channels == 0 then
+ open_menu({
+ type = "yt_feed",
+ title = "Abonnements YouTube",
+ items = { { title = "Aucune chaîne lue dans " .. o.youtube_csv, selectable = false } },
+ })
+ return
+ end
+
+ msg.info(#channels .. " chaînes à interroger")
+ loading_menu("yt_feed", "Abonnements YouTube")
+
+ mp.command_native_async({
+ name = "subprocess",
+ args = args,
+ capture_stdout = true,
+ playback_only = false,
+ }, function(_, res)
+ local videos = {}
+ if res and res.stdout and #res.stdout > 0 then
+ videos = parse_feeds(res.stdout)
+ end
+ msg.info(
+ string.format(
+ "curl status=%s, %d octets, %d vidéos",
+ tostring(res and res.status),
+ res and res.stdout and #res.stdout or 0,
+ #videos
+ )
+ )
+ cache.youtube = videos
+ update_menu(youtube_menu_data(videos))
+ end)
+end
+
+--------------------------------------------------- Parcourir une chaîne
+
+local function channel_videos_menu_data(name, videos)
+ local items = {}
+ for _, v in ipairs(videos) do
+ items[#items + 1] = {
+ title = v.title,
+ hint = v.duration,
+ value = { "script-message", "feeds-play", v.url },
+ }
+ end
+ if #items == 0 then
+ items = { { title = "Rien récupéré — voir les logs (--msg-level=feeds=info)", selectable = false } }
+ end
+ return { type = "yt_channel", title = name, items = items }
+end
+
+local function channel_videos_menu(id, name)
+ if cache.channels[id] then
+ open_menu(channel_videos_menu_data(name, cache.channels[id]))
+ return
+ end
+
+ loading_menu("yt_channel", name)
+
+ mp.command_native_async({
+ name = "subprocess",
+ args = {
+ "yt-dlp",
+ "--flat-playlist",
+ "--dump-single-json",
+ "--no-warnings",
+ "--playlist-items",
+ "1:" .. tostring(o.channel_videos),
+ "https://www.youtube.com/channel/" .. id .. "/videos",
+ },
+ capture_stdout = true,
+ playback_only = false,
+ }, function(_, res)
+ local videos = {}
+ if res and res.stdout and #res.stdout > 0 then
+ local ok, data = pcall(utils.parse_json, res.stdout)
+ if ok and data and data.entries then
+ for _, e in ipairs(data.entries) do
+ if e.id then
+ videos[#videos + 1] = {
+ title = e.title or e.id,
+ duration = format_duration(e.duration),
+ url = "https://www.youtube.com/watch?v=" .. e.id,
+ }
+ end
+ end
+ end
+ end
+ msg.info(
+ string.format(
+ "yt-dlp status=%s, %d octets, %d vidéos (%s)",
+ tostring(res and res.status),
+ res and res.stdout and #res.stdout or 0,
+ #videos,
+ name
+ )
+ )
+ cache.channels[id] = videos
+ update_menu(channel_videos_menu_data(name, videos))
+ end)
+end
+
+mp.register_script_message("feeds-channel", function(id, name)
+ channel_videos_menu(id, name)
+end)
+
+local function channels_menu()
+ local channels = read_youtube_channels()
+ local items = {}
+ for _, c in ipairs(channels) do
+ items[#items + 1] = {
+ title = c.title,
+ value = { "script-message", "feeds-channel", c.id, c.title },
+ }
+ end
+ if #items == 0 then
+ items = { { title = "Aucune chaîne lue dans " .. o.youtube_csv, selectable = false } }
+ end
+ -- tape simplement quelques lettres pour filtrer la liste
+ open_menu({ type = "yt_channels", title = "Parcourir une chaîne", items = items })
+end
+
+--------------------------------------------------- Twitch
+
+local function twitch_menu_data(channels)
+ local items = {}
+ for _, c in ipairs(channels) do
+ local mark = ""
+ if c.live == true then
+ mark = "🔴 "
+ elseif c.live == false then
+ mark = "· "
+ end
+ items[#items + 1] = {
+ title = mark .. (c.label ~= "" and c.label or c.login),
+ hint = c.live == true and "en direct" or nil,
+ value = { "script-message", "feeds-twitch", c.login },
+ }
+ end
+ if #items == 0 then
+ items = { { title = "Aucune chaîne lue dans " .. o.twitch_csv, selectable = false } }
+ end
+ return { type = "twitch_feed", title = "Twitch", items = items }
+end
+
+local function twitch_menu(force)
+ if cache.twitch and not force then
+ open_menu(twitch_menu_data(cache.twitch))
+ return
+ end
+
+ local channels = {}
+ for _, line in ipairs(read_lines(o.twitch_csv)) do
+ local login = line:match("^([%w_][%w_]*)")
+ if login and login:lower() ~= "login" then -- ignore l'en-tête éventuel
+ local label = trim(line:match("^[^,]*,(.*)$") or "")
+ channels[#channels + 1] = { login = login, label = label, live = nil }
+ end
+ end
+
+ if #channels == 0 or not o.twitch_live_check then
+ cache.twitch = channels
+ open_menu(twitch_menu_data(channels))
+ return
+ end
+
+ loading_menu("twitch_feed", "Twitch")
+
+ local logins = {}
+ for _, c in ipairs(channels) do
+ logins[#logins + 1] = c.login
+ end
+
+ local script = [[
+for c in ]] .. table.concat(logins, " ") .. [[; do
+ ( if curl -sL -m 8 "https://www.twitch.tv/$c" | grep -q '"isLiveBroadcast":true'; then
+ echo "$c live"
+ else
+ echo "$c off"
+ fi ) &
+done
+wait
+]]
+
+ mp.command_native_async({
+ name = "subprocess",
+ args = { "sh", "-c", script },
+ capture_stdout = true,
+ playback_only = false,
+ }, function(_, res)
+ local nb_live = 0
+ if res and res.stdout and #res.stdout > 0 then
+ local status = {}
+ for login, state in res.stdout:gmatch("(%S+)%s+(%S+)") do
+ status[login] = (state == "live")
+ end
+ for _, c in ipairs(channels) do
+ if status[c.login] ~= nil then
+ c.live = status[c.login]
+ if c.live then
+ nb_live = nb_live + 1
+ end
+ end
+ end
+ table.sort(channels, function(a, b)
+ if a.live ~= b.live then
+ return a.live == true
+ end
+ return a.login < b.login
+ end)
+ end
+ msg.info(
+ string.format(
+ "twitch status=%s, %d chaînes, %d en direct",
+ tostring(res and res.status),
+ #channels,
+ nb_live
+ )
+ )
+ cache.twitch = channels
+ update_menu(twitch_menu_data(channels))
+ end)
+end
+
+--------------------------------------------------- Menu racine
+
+mp.register_script_message("feeds-open", function(which, force)
+ local refresh = (force == "refresh")
+ if which == "youtube" then
+ youtube_menu(refresh)
+ elseif which == "channels" then
+ channels_menu()
+ elseif which == "twitch" then
+ twitch_menu(refresh)
+ end
+end)
+
+mp.add_key_binding(o.key, "feeds-menu", function()
+ open_menu({
+ type = "feeds",
+ title = "Chaînes",
+ items = {
+ {
+ title = "Abonnements YouTube",
+ icon = "subscriptions",
+ value = { "script-message", "feeds-open", "youtube" },
+ },
+ {
+ title = "Parcourir une chaîne",
+ icon = "video_library",
+ value = { "script-message", "feeds-open", "channels" },
+ },
+ {
+ title = "Twitch",
+ icon = "live_tv",
+ value = { "script-message", "feeds-open", "twitch" },
+ },
+ {
+ title = "Rafraîchir YouTube",
+ icon = "refresh",
+ muted = true,
+ value = { "script-message", "feeds-open", "youtube", "refresh" },
+ },
+ {
+ title = "Rafraîchir Twitch",
+ icon = "refresh",
+ muted = true,
+ value = { "script-message", "feeds-open", "twitch", "refresh" },
+ },
+ },
+ })
+end)
diff --git a/dotfiles/mpv/twitch.csv b/dotfiles/mpv/twitch.csv
new file mode 100644
index 0000000..17bd2c0
--- /dev/null
+++ b/dotfiles/mpv/twitch.csv
@@ -0,0 +1,13 @@
+Wissam_Xelka
+HasanAbi
+Zawa_Prod
+Cass_Andre
+Ostpolitik
+dofla
+LeMawakast
+ParolesDHonneur_
+lives2leo
+MOuffette_
+KaLeeVision
+Bamwempan
+RiboDansLaSauce
diff --git a/dotfiles/mpv/youtube.csv b/dotfiles/mpv/youtube.csv
new file mode 100644
index 0000000..5836bdd
--- /dev/null
+++ b/dotfiles/mpv/youtube.csv
@@ -0,0 +1,101 @@
+ID des chaînes,URL des chaînes,Titres des chaînes
+UC0HxyEc_ojRJ1oJXS5K6oaA,http://www.youtube.com/channel/UC0HxyEc_ojRJ1oJXS5K6oaA,Politikon
+UC0Miwa4YsGduqJFYmpCSXeg,http://www.youtube.com/channel/UC0Miwa4YsGduqJFYmpCSXeg,taoqan
+UC0fDG3byEcMtbOqPMymDNbw,http://www.youtube.com/channel/UC0fDG3byEcMtbOqPMymDNbw,/noclip
+UC0yPCUmdMZIGtnxSnx5_ifA,http://www.youtube.com/channel/UC0yPCUmdMZIGtnxSnx5_ifA,Tzitzimitl - Esprit Critique
+UC1ObD9AuLfxiqRkrY7A_kgA,http://www.youtube.com/channel/UC1ObD9AuLfxiqRkrY7A_kgA,Les vidéos de Léo
+UC1eBAh5DIkn9N_5XzMS0wFg,http://www.youtube.com/channel/UC1eBAh5DIkn9N_5XzMS0wFg,Les Amoureuses
+UC1zVvHsRsvxInUMX4dfIyAQ,http://www.youtube.com/channel/UC1zVvHsRsvxInUMX4dfIyAQ,The Sciencoder
+UC2qlgiYCCtaYpn2_blX01xg,http://www.youtube.com/channel/UC2qlgiYCCtaYpn2_blX01xg,Antoine Goya
+UC2rpRj3VqEMaT7vgtiBnQoQ,http://www.youtube.com/channel/UC2rpRj3VqEMaT7vgtiBnQoQ,Veridis Project
+UC3A_TG1leX0eQEJD1Ew6Ftw,http://www.youtube.com/channel/UC3A_TG1leX0eQEJD1Ew6Ftw,Game Of Hearth
+UC476clcLNhTKwjdulsfvHLw,http://www.youtube.com/channel/UC476clcLNhTKwjdulsfvHLw,imranepresents
+UC4vfXTvb9fysmiGab_cdJXg,http://www.youtube.com/channel/UC4vfXTvb9fysmiGab_cdJXg,Legendes Musique
+UC5AA57FT52rBCyGc9UT1DDg,http://www.youtube.com/channel/UC5AA57FT52rBCyGc9UT1DDg,Emmodem VOD
+UC5eOLQO5VUEFJukNg9cl5jg,http://www.youtube.com/channel/UC5eOLQO5VUEFJukNg9cl5jg,Dave Sheik
+UC6zC37ITu2dNx3GimE8UV2A,http://www.youtube.com/channel/UC6zC37ITu2dNx3GimE8UV2A,Mathys Aurand
+UC7sXGI8p8PvKosLWagkK9wQ,http://www.youtube.com/channel/UC7sXGI8p8PvKosLWagkK9wQ,Heu?reka
+UC85YiwWx6BC6SYR7wuMyG8A,http://www.youtube.com/channel/UC85YiwWx6BC6SYR7wuMyG8A,SLICE Histoire
+UC8cQpBzgnpkHPMkEpnoWWcQ,http://www.youtube.com/channel/UC8cQpBzgnpkHPMkEpnoWWcQ,Three Hours Later
+UC9_qAALb4xSC4cWyuzECs5g,http://www.youtube.com/channel/UC9_qAALb4xSC4cWyuzECs5g,Saturne
+UC9lWSxp7TdohmRwnvSR2Gvw,http://www.youtube.com/channel/UC9lWSxp7TdohmRwnvSR2Gvw,RiboDansLaSauce
+UC9mes7ZXbrZ_UXn0jzBt21A,http://www.youtube.com/channel/UC9mes7ZXbrZ_UXn0jzBt21A,Bon Pote
+UC9r6ydGn4TA8PexLQlgWSmQ,http://www.youtube.com/channel/UC9r6ydGn4TA8PexLQlgWSmQ,Réflexions Basses
+UC9w4KQLmoOOBhhFwcKnK7UA,http://www.youtube.com/channel/UC9w4KQLmoOOBhhFwcKnK7UA,Max Atger
+UCAJta1-T5V_RB7YDfp7GQag,http://www.youtube.com/channel/UCAJta1-T5V_RB7YDfp7GQag,Headlundas
+UCAbFIrKZCYdKucQYnhxWnrA,http://www.youtube.com/channel/UCAbFIrKZCYdKucQYnhxWnrA,LIMIT
+UCBm4zCKYn0Iirik1j5yRTfg,http://www.youtube.com/channel/UCBm4zCKYn0Iirik1j5yRTfg,Satellisés
+UCCTbruTofQHTMkmG8cEJ4fQ,http://www.youtube.com/channel/UCCTbruTofQHTMkmG8cEJ4fQ,Jack Couscous
+UCCTuFofVWD9uORhQqgn-d7Q,http://www.youtube.com/channel/UCCTuFofVWD9uORhQqgn-d7Q,INA Société
+UCCauEjHzPXxGer8af3u4x5g,http://www.youtube.com/channel/UCCauEjHzPXxGer8af3u4x5g,Envoyé Spécial
+UCDanmCiN4kaOO_tOOv81s6g,http://www.youtube.com/channel/UCDanmCiN4kaOO_tOOv81s6g,Emmodem Vod
+UCEFGCs68E9Vr5te5qZuJsbg,http://www.youtube.com/channel/UCEFGCs68E9Vr5te5qZuJsbg,Off Investigation
+UCEfpbmshpf-G90fGLla73kA,http://www.youtube.com/channel/UCEfpbmshpf-G90fGLla73kA,Bolchegeek
+UCEsShazY9frY_x51rknY2dw,http://www.youtube.com/channel/UCEsShazY9frY_x51rknY2dw,Dandee
+UCF8wHRQmuBpOuvQvsfSFMnQ,http://www.youtube.com/channel/UCF8wHRQmuBpOuvQvsfSFMnQ,Faustin Sullivan
+UCGjKwjEvPA0ms2wOyBwdMPQ,http://www.youtube.com/channel/UCGjKwjEvPA0ms2wOyBwdMPQ,Parlons Cyber
+UCHOtlkL8XEHbL6KHIHYRGSw,http://www.youtube.com/channel/UCHOtlkL8XEHbL6KHIHYRGSw,Franck Lepage
+UCHiwtz2tCEfS17N9A-WoSSw,http://www.youtube.com/channel/UCHiwtz2tCEfS17N9A-WoSSw,Pop Culture Detective
+UCI_FnSJPNGd3Y3IL0PwD-NQ,http://www.youtube.com/channel/UCI_FnSJPNGd3Y3IL0PwD-NQ,Diable Positif
+UCIlNSCHPwun7060OYU5thrw,http://www.youtube.com/channel/UCIlNSCHPwun7060OYU5thrw,Barap
+UCJ9kIKgFuAB23GkEko1hHDg,http://www.youtube.com/channel/UCJ9kIKgFuAB23GkEko1hHDg,Mycéliums
+UCJT6bmaCnlJDq5Yh1EfoSQw,http://www.youtube.com/channel/UCJT6bmaCnlJDq5Yh1EfoSQw,Grünt
+UCJd1AFjN9vIgVDDB6UE2UQg,http://www.youtube.com/channel/UCJd1AFjN9vIgVDDB6UE2UQg,Marouchka de Qu'est-ce qu'on lit ?
+UCJpIF-QIOk1YOjSqD5eqyqQ,http://www.youtube.com/channel/UCJpIF-QIOk1YOjSqD5eqyqQ,Elyne Simon
+UCLXDNUOO3EQ80VmD9nQBHPg,http://www.youtube.com/channel/UCLXDNUOO3EQ80VmD9nQBHPg,Fouloscopie
+UCLdmnkqdcTPHvVZ8aNdbf5A,http://www.youtube.com/channel/UCLdmnkqdcTPHvVZ8aNdbf5A,Spline LND
+UCNxe55xhsqp_w6k6GEor8Uw,http://www.youtube.com/channel/UCNxe55xhsqp_w6k6GEor8Uw,Papy fait de la Resistance
+UCP46_MXP_WG_auH88FnfS1A,http://www.youtube.com/channel/UCP46_MXP_WG_auH88FnfS1A,Nota Bene
+UCPTlw9-dflN3_Sw9AfQs3vw,http://www.youtube.com/channel/UCPTlw9-dflN3_Sw9AfQs3vw,Faune Cool
+UCPVS8efBdhXGGYs4JW2IJcQ,http://www.youtube.com/channel/UCPVS8efBdhXGGYs4JW2IJcQ,Kiffe ta race
+UCPZIJMFJBoyG7t7nsUQ3UcA,http://www.youtube.com/channel/UCPZIJMFJBoyG7t7nsUQ3UcA,Nedrac
+UCPo1nFSGNkyzrA9yvtkEvIw,http://www.youtube.com/channel/UCPo1nFSGNkyzrA9yvtkEvIw,Eliott Meunier
+UCQHX6ViZmPsWiYSFAyS0a3Q,http://www.youtube.com/channel/UCQHX6ViZmPsWiYSFAyS0a3Q,GothamChess
+UCQcas4dulTzVUrAo0fMfWWg,http://www.youtube.com/channel/UCQcas4dulTzVUrAo0fMfWWg,Clément Viktorovitch
+UCRoQyGLLPd9lwPUJtUwD2yw,http://www.youtube.com/channel/UCRoQyGLLPd9lwPUJtUwD2yw,Samora | Le Mwakast
+UCT67YOMntJxfRnO_9bXDpvw,http://www.youtube.com/channel/UCT67YOMntJxfRnO_9bXDpvw,Le Média
+UCTth6-5ZiCD_CUJ6xOMug_w,http://www.youtube.com/channel/UCTth6-5ZiCD_CUJ6xOMug_w,Paroles D'Honneur
+UCUc-m8A95mE9zLlNvVtLfFQ,http://www.youtube.com/channel/UCUc-m8A95mE9zLlNvVtLfFQ,Orange Trio Music
+UCVeMw72tepFl1Zt5fvf9QKQ,http://www.youtube.com/channel/UCVeMw72tepFl1Zt5fvf9QKQ,Osons Causer
+UCVuMMUfqEI448VKbkpuNPHQ,http://www.youtube.com/channel/UCVuMMUfqEI448VKbkpuNPHQ,Histoires Crépues
+UCWGsN59FON3vOtXCozQPsZQ,http://www.youtube.com/channel/UCWGsN59FON3vOtXCozQPsZQ,Canard Réfractaire
+UCXHWT_QoQSsdGXbqF0hAMtg,http://www.youtube.com/channel/UCXHWT_QoQSsdGXbqF0hAMtg,Arrêt sur images
+UCYpRDnhk5H8h16jpS84uqsA,http://www.youtube.com/channel/UCYpRDnhk5H8h16jpS84uqsA,Le Monde
+UC_-hYjoNe4PJNFa9iZ4lraA,http://www.youtube.com/channel/UC_-hYjoNe4PJNFa9iZ4lraA,Good Work
+UC_2-ii_May8L-lx25YJhSew,http://www.youtube.com/channel/UC_2-ii_May8L-lx25YJhSew,Fracas
+UC_NukSq2ve_AHVAz_9ikTDg,http://www.youtube.com/channel/UC_NukSq2ve_AHVAz_9ikTDg,Usul
+UC__xRB5L4toU9yYawt_lIKg,http://www.youtube.com/channel/UC__xRB5L4toU9yYawt_lIKg,"BLAST, Le souffle de l'info"
+UCaU9xFZi08_meRCb9Uzr_Ew,http://www.youtube.com/channel/UCaU9xFZi08_meRCb9Uzr_Ew,L'Argumentarium
+UCafxR2HWJRmMfSdyZXvZMTw,http://www.youtube.com/channel/UCafxR2HWJRmMfSdyZXvZMTw,LOOK MUM NO COMPUTER
+UCar0yo-PpeMk51m116826pQ,http://www.youtube.com/channel/UCar0yo-PpeMk51m116826pQ,Journal l'Humanité
+UCd5DKToXYTKAQ6khzewww2g,http://www.youtube.com/channel/UCd5DKToXYTKAQ6khzewww2g,France Culture
+UCdKTlsmvczkdvGjiLinQwmw,http://www.youtube.com/channel/UCdKTlsmvczkdvGjiLinQwmw,Philoxime
+UCdnaDhU-LDQrIEEmSIfq0-Q,http://www.youtube.com/channel/UCdnaDhU-LDQrIEEmSIfq0-Q,Mediapart
+UCe6iUNw4s5VxGnWi63JRU3g,http://www.youtube.com/channel/UCe6iUNw4s5VxGnWi63JRU3g,Bamwempan
+UCeQRERCvEY3-ZeQONwnx0zw,http://www.youtube.com/channel/UCeQRERCvEY3-ZeQONwnx0zw,Zawa Prod
+UCebfFtcRXJWGgAHi0h8n2JA,http://www.youtube.com/channel/UCebfFtcRXJWGgAHi0h8n2JA,Sismique
+UCeovElJP0n0i8ADaPsRSd8g,http://www.youtube.com/channel/UCeovElJP0n0i8ADaPsRSd8g,HAINBACH
+UCg5OUM5Y5UWJHJgFCjXcfkA,http://www.youtube.com/channel/UCg5OUM5Y5UWJHJgFCjXcfkA,Way Back 2 Afrika
+UChG8nxeVTk6jQJyH-_TG7xw,http://www.youtube.com/channel/UChG8nxeVTk6jQJyH-_TG7xw,InPower Podcast
+UChYdEwKt1QyP27OwzmVObnA,http://www.youtube.com/channel/UChYdEwKt1QyP27OwzmVObnA,Blast2
+UChaI6XKLJJHZu70_QJFQjKA,http://www.youtube.com/channel/UChaI6XKLJJHZu70_QJFQjKA,Gael Berlinger
+UCi1h68Fys0apnhLaJ-ijQOw,http://www.youtube.com/channel/UCi1h68Fys0apnhLaJ-ijQOw,INA Politique
+UCkgO4A3Fzm5D9Xu1Y_4vCKQ,http://www.youtube.com/channel/UCkgO4A3Fzm5D9Xu1Y_4vCKQ,ÉLUCID
+UCn1eIYIqfPOhj-KhDmbG8KA,http://www.youtube.com/channel/UCn1eIYIqfPOhj-KhDmbG8KA,AMI | INSA Lyon
+UCnP7HeJGVy8EL1L-1gTDm8w,http://www.youtube.com/channel/UCnP7HeJGVy8EL1L-1gTDm8w,STUPMEDIA
+UCoobqoKhnrJhgP8v2bx3ZnA,http://www.youtube.com/channel/UCoobqoKhnrJhgP8v2bx3ZnA,SOCIOLOGIK
+UCp9cUr3zrGguWsm5oIa2VAQ,http://www.youtube.com/channel/UCp9cUr3zrGguWsm5oIa2VAQ,Bignou Collectif
+UCqt99sKYNTxqlHtzV9weUYA,http://www.youtube.com/channel/UCqt99sKYNTxqlHtzV9weUYA,VU FranceTV
+UCrzwP_TnWL_FA-PjIZ9qaZg,http://www.youtube.com/channel/UCrzwP_TnWL_FA-PjIZ9qaZg,Cass Andre
+UCt3XYjlua5RtcHyEKUkUKOQ,http://www.youtube.com/channel/UCt3XYjlua5RtcHyEKUkUKOQ,Kong DIRTY BOXING
+UCvW-hY30MCw1qrxAN2HeeZg,http://www.youtube.com/channel/UCvW-hY30MCw1qrxAN2HeeZg,À L'Assaut du Ciel
+UCwI-JbGNsojunnHbFAc0M4Q,http://www.youtube.com/channel/UCwI-JbGNsojunnHbFAc0M4Q,ARTE
+UCwU7Gq3vG5kL1eknXIZvMag,http://www.youtube.com/channel/UCwU7Gq3vG5kL1eknXIZvMag,Wissam Xelka
+UCwaliuWsJWhdhk-KUfQHlYA,http://www.youtube.com/channel/UCwaliuWsJWhdhk-KUfQHlYA,Metabolism of Cities
+UCweCc7bSMX5J4jEH7HFImng,http://www.youtube.com/channel/UCweCc7bSMX5J4jEH7HFImng,GMHikaru
+UCxJka_qnTVquoIL-PQV5POg,http://www.youtube.com/channel/UCxJka_qnTVquoIL-PQV5POg,DanyCaligula
+UCy4CtZFhDCSkNnr6GUypacw,http://www.youtube.com/channel/UCy4CtZFhDCSkNnr6GUypacw,Síomha
+UCyJDHgrsUKuWLe05GvC2lng,http://www.youtube.com/channel/UCyJDHgrsUKuWLe05GvC2lng,Stupid Economics
+UCyjXo7RjhDQ1oX7Q7CPUUIQ,http://www.youtube.com/channel/UCyjXo7RjhDQ1oX7Q7CPUUIQ,Avant l'orage
+UCzB5vMdtF0qAf84nV3HrD2A,http://www.youtube.com/channel/UCzB5vMdtF0qAf84nV3HrD2A,Thomas Babord
+
diff --git a/flake.lock b/flake.lock
index 3c6c14b..ec4d38e 100644
--- a/flake.lock
+++ b/flake.lock
@@ -147,11 +147,11 @@
]
},
"locked": {
- "lastModified": 1786031233,
- "narHash": "sha256-TIDlLTLI1/pB7IqgjzcKQjpODQsZE2oII4XGG9B6KjI=",
+ "lastModified": 1786286733,
+ "narHash": "sha256-7dN93WKkEERgutPtTjK7SY3ZarO2LAU33A1+vKu+7cc=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "7834e82588860aaf780cec1366524456a70898d7",
+ "rev": "367f7ef80856d18438953d54dda235d42a46ba31",
"type": "github"
},
"original": {
@@ -267,11 +267,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1785967620,
- "narHash": "sha256-IItrdb7Puk05RqOBWZYFC5X6Wl1sJmCfh5MWVHw5iMM=",
+ "lastModified": 1786106723,
+ "narHash": "sha256-zDSUbpoeo/9ZmD2+wXnzxoo1+uhL8vxc0b8yuYMKYq0=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "b7c2ada94fe99c15b0dbcf4d11fd7850b957a436",
+ "rev": "f13ff45afd1bb73e640eaa08a7066dbed07e3238",
"type": "github"
},
"original": {
@@ -336,11 +336,11 @@
"tinted-zed": "tinted-zed"
},
"locked": {
- "lastModified": 1786144742,
- "narHash": "sha256-ROqWcNC1qYU2p0EY1d9BHnZbcl0ZDaCmXtojENSqL18=",
+ "lastModified": 1786216702,
+ "narHash": "sha256-fOOmw9tUYeqWVnK2w2NiuMTmGIbluj8xiBFTTFDCcEE=",
"owner": "danth",
"repo": "stylix",
- "rev": "d18ba6834c7f1141429b6d4cf148fff6c8c41139",
+ "rev": "b67debfd2db1eb1f0fd8740b950f598bbaf74b8f",
"type": "github"
},
"original": {
@@ -472,11 +472,11 @@
]
},
"locked": {
- "lastModified": 1786171986,
- "narHash": "sha256-kvmCZLYbByuE+zskqtf5+UPGrFI8e8KspB8jVey0nj0=",
+ "lastModified": 1786290296,
+ "narHash": "sha256-V5WOJhCGsuSRkz60K8TcALKMiMVCNubfJquMJqoCow0=",
"owner": "0xc000022070",
"repo": "zen-browser-flake",
- "rev": "f3a721857afb4c34f687390213e0bc034d8e4999",
+ "rev": "a1f244256e643fe7bd3f811e75b4267fd180cd7c",
"type": "github"
},
"original": {
diff --git a/home/afoucaultc/packages/mpv.nix b/home/afoucaultc/packages/mpv.nix
index 372d23a..976aeda 100644
--- a/home/afoucaultc/packages/mpv.nix
+++ b/home/afoucaultc/packages/mpv.nix
@@ -1,6 +1,32 @@
-{ ... }:
+{ pkgs, ... }:
{
+ home.packages = with pkgs; [
+ yt-dlp
+ streamlink
+ curl
+ ];
+
programs.mpv = {
enable = true;
+ scripts = with pkgs.mpvScripts; [
+ uosc
+ thumbfast
+ sponsorblock
+ ];
+ config = {
+ osc = false;
+ border = false;
+ ytdl-format = "bestvideo[height<=?1080]+bestaudio/best";
+ save-position-on-quit = true;
+ };
+ scriptOpts.feeds = {
+ youtube_csv = "~/nixos-config/dotfiles/mpv/youtube.csv";
+ twitch_csv = "~/nixos-config/dotfiles/mpv/twitch.csv";
+ key = "ctrl+h";
+ # twitch_live_check = "no";
+ };
};
+
+ # destination = relative à ~/.config ; source = ./feeds.lua dans ton dépôt
+ xdg.configFile."mpv/scripts/feeds.lua".source = ../../../dotfiles/mpv/feeds.lua;
}
diff --git a/home/afoucaultc/packages/niri/conf/misc.nix b/home/afoucaultc/packages/niri/conf/misc.nix
index 5db8427..792eb20 100644
--- a/home/afoucaultc/packages/niri/conf/misc.nix
+++ b/home/afoucaultc/packages/niri/conf/misc.nix
@@ -31,7 +31,7 @@
};
clipboard.disable-primary = false;
- hotkey-overlay.skip-at-startup = false;
+ hotkey-overlay.skip-at-startup = true;
overview.zoom = 0.5;
};
}
diff --git a/home/afoucaultc/packages/niri/conf/window-rules.nix b/home/afoucaultc/packages/niri/conf/window-rules.nix
index 4716895..eae72e2 100644
--- a/home/afoucaultc/packages/niri/conf/window-rules.nix
+++ b/home/afoucaultc/packages/niri/conf/window-rules.nix
@@ -25,5 +25,9 @@
opacity = 0.95;
clip-to-geometry = true;
}
+ {
+ matches = [ { app-id = "^mpv$"; } ];
+ open-fullscreen = true;
+ }
];
}