rssViedos mostly

This commit is contained in:
Geoffrey Frogeye 2020-12-27 14:20:44 +01:00
parent ceb1e40964
commit 709239dfca
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8
17 changed files with 479 additions and 111 deletions

View file

@ -86,7 +86,7 @@
- name: Download base16 theme for qutebrowser - name: Download base16 theme for qutebrowser
get_url: get_url:
url: "https://raw.githubusercontent.com/theova/base16-qutebrowser/master/themes/default/base16-{{ base16_scheme }}.config.py" url: "https://raw.githubusercontent.com/theova/base16-qutebrowser/main/themes/default/base16-{{ base16_scheme }}.config.py"
dest: "{{ ansible_env.HOME }}/.config/qutebrowser/theme.py" dest: "{{ ansible_env.HOME }}/.config/qutebrowser/theme.py"
mode: "u+rw,g=r,o=r" mode: "u+rw,g=r,o=r"
notify: notify:
@ -111,8 +111,7 @@
dest: "{{ ansible_env.HOME }}/.config/dunst/dunstrc" dest: "{{ ansible_env.HOME }}/.config/dunst/dunstrc"
mode: "u+rw,g=r,o=r" mode: "u+rw,g=r,o=r"
# TODO mechanism to switching light/dark quickly # TODO Generate themes locally because not being able to switch themes because GitHub being down is just silly
# TODO dunst (template online, but not to my liking)
# TODO bar (might change bar in the future, so...) # TODO bar (might change bar in the future, so...)
# TODO highlight (there IS a template but the colors look different from vim and mostly the same from when there's no config) # TODO highlight (there IS a template but the colors look different from vim and mostly the same from when there's no config)
# TODO https://github.com/makuto/auto-base16-theme ? :P # TODO https://github.com/makuto/auto-base16-theme ? :P

1
config/ccache.conf Normal file
View file

@ -0,0 +1 @@
ccache_dir = $HOME/.cache/ccache

3
config/flake8 Normal file
View file

@ -0,0 +1,3 @@
[flake8]
# Compatibility with Black
max-line-length = 88

View file

@ -381,7 +381,7 @@ exec --no-startup-id keynav # Keyboard cursor controller
# exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill # exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill
exec --no-startup-id autorandr --change --force # Screen configuration and everything that depends on it exec --no-startup-id autorandr --change --force # Screen configuration and everything that depends on it
exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification
exec --no-startup-id ~/.config/i3/aw_start # Activity tracker # exec --no-startup-id ~/.config/i3/aw_start # Activity tracker
{{ lookup('file', ansible_env.HOME + '/.config/i3/theme') }} {{ lookup('file', ansible_env.HOME + '/.config/i3/theme') }}
@ -389,7 +389,7 @@ set $ignore #ff00ff
# Basic color configuration using the Base16 variables for windows and borders. # Basic color configuration using the Base16 variables for windows and borders.
# Property Name Border BG Text Indicator Child Border # Property Name Border BG Text Indicator Child Border
client.focused $base00 $base00 $base05 $base00 $base07 client.focused $base0B $base0B $base00 $base00 $base0B
client.focused_inactive $base02 $base02 $base05 $base02 $base02 client.focused_inactive $base02 $base02 $base05 $base02 $base02
client.unfocused $base05 $base04 $base00 $base04 $base00 client.unfocused $base05 $base04 $base00 $base04 $base00
client.urgent $base0F $base08 $base00 $base08 $base0F client.urgent $base0F $base08 $base00 $base08 $base0F

3
config/pycodestyle Normal file
View file

@ -0,0 +1,3 @@
[pycodestyle]
# Compatibility with Black
max-line-length = 88

8
config/scripts/crepuscule Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# TODO De-hardcode
cd ~/.dotfiles/config/automatrop
echo 30000 | sudo tee /sys/class/backlight/intel_backlight/brightness
xrandr --output HDMI-0 --brightness 1
ansible-playbook playbooks/default.yml -e base16_scheme=solarized-dark

8
config/scripts/jour Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# TODO De-hardcode
cd ~/.dotfiles/config/automatrop
echo 30000 | sudo tee /sys/class/backlight/intel_backlight/brightness
xrandr --output HDMI-0 --brightness 1
ansible-playbook playbooks/default.yml -e base16_scheme=solarized-light

58
config/scripts/mediaDuration Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
import os
import sys
import subprocess
import logging
import coloredlogs
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger()
def duration_file(path: str) -> float:
cmd = [
"ffprobe",
"-v",
"error",
"-show_entries",
"format=duration",
"-of",
"default=noprint_wrappers=1:nokey=1",
path,
]
run = subprocess.run(cmd, stdout=subprocess.PIPE)
ret = run.stdout.decode().strip()
if run.returncode != 0:
log.warning(f"{path}: unable to get duration")
elif ret == 'N/A':
log.warning(f"{path}: has no duration")
else:
try:
return float(ret)
except ValueError:
log.error(f"{path}: returned {ret}")
return 0
def duration_directory(path: str) -> float:
total = 0.0
for root, dirs, files in os.walk(path):
for f in files:
fullPath = os.path.join(root, f)
total += duration_file(fullPath)
return total
total = 0.0
for arg in sys.argv[1:]:
if os.path.isfile(arg):
total += duration_file(arg)
elif os.path.isdir(arg):
total += duration_directory(arg)
else:
raise FileNotFoundError(f"No such file or directory: '{arg}'")
print(total)

8
config/scripts/nuit Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# TODO De-hardcode
cd ~/.dotfiles/config/automatrop
echo 1 | sudo tee /sys/class/backlight/intel_backlight/brightness
xrandr --output HDMI-0 --brightness 0.5
ansible-playbook playbooks/default.yml -e base16_scheme=solarized-dark

View file

@ -10,10 +10,10 @@ with the unread items (non-video links are ignored).
# TODO Distribute this correclty, in the meanwhile please do # TODO Distribute this correclty, in the meanwhile please do
# pip install --user youtube-dl ConfigArgParse progressbar2 # pip install --user youtube-dl ConfigArgParse progressbar2
# TODO Allow to specify youtube_dl options (e.g. subtitles) # TODO Better logging (youtube-dl allow to pass loggers)
# TODO Restrict quality (it's not that I don't like 8GB 4K videos but...)
from typing import Dict, Set import sys
from typing import Dict, Set, Tuple
import urllib.request import urllib.request
import urllib.parse import urllib.parse
import os import os
@ -22,27 +22,69 @@ import youtube_dl
import configargparse import configargparse
if __name__ == "__main__": def get_args() -> configargparse.Namespace:
defaultConfigPath = os.path.join(
os.path.expanduser(os.getenv("XDG_CONFIG_PATH", "~/.config/")), "rssVideos"
)
defaultConfigPath = os.path.join(os.path.expanduser( parser = configargparse.ArgParser(
os.getenv('XDG_CONFIG_PATH', '~/.config/')), 'rssVideos') description="Download videos linked in "
+ "a RSS feed (e.g. an unread feed from "
+ "an RSS aggregator",
parser = configargparse.ArgParser(description="Download videos linked in " + default_config_files=[defaultConfigPath],
"a RSS feed (e.g. an unread feed from " + )
"an RSS aggregator", parser.add(
default_config_files=[defaultConfigPath]) "-c", "--config", required=False, is_config_file=True, help="Configuration file"
parser.add('-c', '--config', required=False, is_config_file=True, )
help='Configuration file') parser.add(
parser.add('--feed', help='URL of the RSS feed (must be public for now)', "--feed",
env_var='RSS_VIDEOS_FEED', required=True) help="URL of the RSS feed (must be public for now)",
parser.add('--videos', help='Directory to store videos', env_var="RSS_VIDEOS_FEED",
env_var='RSS_VIDEOS_VIDEO_DIR', required=True) required=True,
parser.add('-n', '--dryrun', help='Do not download the videos', )
action='store_const', const=True, default=False) parser.add(
# TODO This feature might require additional documentation and an on/off switc "--videos",
parser.add('--track', help='Directory where download videos are maked (so they are not downloaded twice)', help="Directory to store videos",
env_var='RSS_VIDEOS_TRACK', required=False, default='.rssVideos') env_var="RSS_VIDEOS_VIDEO_DIR",
required=True,
)
parser.add(
"-n",
"--dryrun",
help="Do not download the videos",
action="store_const",
const=True,
default=False,
)
# TODO This feature might require additional documentation and an on/off switch
parser.add(
"--track",
help="Directory where download videos are marked "
+ "to not download them after deletion.",
env_var="RSS_VIDEOS_TRACK",
required=False,
default=".rssVideos",
)
parser.add(
"--max-duration",
help="Skip video longer than this amount of seconds",
env_var="RSS_VIDEOS_MAX_DURATION",
type=int,
default=0,
)
parser.add(
"--format",
help="Use this format to download videos."
+ " See FORMAT SELECTION in youtube-dl(1)",
env_var="RSS_VIDEOS_FORMAT",
default="bestvideo+bestaudio/best",
)
parser.add(
"--subtitles",
help="Download all subtitles",
env_var="RSS_VIDEOS_SUBTITLES",
action="store_true",
)
args = parser.parse_args() args = parser.parse_args()
args.videos = os.path.realpath(os.path.expanduser(args.videos)) args.videos = os.path.realpath(os.path.expanduser(args.videos))
@ -50,54 +92,70 @@ if __name__ == "__main__":
if not os.path.isabs(args.track): if not os.path.isabs(args.track):
args.track = os.path.realpath(os.path.join(args.videos, args.track)) args.track = os.path.realpath(os.path.join(args.videos, args.track))
os.makedirs(args.videos, exist_ok=True) return args
os.makedirs(args.track, exist_ok=True)
# Read the feed XML, get the links
print("→ Retrieveing RSS feed")
links: Set[str] = set() def get_links(args: configargparse.Namespace) -> Set[str]:
"""
Read the feed XML, get the links
"""
links = set()
with urllib.request.urlopen(args.feed) as request: with urllib.request.urlopen(args.feed) as request:
with minidom.parse(request) as xmldoc: with minidom.parse(request) as xmldoc:
for item in xmldoc.getElementsByTagName('item'): for item in xmldoc.getElementsByTagName("item"):
try: try:
linkNode = item.getElementsByTagName('link')[0] linkNode = item.getElementsByTagName("link")[0]
link: str = linkNode.childNodes[0].data link: str = linkNode.childNodes[0].data
links.add(link) links.add(link)
except BaseException as e: except BaseException as e:
print("Error while getting link from item:", e) print("Error while getting link from item:", e)
continue continue
return links
# Filter out non-video links and store video download info
# and associated filename
print(f"→ Getting infos on {len(links)} unread articles")
videosInfos: Dict[str, str] = {} def get_video_infos(
args: configargparse.Namespace, ydl_opts: Dict, links: Set[str]
) -> Dict[str, Dict]:
"""
Filter out non-video links and store video download info
and associated filename
"""
videosInfos = dict()
ydl_opts = { dry_ydl_opts = ydl_opts.copy()
"simulate": True, dry_ydl_opts.update({"simulate": True, "quiet": True})
"quiet": True with youtube_dl.YoutubeDL(dry_ydl_opts) as ydl:
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
for link in links: for link in links:
print(f"Researching {link}...") print(f"Researching {link}...")
try: try:
infos = ydl.extract_info(link) infos = ydl.extract_info(link)
if args.max_duration > 0 and infos["duration"] > args.max_duration:
print(
f"{infos['title']}: Skipping as longer than max duration: "
f"{infos['duration']} > {args.max_duration}"
)
continue
filepath = ydl.prepare_filename(infos) filepath = ydl.prepare_filename(infos)
filename, extension = os.path.splitext(filepath) filename, extension = os.path.splitext(filepath)
videosInfos[filename] = infos videosInfos[filename] = infos
print(f"{infos['title']}: Added")
except BaseException as e: except BaseException as e:
print(e) print(e)
continue continue
# Read the directory content, delete everything that's not a return videosInfos
# video on the download list or already downloaded
print(f"→ Deciding on what to do for {len(videosInfos)} videos")
# Getting information on the video directory
videosDownloaded: Set[str] = set() def get_downloaded_videos(
videosPartiallyDownloaded: Set[str] = set() args: configargparse.Namespace, videosInfos: Dict[str, Dict]
) -> Tuple[Set[str], Set[str]]:
videosDownloaded = set()
videosPartiallyDownloaded = set()
"""
Read the directory content, delete everything that's not a
video on the download list or already downloaded
"""
for filepath in os.listdir(args.videos): for filepath in os.listdir(args.videos):
fullpath = os.path.join(args.videos, filepath) fullpath = os.path.join(args.videos, filepath)
@ -106,12 +164,19 @@ if __name__ == "__main__":
filename, extension = os.path.splitext(filepath) filename, extension = os.path.splitext(filepath)
for onlineFilename in videosInfos.keys(): for onlineFilename in videosInfos.keys():
# Full name already there: completly downloaded → remove from the download list # Full name already there: completly downloaded
# → remove from the download list
if filename == onlineFilename: if filename == onlineFilename:
videosDownloaded.add(onlineFilename) videosDownloaded.add(onlineFilename)
break break
# Partial name already there: not completly downloaded → keep on the download list
elif filename.startswith(onlineFilename): elif filename.startswith(onlineFilename):
# Subtitle file
# → ignore
if filename.endswith(".vtt"):
break
# Partial name already there: not completly downloaded
# → keep on the download list
videosPartiallyDownloaded.add(onlineFilename) videosPartiallyDownloaded.add(onlineFilename)
break break
# Unrelated filename: delete # Unrelated filename: delete
@ -119,10 +184,17 @@ if __name__ == "__main__":
print(f"Deleting: {filename}") print(f"Deleting: {filename}")
os.unlink(fullpath) os.unlink(fullpath)
# Getting informations on the tracking directory return videosDownloaded, videosPartiallyDownloaded
# Videos that were once downloaded using this tool
videosTracked: Set[str] = set() def get_tracked_videos(args: configargparse.Namespace, known: Set[str]) -> Set[str]:
"""
Return videos previously downloaded (=tracked) amongst the unread videos.
This is stored in the tracking directory as empty extension-less files.
Other tracking markers (e.g. for now read videos) are deleted.
"""
videosTracked = set()
for filepath in os.listdir(args.track): for filepath in os.listdir(args.track):
fullpath = os.path.join(args.track, filepath) fullpath = os.path.join(args.track, filepath)
@ -130,18 +202,39 @@ if __name__ == "__main__":
continue continue
# Here filename is a filepath as no extension # Here filename is a filepath as no extension
if filepath in videosInfos: if filepath in known:
videosTracked.add(filepath) videosTracked.add(filepath)
else: else:
os.unlink(fullpath) os.unlink(fullpath)
return videosTracked
def main() -> None:
args = get_args()
os.makedirs(args.videos, exist_ok=True)
os.makedirs(args.track, exist_ok=True)
ydl_opts = {"format": args.format, "allsubtitles": args.subtitles}
print("→ Retrieveing RSS feed")
links = get_links(args)
print(f"→ Getting infos on {len(links)} unread articles")
videosInfos = get_video_infos(args, ydl_opts, links)
print(f"→ Deciding on what to do for {len(videosInfos)} videos")
videosDownloaded, videosPartiallyDownloaded = get_downloaded_videos(
args, videosInfos
)
videosTracked = get_tracked_videos(args, set(videosInfos.keys()))
# Deciding for the rest based on the informations # Deciding for the rest based on the informations
def markTracked(filename: str) -> None:
def markTracked(filename):
markerPath = os.path.join(args.track, onlineFilename) markerPath = os.path.join(args.track, onlineFilename)
open(markerPath, 'a').close() open(markerPath, "a").close()
videosToDownload: Set[str] = set() videosToDownload: Set[str] = set()
videosReads: Set[str] = set() videosReads: Set[str] = set()
@ -169,11 +262,10 @@ if __name__ == "__main__":
os.chdir(args.videos) os.chdir(args.videos)
exit_code = 0
if not args.dryrun: if not args.dryrun:
# TODO Progressbar one day maybe? # TODO Progressbar one day maybe?
# We have all the info we need to make a reliable one # We have all the info we need to make a reliable one
ydl_opts = {
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl: with youtube_dl.YoutubeDL(ydl_opts) as ydl:
for onlineFilename in videosToDownload: for onlineFilename in videosToDownload:
infos = videosInfos[onlineFilename] infos = videosInfos[onlineFilename]
@ -183,6 +275,13 @@ if __name__ == "__main__":
ydl.process_ie_result(infos, True, {}) ydl.process_ie_result(infos, True, {})
markTracked(onlineFilename) markTracked(onlineFilename)
except: except BaseException as e:
print(e)
exit_code = 1
continue continue
sys.exit(exit_code)
if __name__ == "__main__":
main()

View file

@ -1,43 +1,74 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import base64
import colorama
import configargparse
import datetime import datetime
import time
import email.utils import email.utils
import subprocess import io
import pprint import pprint
import subprocess
import sys
def command(command: str) -> None:
cmd = command.encode() + b"\n"
subprocess.run(["xdotool", "type", "--file", "-"], input=cmd)
time.sleep(2)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser( parser = configargparse.ArgParser(
description="Generate SMTP messages to send a mail" description="Generate SMTP messages to send a mail"
) )
now = datetime.datetime.now() now = datetime.datetime.now()
now_email = email.utils.formatdate(now.timestamp(), True) now_email = email.utils.formatdate(now.timestamp(), True)
parser.add_argument("-s", "--sender", default="geoffrey@frogeye.fr") parser.add_argument("-o", "--origin", env_var="ORIGIN", default="localhost")
parser.add_argument("-r", "--receiver", default="geoffrey@frogeye.fr")
parser.add_argument("-l", "--helo", default="frogeye.fr")
parser.add_argument( parser.add_argument(
"-o", "--subject", default=f"Test message {now.strftime('%H:%M:%S')}" "-d", "--destination", env_var="DESTINATION", default="localhost"
)
parser.add_argument("-p", "--port", env_var="PORT", default=25)
parser.add_argument(
"-S", "--security", env_var="SECURITY", choices=["plain", "ssl", "starttls"], default='plain'
) )
parser.add_argument("-m", "--me", default="Geoffrey")
parser.add_argument("-g", "--gtube", action="store_true")
parser.add_argument("-d", "--debug", action="store_true")
parser.add_argument("-b", "--body", default="")
parser.add_argument("-l", "--helo", env_var="HELO")
parser.add_argument(
"-s", "--sender", env_var="SENDER", default="geoffrey@frogeye.fr"
)
parser.add_argument(
"-r", "--receiver", env_var="RECEIVER", default="geoffrey@frogeye.fr"
)
# parser.add_argument("-a", "--auth", env_var="AUTH", default="PLAIN")
parser.add_argument("-u", "--user", env_var="MUSER")
parser.add_argument("-w", "--password", env_var="PASSWORD")
parser.add_argument("-f", "--from", env_var="FROM")
parser.add_argument("-t", "--to", env_var="TO")
parser.add_argument(
"-j",
"--subject",
env_var="SUBJECT",
default=f"Test message {now.strftime('%H:%M:%S')}",
)
parser.add_argument("-b", "--body", env_var="BODY", default="")
parser.add_argument("-g", "--gtube", env_var="GTUBE", action="store_true")
parser.add_argument("-m", "--me", env_var="ME", default="Geoffrey")
parser.add_argument("-y", "--dryrun", env_var="DRYRUN", action="store_true")
parser.add_argument("-q", "--quiet", env_var="QUIET", action="store_true")
args = parser.parse_args() args = parser.parse_args()
if args.debug: # Default values
command = print if args.helo is None:
args.helo = args.origin
if getattr(args, "from") is None:
setattr(args, "from", args.sender)
if args.to is None:
args.to = args.receiver
if args.password:
password = args.password
args.password = '********'
# Transmission content
gtube = "" gtube = ""
if args.gtube: if args.gtube:
@ -49,11 +80,10 @@ XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X"""
if args.body: if args.body:
body = f"\n\n{args.body}" body = f"\n\n{args.body}"
text = f"""Date: {now_email} text = f"""Date: {now_email}
From: {args.sender} From: {getattr(args, 'from')}
Subject: {args.subject} Subject: {args.subject}
To: {args.receiver} To: {args.to}
Hello there, Hello there,
@ -63,28 +93,96 @@ If you didn't expect to see this message, please contact {args.me}.{gtube}{body}
Greetings, Greetings,
Input arguments: Input arguments:
{pprint.pformat(args, indent=4)} {pprint.pformat(args.__dict__, indent=4)}
-- --
{args.me} {args.me}
.""" ."""
if not args.debug: # Transmission setup
for i in range(3, 0, -1): cmd = ["ssh", args.origin]
print(f"Typing mail in {i}…") if args.security == "plain":
time.sleep(1) cmd += ["socat", "-", f"tcp:{args.destination}:{args.port}"]
elif args.security == "ssl":
cmd += ["socat", "-", f"openssl:{args.destination}:{args.port}"]
elif args.security == "starttls":
cmd += [
"openssl",
"s_client",
"-starttls",
"smtp",
"-crlf",
"-connect",
f"{args.destination}:{args.port}",
"-quiet",
]
command(f"EHLO {args.helo}") if not args.quiet:
command(f"MAIL FROM: <{args.sender}>") print(colorama.Fore.MAGENTA + f"# {' '.join(cmd)}" + colorama.Fore.RESET)
command(f"RCPT TO: <{args.receiver}>")
command("DATA")
command(text)
command("QUIT")
print("Done") if not args.dryrun:
p = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
def recv() -> None:
if args.dryrun:
return
assert isinstance(p.stdout, io.BufferedReader)
next = True
while next:
line = p.stdout.readline()
code = int(line[:3])
success = code < 400
color = colorama.Fore.GREEN if success else colorama.Fore.RED
if not args.quiet:
print(color + f"< {line[:-1].decode()}" + colorama.Fore.RESET)
next = line[3] == b"-"[0]
if not next and not success:
send("QUIT") # TODO Can loop if QUIT fails
sys.exit(1)
def send(command: str) -> None:
if not args.quiet:
print(colorama.Fore.BLUE + f"> {command}" + colorama.Fore.RESET)
if args.dryrun:
return
assert isinstance(p.stdin, io.BufferedWriter)
cmd = command.encode() + b"\n"
p.stdin.write(cmd)
p.stdin.flush()
recv()
# Transmission
if args.security != 'starttls':
recv()
send(f"EHLO {args.helo}")
if args.user:
encoded = base64.b64encode(
args.user.encode()
+ b"\x00"
+ args.user.encode()
+ b"\x00"
+ password.encode()
).decode()
send(f"AUTH PLAIN {encoded}")
send(f"MAIL FROM: <{args.sender}>")
send(f"RCPT TO: <{args.receiver}>")
send("DATA")
send(text)
send("QUIT")
sys.exit(0)
# For reference: # For reference:
# command("RSET") # send("RSET")
# command("VRFY") # send("VRFY")
# command("NOOP") # send("NOOP")
# command("QUIT") # send("QUIT")

55
config/scripts/videoQuota Executable file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env python
import os
import sys
import subprocess
import logging
import coloredlogs
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
log = logging.getLogger()
def duration_file(path: str) -> float:
cmd = [
"ffprobe",
"-v",
"error",
"-show_entries",
"format=duration",
"-of",
"default=noprint_wrappers=1:nokey=1",
path,
]
run = subprocess.run(cmd, stdout=subprocess.PIPE, check=True)
ret = run.stdout.decode().strip()
return float(ret)
# Constants
audio_br_bi = 128000
# TODO Arguments if you feel like it
quota_by = int(sys.argv[1])
in_file = sys.argv[2]
out_file = sys.argv[3]
quota_bi = quota_by * 8
duration = duration_file(in_file)
tot_br_bi = quota_bi / duration
video_br_bi = int(tot_br_bi - audio_br_bi)
assert video_br_bi > 0, "Not even enough space for audio"
cmd = [
"ffmpeg",
"-i",
in_file,
"-b:v",
str(video_br_bi),
"-b:a",
str(audio_br_bi),
out_file,
]
subprocess.run(cmd, check=True)

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# change Paris to your default location # change Paris to your default location
request="v2.wttr.in/$1" request="v2.wttr.in/${1-Amsterdam}"
[ "$(tput cols)" -lt 125 ] && request+='?n' [ "$(tput cols)" -lt 125 ] && request+='?n'
curl -H "Accept-Language: ${LANG%_*}" --compressed "$request" curl -H "Accept-Language: ${LANG%_*}" --compressed "$request"

View file

@ -28,6 +28,28 @@ FZF_DEFAULT_OPTS="--height 40% --layout=default"
FZF_CTRL_T_OPTS="--preview '[[ -d {} ]] && ls -l --color=always {} || [[ \$(file --mime {}) =~ binary ]] && file --brief {} || (highlight -O ansi -l {} || coderay {} || rougify {} || cat {}) 2> /dev/null | head -500'" FZF_CTRL_T_OPTS="--preview '[[ -d {} ]] && ls -l --color=always {} || [[ \$(file --mime {}) =~ binary ]] && file --brief {} || (highlight -O ansi -l {} || coderay {} || rougify {} || cat {}) 2> /dev/null | head -500'"
FZF_COMPLETION_OPTS="${FZF_CTRL_T_OPTS}" FZF_COMPLETION_OPTS="${FZF_CTRL_T_OPTS}"
# Colored ls
_colored_ls() {
\ls -lh --color=always $@ | awk '
BEGIN {
FPAT = "([[:space:]]*[^[:space:]]+)";
OFS = "";
}
{
$1 = "\033[36m" $1 "\033[0m";
$2 = "\033[31m" $2 "\033[0m";
$3 = "\033[32m" $3 "\033[0m";
$4 = "\033[32m" $4 "\033[0m";
$5 = "\033[31m" $5 "\033[0m";
$6 = "\033[34m" $6 "\033[0m";
$7 = "\033[34m" $7 "\033[0m";
print
}
'
}
alias ll="_colored_ls"
alias la="_colored_ls -a"
## FUNCTIONS ## FUNCTIONS
## MISC ## MISC

View file

@ -26,8 +26,8 @@ export JAVA_FONTS=/usr/share/fonts/TTF # 2019-04-25 Attempt to remove .java/font
# Get out of my $HOME! # Get out of my $HOME!
export BOOT9_PATH="$HOME/.local/share/citra-emu/sysdata/boot9.bin" export BOOT9_PATH="$HOME/.local/share/citra-emu/sysdata/boot9.bin"
direnv CARGOHOME "$HOME/.cache/cargo" # There are config in there that we can version if one want direnv CARGOHOME "$HOME/.cache/cargo" # There are config in there that we can version if one want
direnv CCACHE_BASEDIR "$HOME/.cache/ccache"
export CCACHE_CONFIGPATH="$HOME/.config/ccache.conf" export CCACHE_CONFIGPATH="$HOME/.config/ccache.conf"
direnv CCACHE_DIR "$HOME/.cache/ccache" # The config file alone seems to be not enough
direnv DASHT_DOCSETS_DIR "$HOME/.cache/dash_docsets" direnv DASHT_DOCSETS_DIR "$HOME/.cache/dash_docsets"
direnv GNUPGHOME "$HOME/.config/gnupg" direnv GNUPGHOME "$HOME/.config/gnupg"
direnv GOPATH "$HOME/.cache/go" direnv GOPATH "$HOME/.cache/go"

View file

@ -1,12 +1,12 @@
""" ALE """ """ ALE """
nmap <F3> :ALEFix<CR> " nmap <F3> :ALEFix<CR>
"
let g:ale_sign_error = '×' " let g:ale_sign_error = '×'
let g:ale_sign_warning = '!' " let g:ale_sign_warning = '!'
let g:ale_completion_enabled = 1 " let g:ale_completion_enabled = 1
let g:ale_fixers = ['autopep8', 'shfmt', 'uncrustify', 'remove_trailing_lines', 'trim_whitespace', 'phpcbf'] " let g:ale_fixers = ['autopep8', 'shfmt', 'uncrustify', 'remove_trailing_lines', 'trim_whitespace', 'phpcbf']
let g:ale_php_phpcs_standard = '/srv/http/machines/ruleset.xml' " let g:ale_php_phpcs_standard = '/srv/http/machines/ruleset.xml'
" For PHP, install https://pear.php.net/package/PHP_CodeSniffer " For PHP, install https://pear.php.net/package/PHP_CodeSniffer
@ -182,3 +182,6 @@ au FileType markdown vmap <Bar> :EasyAlign*<Bar><Enter>
" SmoothScroll " SmoothScroll
noremap <silent> <C-K> :call smooth_scroll#up(20, 5, 1)<CR> noremap <silent> <C-K> :call smooth_scroll#up(20, 5, 1)<CR>
noremap <silent> <C-J> :call smooth_scroll#down(20, 5, 1)<CR> noremap <silent> <C-J> :call smooth_scroll#down(20, 5, 1)<CR>
" gutentags
let g:gutentags_cache_dir = expand('~/.cache/vim/tags')

View file

@ -82,3 +82,6 @@ nmap <C-J> jjjjjjjjjjjjjjjjjjjjj
" \s to replace globally the word under the cursor " \s to replace globally the word under the cursor
nnoremap <Leader>s :%s/\<<C-r><C-w>\>/ nnoremap <Leader>s :%s/\<<C-r><C-w>\>/
" add extensions to syntax
au BufNewFile,BufRead *.jinja set filetype=jinja2