-
Notifications
You must be signed in to change notification settings - Fork 1
/
blog.html
128 lines (109 loc) · 270 KB
/
blog.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<!doctype html>
<html lang="en">
<head>
<title>Blog</title>
<!-- 2024-08-24 Sat 23:56 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Org-mode">
<meta name="author" content="Enrico Benini">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style>body { margin-bottom: 0px; }</style><script>
$(function() {
'use strict';
$('.bs-docs-sidebar li').first().addClass('active');
$(document.body).scrollspy({target: '.bs-docs-sidebar'});
$('.bs-docs-sidebar').affix();
});
</script><link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<link rel="icon" href="images/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet" type="text/css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/blog.css">
</head>
<body>
<div id="content" class="container">
<div class="row"><div class="col-md-12"><h1 class="title">Blog</h1>
<div id="outline-container-navbar" class="outline-2 text-center navbar navbar-inverse navbar-fixed-top">
<h2 id="navbar"><a id="sec-" name="sec-"></a>Blog</h2>
<div class="outline-text-2" id="text-navbar">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#collapsableNavbar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<h1 id="navbarTitle" class="navbar-text">Blog</h1>
<div class="collapse navbar-collapse" id="collapsableNavbar">
<ul class="nav navbar-nav">
<li><a title="Home" href="./index.html"><i class="fas fa-home fa-3x" aria-hidden="true"></i></a></li>
<li><a title="Article List" href="./articleList.html" class="navbar-text h3">Article List</a></li>
<li><a title="Book List" href="./bookList.html" class="navbar-text h3">Book List</a></li>
<li><a title="Album List" href="./albumList.html" class="navbar-text h3">Album List</a></li>
<li><a title="Note Trainer" href="./NoteTrainer/NoteTrainer.html" class="navbar-text h3">Note Trainer</a></li>
</ul>
</div>
</div>
</div>
<div id="outline-container-Articles" class="outline-2">
<h2 id="Articles"><a id="sec-" name="sec-"></a>Articles</h2>
<div class="outline-text-2" id="text-Articles">
<div id='wrap1' data-include="article1"></div>
<div id='wrap2' data-include="article2"></div>
<div id='wrap3' data-include="article3"></div>
<div id='wrap4' data-include="article4"></div>
<div id='wrap5' data-include="article5"></div>
<ul id="pagination" class="pagination pagination-lg"></ul>
<script type="text/javascript">
var htmlArticles = ["<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Extract Data From Youtube with `yt-dlp` And `jq`<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2024-08-24 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>In this article I summarise the steps you need to take in order toextract data from YouTube using the <code>yt-dlp<\/code> and <code>jq<\/code>.<\/p><p>The first one is able to generate a JSON containing all the data ofa given YouTube link. In particular, it can work with YouTubeplaylists and channels too.<\/p><p>The second one is able to query a JSON and return a new JSON withthe data we are interested in.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Motivation<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>All started with the need of automatically fill the <a href=\"https:\/\/github.com\/benkio\/sBots\">sBots<\/a> databasewith any new content coming out from YouTube. For details check <a href=\"https:\/\/github.com\/benkio\/sBots\/issues\/461\">this issue<\/a>.<\/p><p>Therefore, we need a way to extract data easily from the site and tryto get the best result, so we don\'t actually need to parse extremelycomplicated JSON. Fortunately <code>~yt-dlp<\/code> and <code>jq<\/code> comes for the rescue.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Get Data from YouTube<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>After a brief investigation, we can just run the following command:<\/p><p><code>yt-dlp -J <<youtubeLink>><\/code><\/p><p>This works with a YouTube:<\/p><ul class=\"org-ul\"><li>video (<a href=\"2024-09-24-YTDLPJQYoutubeExtraction\/video.json\">JSON<\/a>)<\/li><li>playlist (<a href=\"2024-09-24-YTDLPJQYoutubeExtraction\/barbero.json\">JSON<\/a>)<\/li><li>channel (<a href=\"2024-09-24-YTDLPJQYoutubeExtraction\/youtubo.json\">JSON<\/a>)<\/li><\/ul><p>Yielding different JSONs. However, each JSON is then wrapped insidethe others: the YouTube video JSON structure could be found insidethe playlist JSON one. Same happens between the playlist and thechannel.This makes the next phase easier as some of the etractionlogic could be reused.<\/p><p>The size of such JSON could be quite big accondingly on how big isthe target playlist\/\/channel. In my usecases, I saw a size of:<\/p><ul class=\"org-ul\"><li>38MB for a playlist of 71 videos<\/li><li>80MB for a channel of 182 videos<\/li><\/ul><p>This puts a size of a sigle video <code>0.5MB. ~yt-dlp<\/code> takes quite sometime to put together such information.<\/p><p>In our case it\'s fine since we plan to run it periodically and notthat often. An optimization could be to save the JSONs locally andre-download them only when they become obsolete, eg 1 month old.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Get Automatic Caption Data<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Each <a href=\"2024-09-24-YTDLPJQYoutubeExtraction\/video.json\">YouTube video JSON<\/a> also contains a field related to theautomatic captions where several URLs can be found. Between theavailable formats, json is available to download. This new JSONcontains all the captions allowing us to extract the transcript ofthe video itself. <a href=\"2024-09-24-YTDLPJQYoutubeExtraction\/f.txt\">here<\/a> an example of such JSON. Fun fact, itsextension is TXT 🤷<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Extract Data from the JSON<\/h4><div class=\"outline-text-4\" id=\"text-\"><\/div><div id=\"outline-container-sec-\" class=\"outline-5\"><h5 id=\"sec-\">Single YouTube Video<\/h5><\/div><div id=\"outline-container-sec-\" class=\"outline-5\"><h5 id=\"sec-\">YouTube Playlist<\/h5><\/div><div id=\"outline-container-sec-\" class=\"outline-5\"><h5 id=\"sec-\">YouTube Channel<\/h5><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">References<\/h3><div class=\"outline-text-3\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/github.com\/yt-dlp\/yt-dlp\">yt-dlp repository<\/a><\/li><li><a href=\"https:\/\/jqlang.github.io\/jq\/\">jq site<\/a><\/li><\/ul><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Advent Of Code 2023<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2024-09-23 Mon><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>If you already saw the other Advent of Code pages you know how it goes.This time I finished the 2023. Enjoy the screencast.<\/p><p>You can find all the code in here: <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/tree\/master\/AdventOfCode\">Advent Of CodeRepository<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure> <img src=\".\/2024-09-23-AdventOfCode2023\/AdventOfCodeCompleted.png\" alt=\"Completed Screen of Advent of Code\" align=\"left\" title=\"Advent Of Code Completed\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><br\/><div class=\"embed-responsive embed-responsive-16by9\"> <video controls autoplay loop> <source src=\".\/2024-09-23-AdventOfCode2023\/AdventOfCodeCalendar.mp4\" type=\"video\/mp4\"> <\/video><\/div><p>The Advent of Code Calendar Animation<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Linux (NixOs) External HD Mounting Error<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2023-12-02 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Very quick article to show hot to solve an annoying mounting problem with NTFS and linux (NixOs)<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><p>When you insert an Extenal HD in <code>nautilus<\/code> and you try to access it you might encounter the following error:<\/p><figure> <img src=\".\/2023-12-02-MountErrorWrongFSTypeBadOption\/ErrorMount.png\" alt=\"Nautilus NTFS Mounting Error\" align=\"left\" title=\"Nautilus NTFS Mounting Error\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><p>To solve this:<\/p><ul class=\"org-ul\"><li>Install <code>nftsfix<\/code><\/li><li>Open a terminal<\/li><li>Run the following command:<\/li><\/ul><p><code>sudo ntfsfix -bd \/dev\/sdb1<\/code><\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Advent Of Code 2022<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2023-03-25 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>If you already saw the other Advent of Code pages you know how it goes.This time I finished the 2022. Enjoy the screencast.<\/p><p>You can find all the code in here: <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/tree\/master\/AdventOfCode\">Advent Of CodeRepository<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure> <img src=\".\/2023-03-25-AdventOfCode2022\/AdventOfCodeCompleted.png\" alt=\"Completed Screen of Advent of Code\" align=\"left\" title=\"Advent Of Code Completed\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><br\/><div class=\"embed-responsive embed-responsive-16by9\"> <video controls autoplay loop> <source src=\".\/2023-03-25-AdventOfCode2022\/AdventOfCodeCalendar.mp4\" type=\"video\/mp4\"> <\/video><\/div><p>The Advent of Code Calendar Animation<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Scripting with Scala CLI & Os-lib<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2022-10-08 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>In this article, I will show how I refactored some existing scriptswritten in <a href=\"https:\/\/scala-cli.virtuslab.org\/\">scala-cli<\/a> using the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a>.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Use Cases & Motivations<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><p>Everyone has his own scripts for various tasks and I\'m no exception.At the time of writing I have 2:<\/p><ul class=\"org-ul\"><li>One for converting movies to a format compatible with my mom\'sTV. Movies need to be in Xvid, 720p, with a specific bit rate, mp3audio track, and be not bigger than 1GB (if so it must besplit). I discovered this by trial and error.<\/li><li>One for generating GIFs (muted video chunks with subtitles, to beprecise) starting from YouTube links.<\/li><\/ul><p>For these tasks I rely on <a href=\"https:\/\/scala-cli.virtuslab.org\/\">scala-cli<\/a>, <code>ffmpeg<\/code>, <code>yt-dlp<\/code> and classicUnix commands such as <code>rm<\/code> and <code>mv<\/code>.<\/p><p>Everything works as expected and anyone with a little trace ofintelligence would keep the situation as it is. At least untilsomething goes wrong. However, being a Scala engineer, I heard about\"the lihaoyi ecosystem\" several times and I\'ve never had a chanceto play with it. Plus, the above scripts use the <a href=\"https:\/\/www.scala-lang.org\/api\/2.13.x\/scala\/sys\/process\/index.html\">sys.process._<\/a>package and I\'m not really a super fan of it. So I thought: \"whyjust I don\'t refactor the scripts using the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a>?\". This way I canboth the goals of getting rid of the <a href=\"https:\/\/www.scala-lang.org\/api\/2.13.x\/scala\/sys\/process\/index.html\">sys.process._<\/a> and try somethingfrom lihaoyi.<\/p><p>Moreover, apart from the library <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib#cookbook\">README Cookbook<\/a> and <a href=\"https:\/\/www.lihaoyi.com\/post\/HowtoworkwithFilesinScala.html\">this article<\/a>from Li Haoyi himself, check them out if you want to explore thelibrary, I couldn\'t find a lot of examples of its usage,especially in a \"real\" situation. The library is a porting of<a href=\"https:\/\/docs.python.org\/3\/library\/os.html\">Python\'s Os library<\/a> and there are examples of it around for sure,but no one wants to have to translate from one language to anotherwhen he is looking for examples.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">Insert Script Dependency<\/h3><div class=\"outline-text-3\" id=\"text-\"><p>The first thing you want to do is to add the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a> to your<a href=\"https:\/\/scala-cli.virtuslab.org\/\"> scala-cli<\/a>. At the moment of writing the latest version is <code>0.8.1<\/code>.Just add this snippet at the very top of your script<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">#!\/usr\/bin\/env scala-cli<\/span><span style=\"color: #00ffff;\">using<\/span> lib <span style=\"color: #ffa07a;\">\"com.lihaoyi::os-lib:0.8.1\"<\/span><\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">Movie Conversion Script<\/h3><div class=\"outline-text-3\" id=\"text-\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">File Paths<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Let\'s convert simple file path strings to the types used by thelibrary. Note how I had to specify a temporary existing directoryfor the script to work and store intermediate results. Instead, the<a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\/\">os-lib<\/a> provides a <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib#ostempdir\">temp dir<\/a> operation out of the box.<\/p><p>I can then change the other paths as well to start straight awayfrom the <code>home directory<\/code>. This will make it more flexible since Ican easily move the script from one machine to another, let say aMacbook one to a Linux one, without having to update the paths.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">tempPath<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #ffa07a;\">\"\/Users\/benkio\/temp\"<\/span><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">startingPath<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #ffa07a;\">\"\/Users\/benkio\/temp\/test\"<\/span><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">destinationPath<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #ffa07a;\">\"\/Users\/benkio\/temp\/convertedFilms\"<\/span><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">tempPath<\/span> <span style=\"color: #00ffff;\">=<\/span> os.temp.dir()<span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">startingPath<\/span> <span style=\"color: #00ffff;\">=<\/span> os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/<span style=\"color: #ffa07a;\">\"test\"<\/span><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">destinationPath<\/span> <span style=\"color: #00ffff;\">=<\/span> os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/<span style=\"color: #ffa07a;\">\"convertedFilms\"<\/span><\/pre><\/div><p>Note: on the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib#ospath\">README os.Path section<\/a> you could find that you cancreate a path using just the <code>\'<\/code> prefix. However, if you try youmight get this error:<\/p><pre class=\"example\">[error] .\/convertFilms.sc:18:31: symbol literal \'temp is no longer supported,[error] use a string literal \"temp\" or an application Symbol(\"temp\") instead,[error] or enclose in braces \'{temp} if you want a quoted expression.[error] For now, you can also `import language.deprecated.symbolLiterals` to accept[error] the idiom, but this possibility might no longer be available in the future.[error] val destinationPath = os.home\/\'temp\/\'convertedFilms<\/pre><p>That\'s why I ended up using simple strings.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Script Entry Point<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>In order to understand what this script is supposed to do, we needto have a look at its entry point. This will be affected by theintroduction of the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a> later on, but I introduce it here toprovide more context to understand each step.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">inputFiles<\/span><span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">List<\/span>[(<span style=\"color: #7fffd4;\">String<\/span>, <span style=\"color: #7fffd4;\">String<\/span>)] <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #7fffd4;\">List<\/span>( (<span style=\"color: #ffa07a;\">\"movie.avi\"<\/span>, <span style=\"color: #ffa07a;\">\"movie\"<\/span>), (<span style=\"color: #ffa07a;\">\"film.mkv\"<\/span>, <span style=\"color: #ffa07a;\">\"film\"<\/span>))inputFiles.foreach { <span style=\"color: #ff7f24;\">\/\/<\/span><span style=\"color: #ff7f24;\">1<\/span> <span style=\"color: #00ffff;\">case<\/span> (<span style=\"color: #eedd82;\">inputFile<\/span>, <span style=\"color: #eedd82;\">filename<\/span>) <span style=\"color: #00ffff;\">=><\/span> { ffmpegCommand(inputFile, filename) <span style=\"color: #ff7f24;\">\/\/<\/span><span style=\"color: #ff7f24;\">2<\/span> splitCommand(filename) <span style=\"color: #ff7f24;\">\/\/<\/span><span style=\"color: #ff7f24;\">3<\/span> removeCommand(filename) <span style=\"color: #ff7f24;\">\/\/<\/span><span style=\"color: #ff7f24;\">4<\/span> moveCommand(filename) <span style=\"color: #ff7f24;\">\/\/<\/span><span style=\"color: #ff7f24;\">5<\/span> }}<\/pre><\/div><p>What it does is:<\/p><ol class=\"org-ol\"><li>cycle through each pair in input where the first one is theactual file to be converted and the second one is the outputfilename without extension. We could think about extracting theoutput file name straight from the first value, but often theinput values have non-consistent names, so it\'s better to givethe user a way to specify the output name. For each pair this will:<\/li><li>Run the <code>ffmpeg<\/code> command with the specific input to produce a videocompatible with the target TV: right bitrate, resolution, codec,container, audio codec… This will output a full converted movie.<\/li><li>Split the result of the previous step in chunks of max 1GB.<\/li><li>Remove the output of step 2. We are only interested in the chunksfrom step 3.<\/li><li>Move the chunks for step 3 to the destination path.<\/li><\/ol><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">FFMPEG Command<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Previously we just craft the command as a simple string withinterpolated input and \"run & forget\" it with the <code>!<\/code>command. Instead, the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a> provides <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib#osproccall\">os.call<\/a> that will run acommand <a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/Shellable.html\">Shellable<\/a> process synchronously.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">ffmpegCommand<\/span>(inputFile<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">ffmpegCmd<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"ffmpeg -i \"<\/span><span style=\"color: #eedd82;\">$startingPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$inputFile<\/span><span style=\"color: #ffa07a;\">\" -c:v libxvid -b:v 1500k -c:a libmp3lame -qscale:a 4 -vf scale=720:-1 \"<\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">.avi\"\"\"\"<\/span> ffmpegCmd !}<span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">ffmpegCommand<\/span>(inputFile<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">os<\/span>.<span style=\"color: #7fffd4;\">CommandResult<\/span> <span style=\"color: #00ffff;\">=<\/span> os.proc( <span style=\"color: #ffa07a;\">\"ffmpeg\"<\/span>, <span style=\"color: #ffa07a;\">\"-i\"<\/span>, startingPath \/ inputFile, <span style=\"color: #ffa07a;\">\"-c:v\"<\/span>, <span style=\"color: #ffa07a;\">\"libxvid\"<\/span>, <span style=\"color: #ffa07a;\">\"-b:v\"<\/span>, <span style=\"color: #ffa07a;\">\"1500k\"<\/span>, <span style=\"color: #ffa07a;\">\"-c:a\"<\/span>, <span style=\"color: #ffa07a;\">\"libmp3lame\"<\/span>, <span style=\"color: #ffa07a;\">\"-qscale:a\"<\/span>, <span style=\"color: #ffa07a;\">\"4\"<\/span>, <span style=\"color: #ffa07a;\">\"-vf\"<\/span>, <span style=\"color: #ffa07a;\">\"scale=720:-1\"<\/span>, tempPath \/ s<span style=\"color: #ffa07a;\">\"<\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">.avi\"<\/span> ).call()<\/pre><\/div><p>Another noticeable change here is the fact that we return an<a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/CommandResult.html\">os.CommandResult<\/a> instead of <code>Unit<\/code>. This will be later used to checkthe <code>exitcode<\/code> of the <code>ffmpeg<\/code> process.<\/p><p><a id=\"spawn\" name=\"spawn\"><\/a>One could wonder why I didn\'t go for <a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/proc.html\">os.spawn<\/a> instead of <a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/proc.html\">os.call<\/a> inorder to take advantage of sub-processing and parallelize the wholescript. The issue here is that we don\'t know in advance how manyfiles we will have to convert. In the scenario where there areseveral input files, having the <a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/proc.html\">os.spawn<\/a> will cause tons oflong-live <code>ffmpeg<\/code> sub-processes that will hang the whole system.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Split Command<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>The Split command still uses <code>ffmpeg<\/code> since we want these files tobe playable and <code>ffmpeg<\/code> guarantees to produce a video file. Wecan\'t just split the bytes, you know. The idea here is to split bytime: 1 chunk per hour. Ideally we could run <code>ffmpeg<\/code> to the inputfile, parse its duration and modify the split command accordingly,but it will complicate the script for little value, so we can assumeit will be at most 3 hours. This is an improvement that could bemade eventually.<\/p><p>So, we can build a long command to do it. Again, converting it tothe new library will just be a long long call with quite someparameters.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"> <span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span> <span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">splitCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">splitCmd<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"ffmpeg -v quiet -y -i \"<\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$inputFilename<\/span><span style=\"color: #ffa07a;\">.avi\" -vcodec copy -acodec copy -ss 00:00:00 -t 01:00:00 -sn \"<\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">${inputFilename}<\/span><span style=\"color: #ffa07a;\">_1.avi\" -vcodec copy -acodec copy -ss 01:00:00 -t 01:00:00 -sn \"<\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">${inputFilename}<\/span><span style=\"color: #ffa07a;\">_2.avi\" -vcodec copy -acodec copy -ss 02:00:00 -t 01:00:00 -sn \"<\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">${inputFilename}<\/span><span style=\"color: #ffa07a;\">_3.avi\"\"\"\"<\/span> splitCmd !}<span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">splitCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">os<\/span>.<span style=\"color: #7fffd4;\">CommandResult<\/span> <span style=\"color: #00ffff;\">=<\/span> os.proc( <span style=\"color: #ffa07a;\">\"ffmpeg\"<\/span>, <span style=\"color: #ffa07a;\">\"-v\"<\/span>, <span style=\"color: #ffa07a;\">\"quiet\"<\/span>, <span style=\"color: #ffa07a;\">\"-y\"<\/span>, <span style=\"color: #ffa07a;\">\"-i\"<\/span>, tempPath \/ <span style=\"color: #ffa07a;\">\"$inputFilename.avi\"<\/span>, <span style=\"color: #ffa07a;\">\"-vcodec\"<\/span>, <span style=\"color: #ffa07a;\">\"copy\"<\/span>, <span style=\"color: #ffa07a;\">\"-acodec\"<\/span>, <span style=\"color: #ffa07a;\">\"copy\"<\/span>, <span style=\"color: #ffa07a;\">\"-ss\"<\/span>, <span style=\"color: #ffa07a;\">\"00:00:00\"<\/span>, <span style=\"color: #ffa07a;\">\"-t\"<\/span>, <span style=\"color: #ffa07a;\">\"01:00:00\"<\/span>, <span style=\"color: #ffa07a;\">\"-sn\"<\/span>, tempPath \/ <span style=\"color: #ffa07a;\">\"${inputFilename}_1.avi\"<\/span>, <span style=\"color: #ffa07a;\">\"-vcodec\"<\/span>, <span style=\"color: #ffa07a;\">\"copy\"<\/span>, <span style=\"color: #ffa07a;\">\"-acodec\"<\/span>, <span style=\"color: #ffa07a;\">\"copy\"<\/span>, <span style=\"color: #ffa07a;\">\"-ss\"<\/span>, <span style=\"color: #ffa07a;\">\"01:00:00\"<\/span>, <span style=\"color: #ffa07a;\">\"-t\"<\/span>, <span style=\"color: #ffa07a;\">\"01:00:00\"<\/span>, <span style=\"color: #ffa07a;\">\"-sn\"<\/span>, tempPath \/ <span style=\"color: #ffa07a;\">\"${inputFilename}_2.avi\"<\/span>, <span style=\"color: #ffa07a;\">\"-vcodec\"<\/span>, <span style=\"color: #ffa07a;\">\"copy\"<\/span>, <span style=\"color: #ffa07a;\">\"-acodec\"<\/span>, <span style=\"color: #ffa07a;\">\"copy\"<\/span>, <span style=\"color: #ffa07a;\">\"-ss\"<\/span>, <span style=\"color: #ffa07a;\">\"02:00:00\"<\/span>, <span style=\"color: #ffa07a;\">\"-t\"<\/span>, <span style=\"color: #ffa07a;\">\"01:00:00\"<\/span>, <span style=\"color: #ffa07a;\">\"-sn\"<\/span>, tempPath \/ <span style=\"color: #ffa07a;\">\"${inputFilename}_3.avi\"<\/span> ).call()<\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Remove Command<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>The remove command is straightforward to translate to the newlibrary since it exposes <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib#osremove\">os.remove<\/a> that removes a file if exists.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">removeCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">removeCmd<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"rm \"<\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$inputFilename<\/span><span style=\"color: #ffa07a;\">.avi\"\"\"\"<\/span> removeCmd !}<span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">removeCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.remove(tempPath\/s<span style=\"color: #ffa07a;\">\"<\/span><span style=\"color: #eedd82;\">$inputFilename<\/span><span style=\"color: #ffa07a;\">.avi\"<\/span>)<\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">List Files<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Right now, there is this function in the script:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">getListOfFiles<\/span>(dir<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">File<\/span>, prefix<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">List<\/span>[<span style=\"color: #7fffd4;\">File<\/span>] <span style=\"color: #00ffff;\">=<\/span> {dir.listFiles.filter(<span style=\"color: #00ffff;\">_<\/span>.isFile).toList.filter { file <span style=\"color: #00ffff;\">=><\/span> file.getName.startsWith(prefix) }}<\/pre><\/div><p>Starting from a folder, This will just return a list of files with agiven prefix. Please close an eye on the double <code>filter<\/code> call, Ijust realized it 😅<\/p><p>I can now simply replace this using <code>os.list<\/code> and then filter theresults with the same <code>filter<\/code> used above.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\">os.list(path()).filter(f <span style=\"color: #00ffff;\">=><\/span> os.isFile(f) && <span style=\"color: #ffa07a;\">\"gif[0-9]+.mp4\"<\/span>.r.matches(f.last))<\/pre><\/div><p>I want to underline this error message I got, really really nice!<\/p><pre class=\"example\">value IsFile is not a member of os - did you mean os.isFile?<\/pre><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Move Files<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>We can then move the files listed and filtered in the previoussection with just a simple <code>foreach<\/code> and the help of the<code>os.move.into<\/code>.<\/p><p>Beware the semantic differences between the move operations sincethey could remove everything from the destination before performingthe operation, eg <code>os.move.over<\/code>, or just fail if the destinationdoes not exist, eg <code>os.move<\/code><\/p><p>The result becomes:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">moveCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span><span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">filesToMove<\/span> <span style=\"color: #00ffff;\">=<\/span> getListOfFiles(<span style=\"color: #00ffff;\">new<\/span> <span style=\"color: #98fb98;\">File<\/span>(tempPath), inputFilename) filesToMove.foreach { file <span style=\"color: #00ffff;\">=><\/span> <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">moveCmd<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"mv <\/span><span style=\"color: #eedd82;\">$tempPath<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">${file.getName}<\/span><span style=\"color: #ffa07a;\"> \"<\/span><span style=\"color: #eedd82;\">$destinationPath<\/span><span style=\"color: #ffa07a;\">\/\"\"\"\"<\/span> moveCmd ! }}<span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">moveCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.list(tempPath) .filter(p <span style=\"color: #00ffff;\">=><\/span> os.isFile(p) && p.last.startsWith(inputFilename)) .foreach(f <span style=\"color: #00ffff;\">=><\/span> os.move.into(f, destinationPath))<\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Final Cleanup<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Now we can finally remove the useless imports! We can get rid of:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #00ffff;\">import<\/span> sys.process.<span style=\"color: #00ffff;\">_<\/span><span style=\"color: #00ffff;\">import<\/span> scala.language.postfixOps<span style=\"color: #00ffff;\">import<\/span> java.io.<span style=\"color: #7fffd4;\">File<\/span><\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Parallel Collection<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>We introduced the idea of parallelising the computation in <a href=\"#spawn\">the ffmpegsubsection<\/a> when we talked about <a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/proc.html\">os.spawn<\/a>, saying it\'s not convenientat that point. However, we can do something using <a href=\"https:\/\/github.com\/scala\/scala-parallel-collections\">parallel collections<\/a>.<\/p><p>Since everything starts with a <code>List<\/code>, we can just add the <a href=\"https:\/\/github.com\/scala\/scala-parallel-collections\">parallelcollections<\/a> lib dependency and import, then use the <code>par<\/code> method toconvert it to <code>ParSeq<\/code>. Moreover, we have to limit the amount ofparallelism and we can do it by creating and setting a<code>ForkJoinPoll<\/code> manually as <a href=\"https:\/\/docs.scala-lang.org\/overviews\/parallel-collections\/configuration.html\">this documentation<\/a>.<\/p><p>Finally, we can check the exit code of important commands. Of course,this will not scale and it\'s ugly code: <code>foreach<\/code>, nested ifs,<code>println<\/code>… But we are still doing scripting here! 😛<\/p><p>The final entry point looks like this:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">inputFiles<\/span><span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">ParSeq<\/span>[(<span style=\"color: #7fffd4;\">String<\/span>, <span style=\"color: #7fffd4;\">String<\/span>)] <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #7fffd4;\">List<\/span>( (<span style=\"color: #ffa07a;\">\"movie.avi\"<\/span>, <span style=\"color: #ffa07a;\">\"movie\"<\/span>), (<span style=\"color: #ffa07a;\">\"film.avi\"<\/span>, <span style=\"color: #ffa07a;\">\"film\"<\/span>)).par<span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">forkJoinPool<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">new<\/span> <span style=\"color: #98fb98;\">java<\/span>.util.concurrent.<span style=\"color: #7fffd4;\">ForkJoinPool<\/span>(<span style=\"color: #7fffd4;\">2<\/span>)inputFiles.tasksupport <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">new<\/span> <span style=\"color: #98fb98;\">ForkJoinTaskSupport<\/span>(forkJoinPool)inputFiles.foreach { <span style=\"color: #00ffff;\">case<\/span> (<span style=\"color: #eedd82;\">inputFile<\/span>, <span style=\"color: #eedd82;\">filename<\/span>) <span style=\"color: #00ffff;\">=><\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">ffmpegEC<\/span> <span style=\"color: #00ffff;\">=<\/span> ffmpegCommand(inputFile, filename).exitCode <span style=\"color: #00ffff;\">if<\/span> (ffmpegEC == <span style=\"color: #7fffd4;\">0<\/span>) { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">splitEC<\/span> <span style=\"color: #00ffff;\">=<\/span> splitCommand(filename).exitCode <span style=\"color: #00ffff;\">if<\/span> (splitEC == <span style=\"color: #7fffd4;\">0<\/span>) removeCommand(filename) moveCommand(filename) <span style=\"color: #00ffff;\">else<\/span> println(s<span style=\"color: #ffa07a;\">\"split failed for <\/span><span style=\"color: #eedd82;\">$filename<\/span><span style=\"color: #ffa07a;\">\"<\/span>) } <span style=\"color: #00ffff;\">else<\/span> println(s<span style=\"color: #ffa07a;\">\"ffmpeg command failed for <\/span><span style=\"color: #eedd82;\">$inputFile<\/span><span style=\"color: #ffa07a;\">\"<\/span>) }}<\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Testing<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>I just tried the script after the above changes and I discoveredthat it just hang 😯 🤕<\/p><p>After some <code>println<\/code> and debugging it turned out that the startingpaths defined at the beginning of the files didn\'t quite work asexpected: when I add an additional value with <code>path\/file<\/code> toconstruct the final path and then call <code>toString<\/code> inside a<code>println<\/code> everything is blocked somehow!! 💀<\/p><p>The solution was to convert the above paths to <code>def<\/code> and pass thefinal file as input in other to build the file in place. Plus, I hadto drop the temporary directory in favor of an existing one for thesame reason above.<\/p><p>The final change is:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">tempPath<\/span>(inputFile<span style=\"color: #00ffff;\">:<\/span><span style=\"color: #98fb98;\">String<\/span>) <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">if<\/span> (inputFile.isEmpty) os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span> <span style=\"color: #00ffff;\">else<\/span> os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/inputFile<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">startingPath<\/span>(inputFile<span style=\"color: #00ffff;\">:<\/span><span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">os<\/span>.<span style=\"color: #7fffd4;\">Path<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">if<\/span> (inputFile.isEmpty) os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/<span style=\"color: #ffa07a;\">\"test\"<\/span> <span style=\"color: #00ffff;\">else<\/span> os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/<span style=\"color: #ffa07a;\">\"test\"<\/span>\/inputFile<\/pre><\/div><p>I also had to put the <code>destinationPath<\/code> directly into the <code>move<\/code>function (probably a def would work as well), in this way:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">moveCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.list(tempPath(<span style=\"color: #ffa07a;\">\"\"<\/span>)) .filter(p <span style=\"color: #00ffff;\">=><\/span> os.isFile(p) && p.last.startsWith(inputFilename)) .foreach(os.move.into(<span style=\"color: #00ffff;\">_<\/span>, os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/<span style=\"color: #ffa07a;\">\"convertedFilms\"<\/span>))<\/pre><\/div><p>I tried to use <code>lazy val<\/code>, but it didn\'t help as well.Maybe I did some errors along the way? missing some import? I don\'tknow really. If you have more experience with the library and youknow where the problem is please let me know. You canfind the final code in the <a href=\"#sec-\">References<\/a> section.<\/p><p>Apart from this inconvenience, once it was fixed, the script workedas expected: starting 2 <code>ffmpeg<\/code> processes in parallel, executingthe split, and cleanup 🥳 🎉<\/p><p>Side note: the little price for concurrency is the overlap of theprocesses\' outputs as you might expect in a multi-thread program.<\/p><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">Youtube to GIF Script<\/h3><div class=\"outline-text-3\" id=\"text-\"><p>This script also includes some additional code for converting timemeasures, modeling the input of subtitles, subtitle position,fonts, cutting of the video, spacing between subtitles,and so on. In order to focus only on the library and keep thisarticle short, I\'ll just skip all of that. Please follow the link inthe <a href=\"#sec-\">References<\/a> section if you are interested.<\/p><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">File Paths<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>We have already talked about <a href=\"#sec-\">File Paths<\/a> in the previoussection. Here I just share the changes in this specific case.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">path<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #ffa07a;\">\"\/Users\/benkio\/temp\"<\/span><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">path<\/span>(file<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #ffa07a;\">\"\"<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">os<\/span>.<span style=\"color: #7fffd4;\">Path<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">if<\/span> (file.isEmpty) os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span> <span style=\"color: #00ffff;\">else<\/span> os.home\/<span style=\"color: #ffa07a;\">\"temp\"<\/span>\/file<\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">File System Functions<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>The <i>Youtube to Gif<\/i> script takes advantage of more file systemfunctions compared to the previous script and here is actually where<a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a> shines. In particular, other than moving files, listingfiles, and calling external commands, here we need also to writecontent to files.<\/p><p>Finally, we needed to change the directory and then runthe specified command in order to collect the result in a specificplace. With <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a> we can specify the path directly on the call ofthe process and save an additional step.<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">File System Functions<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">changeDirectory<\/span>(dir<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #7fffd4;\">System<\/span>.setProperty(<span style=\"color: #ffa07a;\">\"user.dir\"<\/span>, dir)<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">moveToTemp<\/span> <span style=\"color: #00ffff;\">=<\/span> changeDirectory(path)<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">writeSubtitles<\/span>(content<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { println(<span style=\"color: #ffa07a;\">\"srt content: \"<\/span> + content) <span style=\"color: #7fffd4;\">Files<\/span>.write(<span style=\"color: #7fffd4;\">Paths<\/span>.get(outputFilename), content.getBytes(<span style=\"color: #7fffd4;\">StandardCharsets<\/span>.<span style=\"color: #7fffd4;\">UTF_8<\/span>))}<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">removeCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span><span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">removeCmd<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"rm \"<\/span><span style=\"color: #eedd82;\">$path<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$inputFilename<\/span><span style=\"color: #ffa07a;\">\"\"\"\"<\/span> println(<span style=\"color: #ffa07a;\">\"removeCmd: \"<\/span> + removeCmd) <span style=\"color: #ffa07a;\">\"sleep 1\"<\/span> #&& removeCmd !}<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">getListOfOutputFiles<\/span>()<span style=\"color: #00ffff;\">:<\/span><span style=\"color: #98fb98;\">List<\/span>[<span style=\"color: #7fffd4;\">File<\/span>] <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">new<\/span> <span style=\"color: #98fb98;\">File<\/span>(path).listFiles.filter(f <span style=\"color: #00ffff;\">=><\/span> f.isFile && f.getName.startsWith(<span style=\"color: #ffa07a;\">\"gif\"<\/span>)).toList<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">renameFile<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">renameCmd<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"mv \"<\/span><span style=\"color: #eedd82;\">$path<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$inputFilename<\/span><span style=\"color: #ffa07a;\">\" \"<\/span><span style=\"color: #eedd82;\">$path<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">\" \"\"\"<\/span> println(<span style=\"color: #ffa07a;\">\"rename: \"<\/span> + renameCmd) renameCmd !}<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">burnSubtitlesInVideo<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, subtitleFilename<span style=\"color: #00ffff;\">:<\/span><span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">ffmpegBurnSubtitlesInVideo<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"ffmpeg -i <\/span><span style=\"color: #eedd82;\">$inputFilename<\/span><span style=\"color: #ffa07a;\"> -vf \"subtitles=<\/span><span style=\"color: #eedd82;\">$subtitleFilename<\/span><span style=\"color: #ffa07a;\">\" <\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">\"\"\"<\/span> println(<span style=\"color: #ffa07a;\">\"ffmpegBurnSubtitlesInVideo: \"<\/span> + ffmpegBurnSubtitlesInVideo) ffmpegBurnSubtitlesInVideo !}<span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">File System Functions<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">writeSubtitles<\/span>(content<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFile<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">os<\/span>.<span style=\"color: #7fffd4;\">Path<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.write(outputFile, content.getBytes(<span style=\"color: #7fffd4;\">StandardCharsets<\/span>.<span style=\"color: #7fffd4;\">UTF_8<\/span>))<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">removeCommand<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.remove(path(inputFilename))<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">getListOfOutputFiles<\/span>()<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">IndexedSeq<\/span>[os.<span style=\"color: #7fffd4;\">Path<\/span>] <span style=\"color: #00ffff;\">=<\/span> os.list(path()).filter(f <span style=\"color: #00ffff;\">=><\/span> os.isFile(f) && <span style=\"color: #ffa07a;\">\"gif[0-9]+.mp4\"<\/span>.r.matches(f.last))<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">renameFile<\/span>(inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.move(path(inputFilename), path(outputFilename))<span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">burnSubtitlesInVideo<\/span>( inputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, subtitleFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> os.proc( <span style=\"color: #ffa07a;\">\"ffmpeg\"<\/span>, <span style=\"color: #ffa07a;\">\"-i\"<\/span>, path(inputFilename), <span style=\"color: #ffa07a;\">\"-vf\"<\/span>, s<span style=\"color: #ffa07a;\">\"subtitles=<\/span><span style=\"color: #eedd82;\">$subtitleFilename<\/span><span style=\"color: #ffa07a;\">\"<\/span>, path(outputFilename) ).call()<\/pre><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Piping Commands<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>In order to download just a specific chunk of the youtube video you needto get the direct stream URL, inject it into a non-trivial <code>ffmpeg<\/code>command with the right timestamps and then run such process. To doso, we can take advantage of the output from the tool <code>yt-dlp<\/code> thatwill return the video stream URL to inject and then run <code>ffmpeg<\/code>.<\/p><p>Before it was done this way:<\/p><div class=\"org-src-container\"><pre class=\"src src-scala\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Before<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">downloadFileToDir<\/span>(youtubeVideo<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">YoutubeVideo<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> { moveToTemp <span style=\"color: #00ffff;\">if<\/span> (!<span style=\"color: #7fffd4;\">File<\/span>(s<span style=\"color: #ffa07a;\">\"<\/span><span style=\"color: #eedd82;\">$path<\/span><span style=\"color: #ffa07a;\">\/<\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">\"<\/span>).exists) { <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">ytDlpCommand<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"yt-dlp -f \"best[ext=mp4]\" -g <\/span><span style=\"color: #eedd82;\">${youtubeVideo.url}<\/span><span style=\"color: #ffa07a;\">\"\"\"<\/span> println(s<span style=\"color: #ffa07a;\">\"ytDlpCommand: <\/span><span style=\"color: #eedd82;\">$ytDlpCommand<\/span><span style=\"color: #ffa07a;\">\"<\/span>) <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">ytDlpCommandOutputUrl<\/span> <span style=\"color: #00ffff;\">=<\/span> (ytDlpCommand !!).lines.findFirst().get <span style=\"color: #00ffff;\">val<\/span> <span style=\"color: #eedd82;\">ffmpegCommand<\/span> <span style=\"color: #00ffff;\">=<\/span> s<span style=\"color: #ffa07a;\">\"\"\"ffmpeg -i \"<\/span><span style=\"color: #eedd82;\">$ytDlpCommandOutputUrl<\/span><span style=\"color: #ffa07a;\">\" -ss <\/span><span style=\"color: #eedd82;\">${secondsToString(youtubeVideo.from.toMillis \/ 1000)}<\/span><span style=\"color: #ffa07a;\"> -to <\/span><span style=\"color: #eedd82;\">${secondsToString(youtubeVideo.to.toMillis \/ 1000)}<\/span><span style=\"color: #ffa07a;\"> -an \"<\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">.mp4\"\"\"\"<\/span> println(<span style=\"color: #ffa07a;\">\"ffmpegCommand: \"<\/span> + ffmpegCommand) ffmpegCommand ! }}<span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">After<\/span><span style=\"color: #00ffff;\">def<\/span> <span style=\"color: #87cefa;\">downloadFileToDir<\/span>( youtubeVideo<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">YoutubeVideo<\/span>, outputFilename<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">String<\/span>)<span style=\"color: #00ffff;\">:<\/span> <span style=\"color: #98fb98;\">Unit<\/span> <span style=\"color: #00ffff;\">=<\/span> <span style=\"color: #00ffff;\">if<\/span> (!os.exists(path(outputFilename))) os.proc(<span style=\"color: #ffa07a;\">\"yt-dlp\"<\/span>, <span style=\"color: #ffa07a;\">\"-f\"<\/span>, <span style=\"color: #ffa07a;\">\"best[ext=mp4]\"<\/span>, <span style=\"color: #ffa07a;\">\"-g\"<\/span>, youtubeVideo.url) .call(cwd <span style=\"color: #00ffff;\">=<\/span> path()) .out .lines() .headOption .map(ytDlpCommandOutputUrl <span style=\"color: #00ffff;\">=><\/span> os.proc( <span style=\"color: #ffa07a;\">\"ffmpeg\"<\/span>, <span style=\"color: #ffa07a;\">\"-i\"<\/span>, s<span style=\"color: #ffa07a;\">\"<\/span><span style=\"color: #eedd82;\">$ytDlpCommandOutputUrl<\/span><span style=\"color: #ffa07a;\">\"<\/span>, <span style=\"color: #ffa07a;\">\"-ss\"<\/span>, secondsToString(youtubeVideo.from.toMillis \/ <span style=\"color: #7fffd4;\">1000<\/span>), <span style=\"color: #ffa07a;\">\"-to\"<\/span>, secondsToString(youtubeVideo.to.toMillis \/ <span style=\"color: #7fffd4;\">1000<\/span>), <span style=\"color: #ffa07a;\">\"-an\"<\/span>, path(s<span style=\"color: #ffa07a;\">\"<\/span><span style=\"color: #eedd82;\">$outputFilename<\/span><span style=\"color: #ffa07a;\">.mp4\"<\/span>) ).call(cwd <span style=\"color: #00ffff;\">=<\/span> path()) ) .getOrElse(())<\/pre><\/div><p>If you look at the documentation of the library, they show a verynice way to pipe commands into one another. Here I just preferred tocall them one after the other and pass the result manually since I\'mquite new to this library and the commands are quite long bythemselves. Probably there\'s a better\/more elegant way to achievethis, if so, just reach out to me and let me know. I\'ll be happy togive it a try.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Testing<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>This time the testing phase didn\'t bring any unexpected surprises. Ijust had to fix a couple of imprecisions in the way the commandswere translated to the new library. After that everything worked asexpected.<\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>As you can see by yourself, the <a href=\"https:\/\/github.com\/com-lihaoyi\/os-lib\">os-lib<\/a> works and improves theinteraction between your scala code and the underlying file system &processes as promised: adding more type safety to things like<code>paths<\/code> and abstracting from the reference OS. You don\'t have torely on specific platform command syntax and everything becomes morecross-platform. It will definitely be my go-to lib for scriptingfrom now on.<\/p><p>The only downside I see is the fact that you lose some control overwhat is actually running, as you don\'t have full directvisibility on the command. I wasn\'t able to find how to get a simplecommand string from the <a href=\"https:\/\/javadoc.io\/doc\/com.lihaoyi\/os-lib_3\/latest\/api\/os\/Shellable.html\">Shellable<\/a> even if it should be quite simple,something like <code>shellable.value.mkString(\" \")<\/code>, but I would like tohave that exposed by the library. A Minor thing by the way.<\/p><img src=\"https:\/\/www.dropbox.com\/s\/und6zl8mlvyk9i0\/ytai_Buonanotte.mp4?dl=1\" alt=\"Buonanotte by Youtubo\"><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">References<\/h3><div class=\"outline-text-3\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/gist.github.com\/benkio\/6fac65fdaec44842712be716f46169e1\">convertFilms.sc script<\/a><\/li><li><a href=\"https:\/\/gist.github.com\/benkio\/103960b7b5a5781c222df1c4e31544a2\">youtubeToGif.sc script<\/a><\/li><\/ul><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Scala Example: TestContainers and MUnit<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2022-07-16 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Simple example of an Integration test using:<\/p><ul class=\"org-ul\"><li><a href=\"https:\/\/github.com\/testcontainers\/testcontainers-scala\">testcontainers-scala<\/a><\/li><li><code>docker-compose<\/code> with database and flyway<\/li><li><a href=\"https:\/\/github.com\/scalameta\/munit\">MUnit<\/a> testing framework<\/li><\/ul><p>Beware the fact that with <a href=\"https:\/\/github.com\/scalameta\/munit\">MUnit<\/a> the names of the traits might change compared to <a href=\"https:\/\/www.scalatest.org\/\">Scalatest<\/a>.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Dependencies<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li>Add dependencies to the project and submodules<\/li><li>Add the <code>it<\/code> scope to the libraries<script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2Fb2fb9d26cb9b9dbc9660374ed90a805e02f5df3e%2Fproject%2FDependencies.scala%23L19&style=github&showBorder=on\"><\/script><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2Fb2fb9d26cb9b9dbc9660374ed90a805e02f5df3e%2Fproject%2FDependencies.scala%23L35&style=github&showBorder=on&showCopy=on=\"><\/script><\/li><li>Add <code>IntegrationTest<\/code> configuration extending <code>Test<\/code> configuration<script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2Fb2fb9d26cb9b9dbc9660374ed90a805e02f5df3e%2Fbuild.sbt%23L24&style=github&showBorder=on&showCopy=on\"><\/script><\/li><li>Add <code>Default.itSettings<\/code> to your project and submodules settings<script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2Fb2fb9d26cb9b9dbc9660374ed90a805e02f5df3e%2Fproject%2FSettings.scala%23L75&style=github&showBorder=on&showCopy=on\"><\/script><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Case 1: Single Test Container<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Most simple case, where you declare a single container in your testto use. In the following example a <code>PostgreSQLContainer<\/code> is shown.<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2F7d13e9470231d27027efc3183cb89e563fe5ccd5%2FbotDB%2Fsrc%2Fit%2Fscala%2Fcom%2Fbenkio%2FbotDB%2FITSpec.scala&style=github&showBorder=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Case 2: Docker-Compose<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Here there\'s <code>flyway<\/code> as well to run the migrations. Not necessary for this example.<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2Fb2fb9d26cb9b9dbc9660374ed90a805e02f5df3e%2FbotDB%2Fdocker-compose.yml&style=github&showBorder=on&showCopy=on\"><\/script><\/div><div id=\"outline-container-sec-\" class=\"outline-5\"><h5 id=\"sec-\">Scala Code<\/h5><div class=\"outline-text-5\" id=\"text-\"><\/div><ul class=\"org-ul\"><li><a id=\"sec-\" name=\"sec-\"><\/a>DockerComposeContainer Definition<br ><div class=\"outline-text-6\" id=\"text-\"><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2F5177bf67616c1d68ac4c9cddd151e5e28461c205%2FbotDB%2Fsrc%2Fit%2Fscala%2Fcom%2Fbenkio%2FbotDB%2FContainerSuite.scala&style=github&showBorder=on&showCopy=on\"><\/script><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>Simple MUnit Spec file<br ><div class=\"outline-text-6\" id=\"text-\"><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FmyTelegramBot%2Fblob%2Fb2fb9d26cb9b9dbc9660374ed90a805e02f5df3e%2FbotDB%2Fsrc%2Fit%2Fscala%2Fcom%2Fbenkio%2FbotDB%2FComposeSpec.scala&style=github&showBorder=on&showCopy=on\"><\/script><\/div><\/li><\/ul><\/div><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Advent Of Code 2021<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2022-01-12 Wed><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>You can find all the code in here: <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/tree\/master\/AdventOfCode\">Advent Of Code Repository<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure> <img src=\".\/2022-01-12-AdventOfCode2021\/AdventOfCodeCompleted.png\" alt=\"Completed Screen of Advent of Code\" align=\"left\" title=\"Advent Of Code Completed\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Instructions Audiobook \"The God Themselves\"<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-12-03 Fri><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Requirements<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li>Install <a href=\"http:\/\/www.ffmpeg.org\/\">ffmpeg<\/a><\/li><li>Install <a href=\"https:\/\/www.gnu.org\/software\/wget\/\">wget<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Instructions<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li>Place yourself in an empty folder<\/li><li>Run the following commands to download the book chapters:<\/li><\/ul><div class=\"org-src-container\"><pre class=\"src src-shell\">wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 01.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2001.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 02.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2002.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 03.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2003.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 04.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2004.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 05.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2005.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 06.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2006.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 07.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2007.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 08.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2008.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 09.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2009.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 10.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2010.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 11.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2011.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 12.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2012.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 13.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2013.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 14.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2014.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 15.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2015.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 16.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2016.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 17.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2017.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 18.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2018.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 19.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2019.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 20.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2020.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 21.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2021.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 22.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2022.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 23.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2023.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 24.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2024.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 25.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2025.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 26.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2026.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 27.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2027.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 28.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2028.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 29.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2029.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 30.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2030.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 31.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2031.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 32.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2032.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 33.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2033.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 34.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2034.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 35.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2035.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 36.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2036.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 37.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2037.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 38.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2038.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 39.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2039.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 40.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2040.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 41.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2041.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 42.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2042.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 43.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2043.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 44.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2044.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 45.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2045.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 46.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2046.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 47.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2047.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 48.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2048.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 49.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2049.mp3wget -O <span style=\"color: #ffa07a;\">\"Gods Themselves 50.mp3\"<\/span> https:\/\/archive.org\/download\/IsaacAsimovAudioBookCollection\/1972%20-%20The%20Gods%20Themselves%20%28Brick%29%2064k%2011.26.20%20%7B330mb%7D\/Gods%20Themselves%2050.mp3<\/pre><\/div><ul class=\"org-ul\"><li>Create a file with the following content, call it <code>chapters.txt<\/code><\/li><\/ul><div class=\"org-src-container\"><pre class=\"src src-shell\">file <span style=\"color: #ffa07a;\">\'Gods Themselves 01.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 02.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 03.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 04.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 05.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 06.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 07.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 08.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 09.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 10.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 11.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 12.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 13.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 14.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 15.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 16.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 17.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 18.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 19.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 20.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 21.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 22.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 23.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 24.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 25.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 26.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 27.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 28.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 29.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 30.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 31.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 32.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 33.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 34.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 35.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 36.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 37.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 38.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 39.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 40.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 41.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 42.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 43.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 44.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 45.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 46.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 47.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 48.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 49.mp3\'<\/span>file <span style=\"color: #ffa07a;\">\'Gods Themselves 50.mp3\'<\/span><\/pre><\/div><ul class=\"org-ul\"><li>Run the following command <code>ffmpeg -f concat -safe 0 -i chapters.txt -c copy \"The God Themselves.mp3\"<\/code><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Reference<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/trac.ffmpeg.org\/wiki\/Concatenate\">https:\/\/trac.ffmpeg.org\/wiki\/Concatenate<\/a><\/li><\/ul><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-\"><p>Now you should have a file of 11h26 minutes you can listen to. Youcan delete the single chapters as well as the <code>chapters.txt<\/code> file.<\/p><p>Enjoy 😃<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>[ITA] - Music Talk Show 4<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-06-02 Wed><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Quarto appuntamento per quanto riguarda il discorso di Teoriamusicale dopo un bel po\'.<\/p><p>Il tutto viene fatto live su Twitch. Di seguito gli argomentitrattati:<\/p><ul class=\"org-ul\"><li>Link interessanti del mese 0:00:33<\/li><li>Armonizzazione Scala Maggiore 0:07:49<\/li><li>Progressione Armonica, Tensione-Risoluzione & Cadenze 0:38:00 <\/li><li>Interscambio modale <\/li><li>Dominanti Secondarie<\/li><\/ul><p><a href=\".\/2021-06-02-MusicTalkShow3.html\"><< Previous Episode<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Talk Links<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Link Interessanti<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/youtu.be\/De97zQi5rzc\">Music Theory Masterclass 1: Drilling the Basics<\/a> by Rick Beato<\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=GF9n9unaBXk\">Music Theory Masterclass 2: Chord Progressions and Harmony<\/a> by Rick Beato<\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=kSux13Yy4pM\">As Much GUITAR THEORY As I Can Teach In 1 Hour<\/a> by Rick Beato<\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=lxnfkcilKtg\">Conosci DAVVERO le DIFFERENZE tra i PICKUP? 🎸<\/a> by Shape Your Tone<\/li><li><a href=\"https:\/\/youtu.be\/XwmOlpbfpsM\">Tritone Vs 5° Diminuito Vs 4° Aumentato | Teoria musicale Q+A<\/a> by Music Theory For Guitar<\/li><li><a href=\"https:\/\/youtu.be\/bwaeBUYcO5o\">Demonstrating All 7 Modes in Parallel [MODAL MUSIC THEORY]<\/a> by Signals Music Studio<\/li><\/ul><\/div><div id=\"outline-container-sec-\" class=\"outline-5\"><h5 id=\"sec-\">Extra<\/h5><div class=\"outline-text-5\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/youtu.be\/qk8uobP4uJE\">Demonstrating The Modes of Harmonic Minor [MUSIC THEORY \/ SCALES]<\/a> by Signals Music Studio<\/li><\/ul><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Armonizzazione Scala Maggiore<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/www.musicoff.com\/strumenti\/teoria\/armonizzazione-della-scala-maggiore\/\">Armonizzazione Scala Maggiore<\/a> by MusicOff<\/li><li><a href=\"https:\/\/antoniopisacane.com\/armonizzazione-della-scala-maggiore\/\">Armonizzazione Scala Maggiore<\/a> by Antonio Pisacane<\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Progressione Armonica, Tensione Risoluzione & Cadenze<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Progressione_armonica\">Progressione Armonica<\/a> by Wikipedia<\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Cadenza\">Cadenze<\/a> by Wikipedia<\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Interscambio Modale<\/h4><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Dominanti Secondarie<\/h4><\/div><\/div><div id=\"outline-container-ArticleVideo\" class=\"outline-3\"><h3 id=\"ArticleVideo\"><a id=\"sec-\" name=\"sec-\"><\/a>Video<\/h3><div class=\"outline-text-3\" id=\"text-ArticleVideo\"><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/QJXpvuhG2U8?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Music Talk Show #4 p1<\/figcaption><\/figure><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>[ITA] - Music Talk Show 3<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-06-02 Wed><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Terzo appuntamento per quanto riguarda il discorso di Teoriamusicale.<\/p><p>Il tutto viene fatto live su Twitch. Di seguito gli argomentitrattati:<\/p><ul class=\"org-ul\"><li>Recap Show precedente 00:07:00<\/li><li>I Modi della Scala Maggiore 00:19:00<\/li><li>Gli Accordi 1:15:00<\/li><\/ul><p><a href=\".\/2021-05-20-MusicTalkShow2.html\"><< Previous Episode<\/a> <a href=\".\/2021-07-04-MusicTalkShow4.html\">>> Next Episode<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Talk Links<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Recap<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/youtu.be\/3AYKQyALBIM?t=141\">Major Second - Justin Guitar<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=oIDl_CvIn8A\">Synphony of Destruction - Rick Beato<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Modi della Scala Maggiore<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Modo_(musica)\">Modo - Wikipedia<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Accordi<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Rivolto\">Rivolto<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Accordo_(musica)\">Accordo<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Triade_(musica)\">Triade<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Accordo_di_settima\">Accordo di Settima<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=NF-kLy44Hls\">Lucio save the dance - Draft Punk<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Extras<\/h4><div class=\"outline-text-4\" id=\"text-\"><img src=\"2021-06-02-MusicTalkShow3\/diatonicModes.jpg\" style=\"width:50%; max-height: 300px; margin-bottom: 1em;\"><\/img><img src=\"2021-06-02-MusicTalkShow3\/triads.jpg\" style=\"width:50%; max-height: 300px; margin-bottom: 1em;\"><\/img><\/div><\/div><\/div><div id=\"outline-container-ArticleVideo\" class=\"outline-3\"><h3 id=\"ArticleVideo\"><a id=\"sec-\" name=\"sec-\"><\/a>Video<\/h3><div class=\"outline-text-3\" id=\"text-ArticleVideo\"><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/LYR8tklEaVQ?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Music Talk Show #3<\/figcaption><\/figure><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>[ITA] - Music Talk Show 2<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-05-13 Thu><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-4\"><h4 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h4><div class=\"outline-text-4\" id=\"text-ArticleAbstract\"><p>Secondo appuntamento per quanto riguarda il discorso di Teoriamusicale.<\/p><p>Il tutto viene fatto live su Twitch. Di seguito gli argomentitrattati:<\/p><ul class=\"org-ul\"><li>Come funzionano i Pick up della chitarra a livello fisico? 00:02:00<\/li><li>Recap Show precedente 0:25:25<\/li><li>Intervalli & Scala Cromatica 0:35:17<\/li><li>Scala Maggiore 1:15:14<\/li><\/ul><p><a href=\".\/2021-05-13-MusicTalkShow1.html\"><< Previous Episode<\/a> - <a href=\".\/2021-06-02-MusicTalkShow3.html\">Next Episode >><\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Talk Links<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Pick-up chitarra<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Risonanza_acustica\">Risonanza Acustica<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Pick-up_(elettronica)\">Pick-up<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/P-90\">P-90<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Silent_guitar\">Silent Guitar<\/a><\/li><li><a href=\"https:\/\/www.trechitarre.com\/come-funzionano-pickup-chitarra\/\">Come funzionano i Pick-up<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Recap Show Precedente<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/www.youtube.com\/watch?v=OATjHiOuc70&pp=qAMBugMGCgJpdBAB\">Intro To The Harmonic Series - TWO MINUTE MUSIC THEORY #31<\/a><\/li><li><a href=\"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/3\/3f\/Music_intervals_frequency_ratio_equal_tempered_pythagorean_comparison.svg\/2880px-Music_intervals_frequency_ratio_equal_tempered_pythagorean_comparison.svg.png\">Approssimazione degli Intervalli<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Musical_note#Note_names_and_their_history\">Storia delle Note<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Intervalli e Scala Cromatica<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Interval_(music)\">Intervalli<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Tritone\">Tritono<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Scala Maggiore<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Major_scale\">Scala Maggiore<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Scale_(music)\">Scala Musicale<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Diatonic_scale#Tuning\">Scala Diatonica<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Heptatonic_scale\">Scala Heptatonica<\/a><\/li><\/ul><\/div><\/div><\/div><div id=\"outline-container-ArticleVideo\" class=\"outline-3\"><h3 id=\"ArticleVideo\"><a id=\"sec-\" name=\"sec-\"><\/a>Video<\/h3><div class=\"outline-text-3\" id=\"text-ArticleVideo\"><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/suijWXQvrjE?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Music Talk Show #2<\/figcaption><\/figure><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>[ITA] - Music Talk Show 1<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-05-13 Thu><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Prima live riguardante i fondamenti della musica o più semplicementesi esplorano concetti di musica, cencando di imparare qualcosa nelprocesso. Si possono recuperare i concetti andando a rivedere lalive con i dovuti timestamps. In ogni sezione, collezionerò i varilink trattati nella live, eg. wikipedia.<\/p><p>Il tutto viene fatto live su Twitch. Di seguito gli argomentitrattati:<\/p><ul class=\"org-ul\"><li>Introduzione 0:07:00<\/li><li>Il Suono 0:10:57<\/li><li>Teoria Musicale 0:36:59<\/li><li>Note: A440, sistema temperato 0:56:36<\/li><li>Intervalli Parte 1 1:25:05<\/li><\/ul><p><a href=\".\/2021-05-20-MusicTalkShow2.html\">Next Episode >><\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Talk Links<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-ArticleContentIntroduction\" class=\"outline-4\"><h4 id=\"ArticleContentIntroduction\"><a id=\"sec-\" name=\"sec-\"><\/a>Introduzione<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentIntroduction\"><\/div><\/div><div id=\"outline-container-ArticleContentSound\" class=\"outline-4\"><h4 id=\"ArticleContentSound\"><a id=\"sec-\" name=\"sec-\"><\/a>Suono<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentSound\"><ul class=\"org-ul\"><li><a href=\"https:\/\/www.youtube.com\/watch?v=TnsCsR2wDdk\">What’s that ringing in your ears? - Marc Fagelson<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Suono\">Suono - Wikipedia<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Volume_(acustica)\">Volume - Wikipedia<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/Frequenza\">Frequenza - Wikipedia<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-ArticleContentMusicTheory\" class=\"outline-4\"><h4 id=\"ArticleContentMusicTheory\"><a id=\"sec-\" name=\"sec-\"><\/a>Teoria Musicale<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentMusicTheory\"><p><a href=\"https:\/\/it.wikipedia.org\/wiki\/Teoria_musicale\">Teoria Musicale - Wikipedia<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContentNote\" class=\"outline-4\"><h4 id=\"ArticleContentNote\"><a id=\"sec-\" name=\"sec-\"><\/a>Note<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentNote\"><ul class=\"org-ul\"><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/A440_(pitch_standard)\">Pitch Standard - Wikipedia<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Elvis_Presley#Discography\">Elvis Presley - Discografia - Wikipedia<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Second\">Second - Wikipedia<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=MJumWa_K-8k\">The History of Musical Pitch - Music History<\/a><\/li><li><a href=\"https:\/\/youtu.be\/1Hqm0dYKUx4\">Why It\'s Impossible to Tune a Piano<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Pitch_(music)\">Pitch - Wikipedia<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Octave\">Octave - Wikipedia<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Harmonic_series_(music)\">Harmonic Series - Wikipedia<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-ArticleContentIntervals\" class=\"outline-4\"><h4 id=\"ArticleContentIntervals\"><a id=\"sec-\" name=\"sec-\"><\/a>Intervalli Parte 1<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentIntervals\"><ul class=\"org-ul\"><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Fundamental_frequency\">Fundamental Frequency - Wikipedia<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Just_intonation\">Just Intonation - Wikipedia<\/a><\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Equal_temperament\">Equal Temperament - Wikipedia<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=RuT6Y53LYH4\">The Broken Scales Of Wendy Carlos<\/a><\/li><li><a href=\"https:\/\/www.dailymotion.com\/video\/x2j1gy2\">Wendy Carlos – Beauty In The Beast<\/a><\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Extras<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/youtu.be\/INW7IORhqT0\">The Piano That (almost) Changed Everything<\/a><\/li><li><a href=\"https:\/\/youtu.be\/c6UCeZptMpA\">Perchè le note musicali sono 7<\/a><\/li><\/ul><\/div><\/div><\/div><div id=\"outline-container-ArticleVideo\" class=\"outline-3\"><h3 id=\"ArticleVideo\"><a id=\"sec-\" name=\"sec-\"><\/a>Video<\/h3><div class=\"outline-text-3\" id=\"text-ArticleVideo\"><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/k4TKS5jY2XQ?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Music Talk Show #1<\/figcaption><\/figure><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Advent Of Code 2016<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-03-25 Thu><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>If you already saw the other Advent of Code pages you know how it goes.This time I finished the 2016. Enjoy the screencast.<\/p><p>You can find all the code in here: <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/tree\/master\/AdventOfCode\">Advent Of CodeRepository<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure> <img src=\".\/2021-03-25-AdventOfCode2016\/AdventOfCodeCompleted.png\" alt=\"Completed Screen of Advent of Code\" align=\"left\" title=\"Advent Of Code Completed\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><br\/><div class=\"embed-responsive embed-responsive-16by9\"> <video controls autoplay loop> <source src=\".\/2021-03-25-AdventOfCode2016\/AdventOfCodeCalendar.mp4\" type=\"video\/mp4\"> <\/video><\/div><p>The Advent of Code Calendar Animation<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Away from LastPass - Password Manager Migration<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-02-19 Fri><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>As many people out there I manager my password using <a href=\"https:\/\/www.lastpass.com\/\">LastPass<\/a>. Itis a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Password_manager\">password manager<\/a> who stores your passwords in the cloudallowing you to just have to remember one password for all. It\'snot the only solution out there with this purpose, but it\'sdefinitely the most famous. I was an happy user, until Iread recently this news: <a href=\"https:\/\/9to5google.com\/2021\/02\/16\/lastpass-free-device-type-restriction-march-2021\/\">LastPass will restrict free users to onlyone type of device starting next month<\/a><\/p><p>In particular you can read:<\/p><blockquote><p>Starting on March 16, 2021, LastPass will start restricting its free service to only one device type, meaning those who sign up will be required to pick between accessing the service on their computer or their smartphone. The latter also includes LastPass access on tablets (IPads) and smartwatches. You’ll pick an “active device type” to decide on where you’ll use the service.<\/p><\/blockquote><p>You can see how this is a huge problem. Therefore, a lot of peopleare migrating to a difference service, a little bit like the peopleare moving away recently from Whatsapp due to the privacy policychanges or from Google Photos due to the new limitations (luckily Ifound out I still have enough space there for some years)<\/p><p>In this article, I will not go and do a comparison of the differentservices out there (just a brief mention to motivate my choice),but I will show you how I moved out from Lastpass to…happyreading 😃<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><p>In the following sections, I will focus more on the choices I madeand the motivations at the base of those choices. I think there areway better articles explaining the steps to take in order to movefrom one service to another.<\/p><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Getting Data<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>The first thing is fetching your data from LastPass. Looking onthe web you can find that LastPass offers you to export all yourpasswords\/logins\/secure notes to a <code>CSV file<\/code>. You can do it fromthe browser extension or from the site, I choose the first option<\/p><figure><p><img src=\".\/2021-02-19-PasswordManagerMigration\/LastPassExtensionExport.png\" class=\"img-responsive\" alt=\"LastPassExtensionExport.png\"><\/p><\/figure><p>you will get a CSV file with the following headers:<\/p><pre class=\"example\">url,username,password,extra,name,grouping,fav<\/pre><p>Regarding the secure notes, they are encoded into the name field,with an empty URL, username, and password. I will take thechance to clean up the file from the useless data before importingit somewhere else. I just operate directly on the CSV, if you arenot used to this format, I suggest you just log in to LastPass andcheck your data there before exporting everything.<\/p><p><b>Edit:<\/b> in the end, I spent a day to reorganise all the data intofolders, but that was because I never kept everythingin order. So, the blame is on me, not on the services.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Alternatives<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>There are a lot of good alternatives out there, so you need toidentify the right features you are looking for and then go on theweb and search for the right candidates.<\/p><p><b>For me,<\/b> the features I\'m looking for, sorted by importance, are:<\/p><dl class=\"org-dl\"><dt> Free & Open Source <\/dt><dd>I don\'t want to do the migration andthen end up having the same problem in a year time or so.<\/dd><dt> Integration & Comfort <\/dt><dd>If I have a service, but it\'s hard toaccess it, setup, or not easy to use, it\'s almost the same ashaving no service.<\/dd><dt> Easy Migration <\/dt><dd>If possible, I would like to have a way tojust upload a CSV and be good to go.<\/dd><\/dl><p>Looking online, simply on <a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_password_managers\">Wikipedia<\/a> or online on Twitter, articles,etc, I filtered out and shrieked the selection to just 4 alternatives:<\/p><dl class=\"org-dl\"><dt> <a href=\"https:\/\/www.mozilla.org\/en-US\/firefox\/lockwise\/\">Firefox Lockwise<\/a> (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Firefox_Lockwise\">DEPRECATED<\/a>) <\/dt><dd>This is free (Mozilla) and integrated andsynchronise automatically from the Firefox account. If you live withFirefox this is a great option, but looking for a Chromeextension, I found this comment from the related <a href=\"https:\/\/github.com\/mozilla-lockwise\/lockwise-addon\/issues\/350\">Github issue<\/a><\/dd><\/dl><figure><p><img src=\".\/2021-02-19-PasswordManagerMigration\/FirefoxLockwiseChromeExtension.png\" class=\"img-responsive\" alt=\"FirefoxLockwiseChromeExtension.png\"><\/p><\/figure><p>You won\'t have a standalone desktop application and Android applicationdoesn\'t have a very great reputation, especially looking at themost recent reviews. It has a <a href=\"https:\/\/support.mozilla.org\/en-US\/kb\/import-login-data-file\">CSV import option<\/a>, but you have towork the Last pass CSV in order to fulfil the requested fields:<\/p><pre class=\"example\">\"url\",\"username\",\"password\",\"httpRealm\",\"formActionOrigin\",\"guid\",\"timeCreated\",\"timeLastUsed\",\"timePasswordChanged\"<\/pre><p>Basically, it\'s not different from what other browsers offer, likeChrome who use the Google cloud to synchronise your passwords, orOpera for instance.<\/p><dl class=\"org-dl\"><dt> <a href=\"https:\/\/bitwarden.com\/\">Bitwarden<\/a> <\/dt><dd>This is, by far, the most pushed alternatives bythe media online and I can see why:<ul class=\"org-ul\"><li>It is open source<\/li><li>It has:<ul class=\"org-ul\"><li>all browser\'s extensions<\/li><li>CLI and standalone application for all the operating systems<\/li><li>mobile applications.<\/li><li>integration from all the other main password managers outthere, LastPass included.<\/li><\/ul><\/li><\/ul><\/dd><\/dl><p>The only Cons is that it has a Free account and then <b>pricingtiers<\/b>, limiting the amount of users you can have or other featureslike sharing data or SSO, etc. It\'s true, if you are just thetypical user you probably don\'t care about those Business featuresat all, but nobody knows if, in the future, they might do a movelike LastPass.(even if it would be a stupid one since we arewitnessing this migration right now)<\/p><dl class=\"org-dl\"><dt> <a href=\"https:\/\/lesspass.com\/\">LessPass<\/a> <\/dt><dd>This is also a valid open-source alternative. Italso has browser extensions, mobile, and CLIapplications. Plus, it generates your password byjust apply a function to your master password:basically, the password came from your masterpassword, the site, and some encryptionalgorithm. You don\'t have to synchronise anything. This isvery appealing for me since I like so muchfunctional programming. The cons in here areregarding its low popularity, so: you get lessupdate for the application itself, just go on theirGithub and look at the commit frequency, and theimport from other services is not straight forward<\/dd><dt> <a href=\"https:\/\/lesspass.com\/#\/\">pass<\/a> <\/dt><dd>This is the standard Unix tool to manage passwords andit is born as a local tool. However, the nerds are everywhere,so there are tons of third-party tools that are based onpass. Because of it, we get all the extensions and mobileapplications. The question is, how do you get thesynchronisation? It happens using git and Github. Thedownside is that you need to remember to push (the commithappens every time you modify the password store) yourencrypted passwords. Finally, how do you import your datafrom other services? Well, as I said, this is the nerd tool,so you find the scripts parsing the different CSVs and importthem in pass. The one we might need is <a href=\"https:\/\/git.zx2c4.com\/password-store\/tree\/contrib\/importers\/lastpass2pass.rb\">Ruby LastPass to Pass import script<\/a><\/dd><\/dl><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">My Choice<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>In the beginning, I was thinking to move to Firefox Lockwise sinceI use that as my primary browser, but I also collect some workcredentials as well. For the work credentials I use Chrome, so Ineed something that\'s browser-independent. Plus, I wish to use iton my mobile, so I need a good application for that, and FirefoxLockwise doesn\'t seem to shine there. Due to this, I discardedFirefox Lockwise as an alternative<\/p><p>LessPass doesn\'t have an automatic way of importing the LastPassdata, and I have a lot of credentials. Plus, I\'m scared about thelow popularity of the service. Discarded<\/p><p>I\'m left with Pass and Bitwarden. Well, if I was a normal person,I would just go for Bitwarden, despite the possibility of findingmyself in a similar situation in the future. It is way betterstructured, supported out of the box (without third partiessoftware) and the synchronisation happens automatically, withoutyou having to go and push the data. BUT, I\'m a nerd, so you knowwhat I\'m gonna do, let\'s go for Pass!<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">The Migration<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>This operation should be quite straightforward. Following thepass website (<code>https:\/\/www.passwordstore.org<\/code>), we basically need to do these steps:<\/p><ul class=\"org-ul\"><li>Download & install Pass<\/li><li>Validate the CSV modified. I modified it just to clean it up. Ifyou have the data already in a good shape you shouldn\'t needthis passage.<\/li><li>Setup the main passphrase\/password to encrypt your passwordstore. It should create a folder in <code>$HOME\/.password-store\/<\/code><\/li><li>Run the <a href=\"https:\/\/git.zx2c4.com\/password-store\/tree\/contrib\/importers\/lastpass2pass.rb\">Ruby LastPass to Pass import script<\/a><\/li><li>Initialise the git repository<\/li><li>Add the Github remote repository, I suggest using a private oneeven if the data would be encrypted anyway<\/li><\/ul><p>If you choose Pass, I assume you can follow these steps, so Iwon\'t show you here the commands and so on.<\/p><p>What I would do instead is: reporting in here if I find someobstacles or odds\/unexpected events during the process. This wayyou are warned about those and you might dodge them or just changeyour mind about this approach.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">I Changed my Mind<\/h4><div class=\"outline-text-4\" id=\"text-\"><figure><p><img src=\".\/2021-02-19-PasswordManagerMigration\/DesmondFordQuote.png\" class=\"img-responsive\" alt=\"DesmondFordQuote.png\"><\/p><\/figure><p>What happened? Well, I looked closely at what I have to actuallydo in addition to the points specified in <a href=\"#sec-\">The Migration<\/a> sectionand there\'s more:<\/p><dl class=\"org-dl\"><dt> I have to set up a GPG key, (private & public) <\/dt><dd>Not a big dealafter all, it\'s expected that you want your password store tobe encrypted, but you have to manage those keys carefully:<ul class=\"org-ul\"><li>Move them around yourself between machines. I didn\'t checkwhat you have to do in order to have them available formobile.<\/li><li><b>What happens if you lose your private GPG key? Well, youare literally screwed<\/b>, no chance to recover it by some hintor email recovery mechanism. All your passwords will begone. Then, what you do to mitigate this? You could replicateyour password store somewhere else with a different key, orsave the private key online, or a hint of it? (on the privateGithub repository for instance) You don\'t want to dothat. Plus, you need to manually keep this consistent andupdated.<\/li><\/ul><\/dd><dt> Import Procedure <\/dt><dd>Let say you have to setup your new machineand compare the two alternatives, Pass and Bitwarden (I\'malso considering the amount of work I need to do to get it in<code>NixOs<\/code> since I plan to move there, but it doesn\'t change so much):<ul class=\"org-ul\"><li>Bitwarden:<ul class=\"org-ul\"><li>You download the app and install the browser plugins<\/li><li>Login to your account with your master password: Done.<\/li><\/ul><p>No need to have a local configuration or anything.<\/p><\/li><li>Pass:<ul class=\"org-ul\"><li>Install pass & GPG agent<\/li><li>You need to clone the password store and place it in yourhome. Notice how you might log in to Github\/Bitbucket to do that,remember, you want it in a private place.<\/li><li>Fetch the private key and move it to the GPG home folder<\/li><li>Import the GPG private key<\/li><li>Install the plugins<\/li><\/ul><p>You can see how all this process is much more tedious in termsof steps and configurations than the previous one.<\/p><\/li><\/ul><\/dd><\/dl><p>Because of those reasons, I just decided to migrate to the moremainstream Bitwarden.<\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>I hope this simple article could help you decide which passwordmanager to choose. I know you were expecting something fancier fromme rather than the actual winner, but in the end you need toevaluate the pros and cons carefully and don\'t fall into biases ifyou can.<\/p><p>What matters is to keep your passwords as safe as possible,especially the ones that matter. I really hope this would be theonly article in this blog about password manager migration, butI am reasonably sure this is the case.<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>NixOs Checkout<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-02-03 Wed><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>After trying out the <code>Nix<\/code> package manager (full details <a href=\"https:\/\/benkio.github.io\/articles\/2021-01-29-NixMigration.html\">here<\/a>). It\'stime to try out <code>NixOs<\/code>, still with the idea to setup a reproducibleenvironment once and for all. We will start out from the result ofthe <code>Nix<\/code> experiment and evolve that to use <code>NixOs<\/code>, with a littlecheat. 😉<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Intro<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>We learned from the previous experience that, setting up a fullsystem out of only <code>Nix<\/code> package manager, is a toughtask. However, <code>NixOs<\/code> exists for that exact purpose: leverage the<code>Nix<\/code> package manager to the max and allow you to have areproducible system. Plus, you can find a lot of documentationand examples out there where you can get inspired from.<\/p><p>In particular, I came across Gabriel Volpe\'s <code>NixOs<\/code> configurationand I was quite impressed by what he had done. It seems very easyto setup, very complete and it uses <a href=\"https:\/\/xmonad.org\/\">XMonad<\/a>, a tiling windowmanager written in <code>Haskell<\/code>. Plus, he has an <a href=\"https:\/\/gvolpe.com\/blog\/xmonad-polybar-nixos\/\">article<\/a> on that!! So,it should be easy to extend and that\'s exactly what I want. Let\'swrite down the steps to take<\/p><ul class=\"org-ul\"><li>Setup a <code>NixOs<\/code> Virtual Machine<\/li><li>Take my previous <code>Nix<\/code> configuration<\/li><li>Fork Gabriel\'s Configuration<\/li><li>Merge them together<\/li><li>Try the result on a <code>NixOs<\/code> virtual machine<\/li><\/ul><p>In addition, <code>NixOs<\/code> comes also as <i>minimal<\/i> ISO, so you canconfigure it from the ground up instead of having to remove thesoftware you might not want.<\/p><p>Ah, I forgot to say that Gabriel is still developing his configuration!Then, if he will add something I might end up stealing it justby doing a quick merge from his repository. 😎<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">First Step - NixOs Virtual Machine<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>let\'s install minimal <code>NixOs<\/code> on a <code>VirtualBox<\/code>. You can find theISO <a href=\"https:\/\/nixos.org\/download.html#nixos-iso\">here<\/a>. I just noticed how you can directly download a<code>VirtualBox<\/code> Virtual Machine straight out from the site! Unfortunately it isthe <i>Plasma<\/i> version, meanwhile we want to start from the minimal one.<\/p><p>At this stage, I\'m just going to install the plain system and thenwork on the configuration on the host machine. When, I will feelconfident and satisfied enough about the configuration, I willclone it into the Virtual Machine and test it out.<\/p><p>I do so because it\'s extremely easier to work into the hostmachine instead of doing the changes inside the virtualmachine. Basically, I\'ll use that as a compiler until everythingis fine.<\/p><p>It boots into a shell as expected, but what I liked is thesuggestion to type <code>nixos-help<\/code> and get back the whole detailedmanual about the system. A comprehensive guide with all the linksto different sections<\/p><figure><p><img src=\".\/2021-02-04-NixOs\/Nixos-help.png\" class=\"img-responsive\" alt=\"Nixos-help.png\"><\/p><figcaption><span class=\"figure-number\">Figure 1:<\/span> NixOs Manual from First Installation<\/figcaption><\/figure><p>You have to be patient and read the manual about the differentoptions you have for installation, but at the end it\'s not thatdifficult to do, you need to:<\/p><ul class=\"org-ul\"><li>Partition your drive<\/li><li>Format the drive<\/li><li>Generate and write your configuration file<\/li><li>install the system<\/li><\/ul><div class=\"org-src-container\"><pre class=\"src src-shell\"><span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">for Legacy boot, see manual for uefi<\/span>sudo parted \/dev\/sda -- mklabel msdos <span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Create primary partition, but 8gb for the swap<\/span>sudo parted \/dev\/sda -- mkpart primary 1MiB -8GiB<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Create the Swap partition<\/span>sudo parted \/dev\/sda -- mkpart primary linux-swap -8GiB 100%<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Format primary partition<\/span>sudo mkfs.ext4 -L nixos \/dev\/sda1<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Format Swap<\/span>sudo mkswap -L swap \/dev\/sda2<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Mount the primary partition into mnt<\/span>sudo mount \/dev\/disk\/by-label\/nixos \/mnt<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Enable the swap<\/span>sudo swapon \/dev\/sda2<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Generate the configuration<\/span>sudo nixos-generate-config --root \/mnt<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Edit configurations<\/span>sudo nano \/mnt\/etc\/nixos\/configuration.nix<span style=\"color: #ff7f24;\"># <\/span><span style=\"color: #ff7f24;\">Install the system<\/span>sudo nixos-install<\/pre><\/div><p>I can see how it could be tedious, but if you want there\'s alreadysomeone else online who has crafted scripts to make this prettymuch automatic, eg <a href=\"https:\/\/gist.github.com\/adamlwgriffiths\/53b7ac5d6173a925a6eb980a3ac86b4d\">this installation script<\/a>.If you want to see a video regarding installing <code>NixOs<\/code>, you canfind it in <a href=\"https:\/\/www.youtube.com\/watch?v=J7Hdaqs1rjU\">here<\/a>.<\/p><p><b>Edit:<\/b> after <code>nixos-install<\/code>, the laptop crashed 😄<\/p><p>OK, seems that, after login you need to log as a root and then set the password for the new user. Not so great.<\/p><p><b>End of the day:<\/b> I took the configuration straight out from thebox, apart of my username, but I couldn\'t make it work. I got some<code>DBus<\/code> error that\'s seems related to gnome somehow, Not very promising.<\/p><p>Since we will follow the idea of: installing more then necessaryand then remove\/add\/replace software with your own, at this pointwe can just download the virtual box pre-cooked Virtual Machine from the siteand try with that. Apparently, I\'m not able to start from minimalISO 😢<\/p><p>The good thing about using the ready-to-go is that you can just:<\/p><ul class=\"org-ul\"><li>Import it into <code>VirtualBox<\/code><\/li><li>Mount the main partition as above<\/li><li>Generate the configuration<\/li><li>Install <code>NixOs<\/code><\/li><\/ul><p>So you can skip all the commands about the file system.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Working on configuration<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Well, I worked on the configuration, but I did a great mistake: I startedto change it without regarding if it worked or not. Guess what,the result is that it didn\'t work and I didn\'t know why. I assumeall the guilty, I should just start from something that works and,<b>VERY SLOWLY<\/b>, start changing it as I wish.<\/p><p>So what I do now is, let\'s just apply the configuration I forked from andthen let\'s see what we can do to make it mine 😃<\/p><p>Well, seems that, even just not touching anything from Gabriel\'sconfiguration I got:<\/p><div class=\"org-src-container\"><pre class=\"src src-shell\">error: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name ca.desrt.dconf was not provided by any .service files<\/pre><\/div><p>I tried applying the solution described in <a href=\"https:\/\/github.com\/NixOS\/nixpkgs\/issues\/24333#issuecomment-289235108\">here<\/a> and in otherplaces, but it didn\'t work. Then I tried with <code>nix-env -i dconf<\/code>installing the package manually and it seemed to work. Plus, I hadto add a couple of lines into the install script since there weresome configuration conflicts to solve.<\/p><p>Well, after the restart, <code>XMonad<\/code> started but I got this new error:<\/p><div class=\"org-src-container\"><pre class=\"src src-shell\">org.freedesktop.DBus.Error.ServiceUnknown: The name org.blueman.Mechanism was not provided by any .service files<\/pre><\/div><p>And then the screen stays grey. Apparently we need to installmore stuff on to our configuration, I\'m going to revert the <code>NixOs<\/code> andtry to do the add from <a href=\"https:\/\/github.com\/NixOS\/nixpkgs\/issues\/68591\">here<\/a>. Good news, the oldest configurationseems to have the <code>XMonad<\/code>, so I\'m not able to login to the systemas before. I\'ll try to see if I can access a terminal from thegrub and commit my changes.<\/p><p><i>Did I just debunked the Reproducibility and Reversibility of<code>NixOs<\/code>??<\/i> (or I just can\'t make it work)<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Change of Plan<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Let\'s take a different approach, I will:<\/p><ul class=\"org-ul\"><li>Take the default <code>\/etc\/nixos\/configuration.conf<\/code> andthe <code>home.nix<\/code> file I created from my previous experiment<\/li><li>See if everything works<\/li><li>Keep tweaking the configuration until a satisfactory result<\/li><\/ul><p>After a little effort in editing this and that, this seems to bethe right way to go. The only problem is that, I did quite somechanges, I run a switch command and now the virtual machine isfrozen. Time for a break.<\/p><p>There are a bad and a good news, the good one is that I restartedinto an <code>XFCE<\/code> environment, the bad is that it doesn\'t seems tohave any software available from the ones I specified into my<code>home.nix<\/code> and now I encountered this error:<\/p><div class=\"org-src-container\"><pre class=\"src src-shell\">error: attribute <span style=\"color: #ffa07a;\">\'anything\'<\/span> missing, at \/mix\/var\/nix\/profiles\/pre-user\/root\/channels\/home-manager\/modules\/programs\/matplotlib.nix<\/pre><\/div><p>The fun fact is that I can\'t change it since the file system isread only!! 😠 At least I was able to push all the changes to therepository, so I can retry a fresh install, since that\'s what weare looking for, it needs to work right from the start (if it everdoes)<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Don\'t Give Up<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>I\'m not that kind of person who stops at first issue, so let\'s keeptrying and trying again.<\/p><ul class=\"org-ul\"><li>I restarted from a new <code>NixOs<\/code> machine<\/li><li>I applied the changes following the guideline and I kept trackof each command I did.<\/li><li>I first applied the <code>configuration.nix<\/code>, installed <code>NixOs<\/code>,applied <code>home.nix<\/code> and finally I ran the <code>home-manager<\/code>. It seemsthere\'s a different way of install <code>home-manager<\/code> in <code>NixOs<\/code>since you have to be careful about the specific version you aregoing to use<\/li><\/ul><p>And now, finally, I\'m able to have a simple setup that works inthe Virtual Machine!!!! I can now proceed into tweaking the configurationdetails until I\'m fully happy with it. At each step I will justuse the <code>Nix<\/code> configuration to apply all the changes.<\/p><p><a href=\"https:\/\/github.com\/benkio\/nix-config\/commit\/8366391\">commit<\/a> ← this is the commit that seems to work now ☺<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Up to Speed<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>Now that I\'m able to comfortably configure the system, I must sayit\'s pretty fast doing so:<\/p><ul class=\"org-ul\"><li>Adding a new software is usually just<\/li><\/ul><p>adding it to the packages lines<\/p><ul class=\"org-ul\"><li>Dot files are easy to configure and check<\/li><li>Stuff like environment variables or daemons are built-in theconfiguration of <code>NixOs<\/code> or <code>home-manager<\/code>.<\/li><\/ul><p>Once you start understanding how things works you move quicklythen a bash script. Anyway, you can always fallback to a scriptfor some tasks.<\/p><p>For simplicity I used <code>xfce<\/code> as window manager, but now thateverything seems fine, I really wish to move to a tiling window manager.<\/p><p>I just tried with <code>i3<\/code> and I messed up with the configuration asexpected. Well, I think it\'s time to put the <code>NixOs<\/code> time machinemechanism to the test…and I was able to go back to a previousworking configuration of my system, change the configuration and try adifferent setup, Benkio is happy now!!! 😃<\/p><p>At the end of the day I did some progress, but half of my Virtual Machinesnapshots are broken due to bad configuration.<\/p><p><b>edit:<\/b> when I say «up to speed» I mean, «most of the time». Forexample, it took me half a day to setup Firefox extensions using<code>home-manager<\/code> and <code>nur<\/code> due to the fact that I needed to apply apackage Override and it was not so easy to figure it out.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Follow Up<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>I think I reached quite a fine initial configuration by now: Ijust have all the software I need (Emacs and Firefox basically 😃)and I have a basic tiling window manager (i3) with simpleconfiguration to manage windows and so on.<\/p><p>What I will do now is:<\/p><ul class=\"org-ul\"><li>Playing more into the Virtual Machine: trying to develop in it for a bit anddo my classic tasks.<\/li><li>Work more on the startup installation: setup everything from theminimal <code>NixOs<\/code> as smoothly as possible. Probably creating oneor more basic shell scripts.<\/li><\/ul><p>After a while, when I feel confident I might format my mainmachine entirely and switch to <code>NixOs<\/code>. I might add a new sectionlater on in case of relevant updates.<\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>Finally, I have a working configuration!! 🎉<\/p><p>As you read it wasn\'t simple at all, I hope this would turn out tobe beneficial in the long run, I can\'t say right now. What I know isthat it took me several hours in order to have something that worksand, even if the documentation is actually huge, I think a betterjob could be done regarding the <i>Getting Started<\/i> guides and basic<code>NixOs<\/code> templates instead of having to go to github inspecting thedifferent user configurations and steal from one or another.<\/p><p>I hope this article could was useful to you and if you areinterested in looking at <code>Nix~\/~NixOs<\/code> then feel free to checkout myconfiguration. You will find the link in the <a href=\"#sec-\">References Section<\/a>.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">References<\/h3><div class=\"outline-text-3\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/gvolpe.com\/blog\/xmonad-polybar-nixos\/\">Gabriel Volpe\'s <code>NixOs<\/code> Configuration w\/ Xmonad<\/a><\/li><li><a href=\"https:\/\/xmonad.org\/\">XMonad Homepage<\/a><\/li><li><a href=\"https:\/\/github.com\/benkio\/nix-config\">My Nix Configuration<\/a><\/li><\/ul><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Time to Try Nix - A Weekend Experiment<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-01-29 Fri><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Since some time now I hear about the <a href=\"https:\/\/nixos.wiki\/wiki\/Main_Page\">Nix package manager<\/a>: how it isawesome, how it solves you all the configuration problems of thisworld, how it will make you hardware agnostic. You can read all thebenefits of it all over the web. Then, as a software developer and acurious nerd with his own configuration scripts and so on, I decided that\'stime to give it a go.<\/p><p>In this article, I will describe the process, from being a completenewbie, knowing nothing about it apart form the last paragraph, to setup a configuration in a weekend.<\/p><p>Disclamer: you might have different needs from mine, but still Ithink this could help someone else. So, let\'s get ready to rumble!!!<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">The Plan<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>As a real professional, I must start with a tweet 😉<\/p><figure><p><img src=\".\/2021-01-29-NixMigration\/The_Plan_Tweet.png\" class=\"img-responsive\" alt=\"The_Plan_Tweet.png\"><\/p><figcaption><span class=\"figure-number\">Figure 1:<\/span> The Plan Tweet<\/figcaption><\/figure><p>I skip the <code>Virtual Box<\/code> & <code>Ubuntu Minimal<\/code> installation. I chooseUbuntu minimal as a base since I want to really start from scratchand try to setup also the window manager.<\/p><p>Another question I want to answer straight away is: <i>Why don\'t Ijust start using <code>NixOs<\/code> instead?<\/i> Honestly, I don\'t have a strongreason, I can say I want to start as minimal as possible and from athing I know. Plus, it should be quicker to set up a minimalvirtual machine rather then one with <code>NixOs<\/code>.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">First Impact<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>The only word I can think of is: <b>overwhelming<\/b>, but not in a goodway. It has so much stuff that you, newcomer, will feel confusedand disoriented. You might thing «Oh, let\'s read the manual\/guidefrom the official site», well there are tons of stuff: the <code>Nix<\/code>language, the <code>Nixos<\/code>, the different commands, multi-user setup,profiles and much much more. The only thing I found useful washow to install <code>Nix<\/code> and that\'s it.<\/p><p>What I\'m searching is: how to setup my own configuration, then,when the need arise, I will go and search how to create a Set or afunction with the <code>Nix<\/code> language. Instead, a lot of the guidesdoesn\'t start from a simple plain configuration with, let\'s say, just<code>wget<\/code> and <code>7zip<\/code>.<\/p><p>So, I kept reading and reading, landing finally on Reddit. Theysuggest to take an existing configuration and start by modifyingthat. I must say the majority of the guides are about <code>NixOs<\/code>instead of just <code>Nix<\/code> on a non-NixOs distribution, like I\'m trying to do.<\/p><\/div><div id=\"outline-container-sec-\" class=\"outline-5\"><h5 id=\"sec-\">NixOs and Home Manager<\/h5><div class=\"outline-text-5\" id=\"text-\"><p>In the end, I discovered that, with <code>Nix<\/code> only, you can\'tactually manage the whole system(or it\'s not straightforward atleast). In particular, I wasn\'t able to find a guide telling youhow to set up a proper script like you would with bash. Plus,if you want to change some configuration apart from justinstalling\/uninstalling software, you need <code>NixOs<\/code>. That could beinstalled by using the <a href=\"https:\/\/github.com\/jeaye\/nixos-in-place\">nix-in-place script<\/a>, but, what it does isto create an <span class=\"underline\">alternative<\/span> root folder as you will just restartin <code>NixOs<\/code> and moves everything you have to an <code>old-root<\/code> folder. Idon\'t think you want that, unless you just do it from the start(and still I find it not very cool, let\'s say okayish).<\/p><p>After some more research, I found the <a href=\"https:\/\/github.com\/nix-community\/home-manager\">Home Manager Package<\/a>. Thiswill extend the capabilities of <code>Nix<\/code> by far and let you manageyour home directory using <code>Nix<\/code>. I still don\'t know if this has thesame power of <code>NixOs<\/code>, but it seems the thing I want. Especially,considering that I wish to be more OS independent as possible.<\/p><p>What you have to do is to create a <code>home.nix<\/code> into your<code>$HOME\/.config\/nixpkgs\/<\/code> and then run <code>home-manager switch<\/code>.Checkout my <a href=\"https:\/\/github.com\/benkio\/nix-config\/commit\/378a70906e691d4bdf3892844740743716eef40f\">Initial Commit<\/a>. If you want to check out the initialreference script, you can find it <a href=\"https:\/\/gist.github.com\/benkio\/2d4346e5e02b85556b0e\">here<\/a>.<\/p><\/div><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Setup From existing Github Configuration<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>As every Emacs user, I already have my Emacs configurationjealously crafted and polished. Now, I don\'t want to restart fromscratch and specify all the packages I have and so on. Plus, I havesome custom <code>elisp<\/code> functions I wish to preserve. Fortunately,<code>Nix<\/code> gives you a way to fetch something from Github using<code>fetchFromGitHub<\/code> function. (<a href=\"https:\/\/nixos.org\/manual\/nixpkgs\/stable\/#sec-sources\">Manual Section<\/a>)<\/p><p>So, the idea here is to:<\/p><ul class=\"org-ul\"><li>Download your configuration from Github: provide the owner, repository,reference to the specific commit or tag and hash SHA256 of theresulting directory<\/li><li>Tell <code>Nix<\/code> to copy it into the <code>$HOME\/.emacs.d\/<\/code> directory. Ican\'t move it since <code>Nix<\/code> stores immutable directories in hisstore. Therefore, you must just make a copy to the desiredlocation. Fortunately the Emacs configuration, without compilationpackages is not that big.<\/li><li>Setup the Emacs server hook. (Optional, advanced topic)<\/li><\/ul><p>And that should be it. I took <a href=\"https:\/\/github.com\/NixOS\/nixpkgs\/issues\/14277\">this issue<\/a> as a reference.<b>EDIT:<\/b> After a little bit more research I found this betteranswer: <a href=\"https:\/\/discourse.nixos.org\/t\/home-manager-spacemacs\/8033\">emacs.d home-manager dot file(directory)<\/a><\/p><p>This works, BUT!!! the result will be all symlinks to the <code>Nix<\/code>store. Then, you can\'t actually change anything from theconfiguration itself because <code>Nix<\/code> store immutable data. Youshould change it somewhere else, commit, recalculate thesha256…a full-pain. Then, you have to put a simple shell snippetto copy the source to the <code>$HOME\/.emacs.d\/<\/code> folder and change thepermissions in order to be able to change it.<\/p><p>Huge downside of using <code>fetchFromGitHub<\/code> is that it just downloadthe tarball and extract it\'s content. Therefore, your Emacs <code>.git<\/code>folder is not downloaded and then, when you move it to the rightplace with the shell snippet you don\'t have the gitrepository. Now, you can decide to use <code>fetchGit<\/code> with theoption <code>leaveDotGit = true<\/code> to solve the issue, <b>but<\/b> rememberthat <code>Nix<\/code> rely on sha256 and bring the <code>.git<\/code> folder down willincrease the probability of a mismatch!! (<a href=\"https:\/\/discourse.nixos.org\/t\/keep-git-folder-in-when-fetching-a-git-repo\/8590\/2\">source<\/a>)<\/p><p>As a result, you will have your beloved <code>emacs.d<\/code> configurationmaintained in another repository. The only weirdness I noticed wasthat the resulting <code>git<\/code> repository is set on a new branch called<code>fetchgit<\/code>. What you have to do is to:<\/p><ul class=\"org-ul\"><li>Setup the remote: <code>git add remote origin https:\/\/github.com\/....<\/code><\/li><li>Create & checkout a new branch from <code>master<\/code>.<\/li><li>Delete the <code>fetchgit<\/code>.<\/li><\/ul><p>Now the question is: does in work consistently? Is it actuallyreproducible? Future myself will tell.<\/p><p><a href=\"https:\/\/github.com\/benkio\/nix-config\/commit\/6b2a31fafab4f9853f9ef9a87acbebbfa810eab1\">emacs configuration commit<\/a><\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Provided Packages<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>I must say the amount of available packages is huge: I found(almost) all I needed straight out of the main channels. I hadsome trouble installing the <code>amule<\/code> package due to a missinglibrary (<code>crypto++<\/code>) and the <code>home-manager build<\/code> became quiteslow checking the packages, but overall it went smoothly more thenexpected.<\/p><p><a href=\"https:\/\/github.com\/benkio\/nix-config\/commit\/ef67d992efe0f96840e16814fe669ebae8c2498d\">add packages and todos to the home.nix<\/a><\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">The Unexpected<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>I added more packages, I added the keyboard configuration and soon, then I looked into how to setup <code>I3<\/code> and <code>X11<\/code>. Well, as a resultI caused an infinite login loop, inserting <code>startx<\/code> into the<code>.bash_profile<\/code>. Apparently it tried just to turn back to loginagain!!<\/p><p>Well, I guess it\'s time to try to setup a new virtual machine then.<\/p><p>As you could imagine, setting up <code>X11<\/code> and <code>XOrg<\/code> is not thatstraight forward as the other packages. The reason behind this isbecause online you can find a lot of articles using <code>NixOs<\/code> thatprovides a system layer, meanwhile in here I don\'t have that, Ineed to setup everything into the <code>home-manager<\/code> that seems tohave a subset of the configurations you can specify in<code>NixOs<\/code>.<\/p><p>For example, in the following pages all reference to the<code>Nix<\/code> configurations in the <i>etc<\/i> folder:<\/p><p><a href=\"http:\/\/wiki.haskell.org\/Xmonad\/Installing_xmonad#NixOS\">XMonad NixOs Installation Page<\/a><a href=\"https:\/\/gvolpe.com\/blog\/xmonad-polybar-nixos\/#xmonad\">Gabriel Volpe\'s XMonad Configuration<\/a><\/p><p><b>edit:<\/b> I start thinking that, installing <code>XOrg<\/code> into <code>VirtualBox<\/code>is not a wonderful Idea and it might just not work. I\'m thinkingabout creating a Virtual machine with a GUI in place. I know it\'sa fallback from the initial plan, but I have to reduce the amountof weirdness if I wish to proceed 😃<\/p><p>After tons of trials, finally, I have to give up onsetting the Window Manager. Basically, if you are using <code>NixOs<\/code> itshould be easy: you have your own <code>\/etc\/nixos\/configuration.nix<\/code>where you set your system configuration and then, on<code>$HOME\/.config\/nixos\/home.nix<\/code> you can enable thing on the userside easily. The problem here is that you are bound to<code>NixOs<\/code>. You can install it with <code>nix-in-place<\/code>, but in mymind you should be able to <b>CONFIGURE<\/b> everything you want withouthaving to go into the specific Linux distribution.<\/p><p>I might ask for help in the future into some IRC channel or wherever.<\/p><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">Developer Environment<\/h4><div class=\"outline-text-4\" id=\"text-\"><p>After the failure of the previous section I decided that I canlive without setting my window manager for now (<a href=\"http:\/\/read.gov\/aesop\/005.html\">The Fox & TheGrapes<\/a>), what about <code>Scala<\/code> and <code>Haskell<\/code> then? Well, guess what, Icloned a bunch of project in both languages and I wasn\'t able tocompile them in the virtual machine.<\/p><dl class=\"org-dl\"><dt> Sbt <\/dt><dd><\/dd><\/dl><pre class=\"example\">MissingRequirementError: Object java.lang.objectin compiler mirror not found<\/pre><dl class=\"org-dl\"><dt> Stack <\/dt><dd><code>libgmp.10.so: cannot open shared open file<\/code><\/dd><\/dl><p>For both this errors I scanned the internet for HOURS without anysuccess. After all this pain, I\'m really considering to just havean additional shell script to run post-Nix. At least I can getwhat I can\'t set from <code>Nix<\/code>…if I just don\'t consider the wholeexperiment a failure, but definitely it is not going the way I washoping for. Especially considering that I could justinstall the <code>haskell-plaftorm<\/code> with a one-liner command.<\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><figure><p><img src=\".\/2021-01-29-NixMigration\/weekendResult.png\" class=\"img-responsive\" alt=\"weekendResult.png\"><\/p><figcaption><span class=\"figure-number\">Figure 2:<\/span> Weekend Recap<\/figcaption><\/figure><p>Well, this is the final result. Not that great. What I could try todo is to actually use directly <code>NixOs<\/code> and get away with it, butwho knows.<\/p><p>I know you want me to answer the questions:<\/p><ul class=\"org-ul\"><li>Is it hard? Yes, as every time you need to work with a newtechnology I guess. You need to learn a new language, a new way ofdealing with software and basically google for everything hopingit\'s not a pain in the nuts…being often disappointed…<\/li><li>Do you recommend it? Mmm hard to say, I did it since I\'m a nerdand I like to try stuff out when I hear\/read good things about them,but it\'s really worth it? Isn\'t just better to create your dockerimage and live with it? Is actually better then a well craftedscript? Very hard to say, but I mean: I use Emacs, I have a blogin org-mode. It\'s obvious that there is already something wronghere. Probably, you should consider how important is for you tohave a really stable environment (more than bash script) and howfrequently you have to setup a new machine as well!<\/li><li>When this could be convenient? I think, using <code>Nix<\/code> +<code>home-manager<\/code> without <code>NixOs<\/code>, could be convenient when you don\'tcare\/want\/can manage the operating system settings, eg. somedevelopers set up their MacOs using <code>Nix<\/code> instead of <code>Homebrew<\/code>.<\/li><\/ul><\/div><\/div><div id=\"outline-container-sec-\" class=\"outline-3\"><h3 id=\"sec-\">References<\/h3><div class=\"outline-text-3\" id=\"text-\"><dl class=\"org-dl\"><dt> <a href=\"https:\/\/nixos.org\/\">NixOs Website<\/a> <\/dt><dd>Where you can find all the official instructionsabout <code>Nix<\/code> and the related distribution.<\/dd><dt> <a href=\"https:\/\/github.com\/jeaye\/nixos-in-place\">Nix In Place<\/a> <\/dt><dd>You can install <code>NixOs<\/code> wherever you want (Linux orMac) more or less.<\/dd><dt> <a href=\"https:\/\/github.com\/nix-community\/home-manager\">Home Manager<\/a> <\/dt><dd><code>Nix<\/code> package that allows you to access quite someconfiguration options of your home directory as well as manageyour software.<\/dd><dt> <a href=\"https:\/\/github.com\/benkio\/nix-config.git\">My Nix Configuration<\/a> <\/dt><dd>Here is where I try to build my nixconfiguration.<\/dd><dt> <a href=\"https:\/\/gist.github.com\/benkio\/2d4346e5e02b85556b0e\">Reference Script<\/a> <\/dt><dd>Script I used until now to setup a new machine<\/dd><\/dl><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Advent Of Code 2015<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-01-24 Fri><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>I decided to recover from the <a href=\"https:\/\/www.adventofcode.com\">Advent of code website<\/a> the old 2015problem list and try to solve the problems in there as well.<\/p><p>As for the 2020, here you can find the screencast of the calendaranimated, plus the final congratulation page.<\/p><p>You can find all the code in here: <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/tree\/master\/AdventOfCode\">Advent Of CodeRepository<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure> <img src=\".\/2020-01-24-AdventOfCode2015\/AdventOfCodeCompleted.png\" alt=\"Completed Screen of Advent of Code\" align=\"left\" title=\"Advent Of Code Completed\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><br\/><div class=\"embed-responsive embed-responsive-16by9\"> <video controls autoplay loop> <source src=\".\/2020-01-24-AdventOfCode2015\/AdventOfCodeCalendar.mp4\" type=\"video\/mp4\"> <\/video><\/div><p>The Advent of Code Calendar Animation<\/p><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>Compared to recent years, the 2015 is definitely easier. So, if youare at the beginning of your programming journey, you can considergoing through those exercises.<\/p><p>Just remember to <b>have fun<\/b> 😉<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Advent Of Code 2020<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-12-27 Sun><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>This year I did the advent of code for the first time and, since Iwish to learn haskell I used it to solve (most of) the problems.<\/p><p>It wasn\'t easy and a couple of times I had to look for solutions,since I didn\'t know that specific math theorem or I just couldn\'tfigure out how to proceed.<\/p><p>I just did some screenshot and screencast as a memory of theexperience.You can find all the code in here: <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/tree\/master\/AdventOfCode\">Advent Of Code Repository<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure> <img src=\".\/2020-12-27-AdventOfCode2020\/AdventOfCodeCompleted.png\" alt=\"Completed Screen of Advent of Code\" align=\"left\" title=\"Advent Of Code Completed\" class=\"img-fluid\" style=\"width:100%;\"\/> <figcaption>Advent of Code Final Screen<\/figcaption><\/figure><br\/><div class=\"embed-responsive embed-responsive-16by9\"> <video controls autoplay loop> <source src=\".\/2020-12-27-AdventOfCode2020\/AdventOfCodeCalendar.mp4\" type=\"video\/mp4\"> <\/video><\/div><p>The Advent of Code Calendar Animation<\/p><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>It\'s an experience I recommend to anyone who\'s approachingprogramming. I would say you will pretty much never encounter suchproblems in real life\/work, but it\'s good for your training. Plus,this puzzles are often used in job interviews!! So it might be morehelpful then you think.<\/p><p>I will probably going to resume the advent of code of past years aswell. 😉<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Ken Il Guerriero (Hokuto No Ken) - Italian Season 1 Theme Chords<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-10-03 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Chords of the Hokuto No Ken (Ken Il Guerriero) Season 1 theme.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/1Crm94eMuPE?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Ken il Guerriero 1986 Sigla Completa<\/figcaption><\/figure><br\/><pre class=\"example\">Dmin C BbMai, mai scorderaiC DminL\'attimo, la terra che tremòDmin C BbL\'aria si incendiòC DminE poi silenzioDmin C Dmin C BbE gli avvoltoi sulle case sopra la cittàC Dmin G#minSenza pietàD#min C# BChi mai fermeràC# D#minLa follia, che per le strade va C# BChi mai spezzeràC# D#minLe nostre catene C# D#min C# BChi da quest\'incubo nero ci risve glie ràC# D#minChi mai potràD#min - C# - B - C#D# Bbken, sei tu G# BbFantastico guerrieroD# Bb G#Sceso come un fulmine dal cieloF CKen, sei tu Bb CIl nostro condottieroF C BbE nessuno al mondo adesso è soloBb G# BbD#min C# BKen, sei liberoC# D#minL\'unico, l\'ultimo angeloD#min C# BKen, sei l\'energiaC# D#minL\'azzurra magia (Magia, magia, magia) C#Stella dell\'orsa maggiore D#min C# BStella su di noiC# D#minGuerriero va!D#min - C# - B - C#D# BbKen, sei tu G# BbCol pugno tuo più forteD# Bb G#Tu che hai messo KO la morteF CKen, sei tu Bb CL\'acciaio nelle maniF C BbTu la mia speranza nel domaniBb G# BbD#min C# BMai, mai scorderaiC#L\'attimo D#minLa terra che tremò C# BVai, vai tu vivraiC# D#minGiorni felici C#Stella dell\'orsa maggiore D#min C# BStella su di noiC# D#minGuerriero vai<\/pre><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Rondò Veneziano - Nettuno - Tab + Score + Midi<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-08-31 Mon><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>I\'m studying this song and I was searching in the web for some scoreabout it. I couldn\'t find any. Therefore, since I need to learn itby ear, I decided to write an article on that, transcribing themusic. So a score would be available and I could experiment with <a href=\"https:\/\/lilypond.org\/\">Lilypond<\/a>.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-ArticleContentSong\" class=\"outline-4\"><h4 id=\"ArticleContentSong\"><a id=\"sec-\" name=\"sec-\"><\/a>Song<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentSong\"><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/iWkUn3_0W2E?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Rondò Veneziano - Nettuno<\/figcaption><\/figure><\/div><\/div><div id=\"outline-container-ArticleContentLilypondSource\" class=\"outline-4\"><h4 id=\"ArticleContentLilypondSource\"><a id=\"sec-\" name=\"sec-\"><\/a>Lilypond Source<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentLilypondSource\"><p>Here you can find the <a href=\"https:\/\/lilypond.org\/doc\/v2.20\/Documentation\/learning\/index.html\">Lilypond guide<\/a><\/p><p><a href=\"2020-08-31-Nettuno\/Nettuno.ly\">Transcription<\/a><\/p><\/div><\/div><div id=\"outline-container-ArticleContentScore\" class=\"outline-4\"><h4 id=\"ArticleContentScore\"><a id=\"sec-\" name=\"sec-\"><\/a>Score PDF<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentScore\"> <figure><div class=\"video-container\"> <iframe class=\"responsive-iframe\" src=\".\/2020-08-31-Nettuno\/Nettuno.pdf\" allowfullscreen><\/iframe><\/div> <figcaption> Rondò Veneziano - Nettuno - PDF <\/figcaption> <\/figure><\/div><\/div><div id=\"outline-container-ArticleContentMidi\" class=\"outline-4\"><h4 id=\"ArticleContentMidi\"><a id=\"sec-\" name=\"sec-\"><\/a>lilypond Audio<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentMidi\"><audio controls> <source src=\".\/2020-08-31-Nettuno\/Nettuno.mp3\" type=\"audio\/mp3\">Your browser does not support the audio element.<\/audio><\/div><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>You Don\'t Need Metals When You Can Build Your Own (Heavy) Metal<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-08-29 Sat><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Plain emacs is pretty basic, but we all know you can extend it, withlittle effort, to the max.<\/p><p>This article is about my experience of turning emacs, from a simpleeditor, to something like an IDE. It\'s still a work in progress asalways, but it may inspire you into doing the same or pick up someideas from my configuration, extend and make them yours.<\/p><p>Enjoy 😺<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><p>I am one of those nerds who uses Emacs for pretty mucheverything. I\'m not living in it, but I\'m not too far from it. Oneof the things I love about it is the possibility to configure itas much as you like: creating you own features to extend it.<\/p><p>I work as a software engineer and, when you deal with code, youoften need a set of features from your editor (IDE) that speed upyour productivity by a lot. Nowadays, these features are shippedthroughout classic IDEs, like IntelliJ or Eclipse, or cross-editorextensions, like <a href=\"https:\/\/scalameta.org\/metals\/\">Metals<\/a> in case of Scala, using the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Language_Server_Protocol\">Language ServerProtocol (LSP)<\/a>. Of course, emacs support it as well.<\/p><p><b>However<\/b>, installing metals in emacs ended up being a real pain inthe ass:<\/p><ul class=\"org-ul\"><li>Having a hard time setting up Doom + Scala (Metals) + Gradle +monorepo - <code>https:\/\/www.reddit.com\/r\/emacs\/comments\/idsyxi\/having_a_hard_time_setting_up_doom_scala_metals\/<\/code><\/li><li><a href=\"https:\/\/twitter.com\/NicolasRinaudo\/status\/1204143009807425536\">Metals in VSCode vs Metals in Emacs<\/a><\/li><\/ul><p>I don\'t know about the status of it right now. Check on twitter ifyou like.<\/p><p>At that point, I just told to myself: <i>Benkio, you know how to code, you can learn and use Elisp, you can virtually implement those features for yourself!<\/i>And here we are.<\/p><\/div><\/div><div id=\"outline-container-ArticleContentFeatures\" class=\"outline-3\"><h3 id=\"ArticleContentFeatures\"><a id=\"sec-\" name=\"sec-\"><\/a>Features<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContentFeatures\"><p>The features I selected for my work-flow are the following:<\/p><dl class=\"org-dl\"><dt> <code>shell-clean-old-output<\/code> <\/dt><dd>During development I usually startcontinuous compilation, so I have a background shell printingout the compiler output at every change of code (filesaving). It can be the compiler, the linter or a checker, incase of dynamic languages. You name it. In this way, when youare ready to deal with errors, you just switch to the shell andyou see them. However, when you switch to the shell after awhile, buffer carry the old output results, with errors thataren\'t there anymore. Therefore, I need a command to cleanupthe shell and leave just the last output, the meaningful one.<\/dd><dt> <code>goto-next-warn-error<\/code> <\/dt><dd>Once you have the current errors youwish to jump to them. Fortunately, pretty much every toolprovides, as error\/warning output: file path, line & columnnumber. So, with this command, I put the cursor at thebeginning of the shell output and it moves the cursor to thenext location of the error\/warnings it encounter, parsing theoutput.<\/dd><dt> <code>remove-unused-import<\/code> <\/dt><dd>Dependency management is a very usefulfeature of any IDE. When happens to have an import that\'s notused you really wish to get rid of it. This command is built onthe previous one and it goes and remove it for you. I must sayit\'s not always super precise (depends on how you instruct it)and it might leave empty lines, but nothing a code formatter,like <a href=\"https:\/\/scalameta.org\/scalafmt\/\">scalafmt<\/a> or <a href=\"https:\/\/github.com\/mihaimaruseac\/hindent\">hindent<\/a>, can\'t deal with.<\/dd><dt> <code>import-type-at-point<\/code> <\/dt><dd>The other way around can happen. Whenyou have something in your code that\'s not in scope, so youneed the right import. This command goes in search of anexisting import for such object and, if none is found, it goesfor its definition and build it by itself. Then, the import isadded to your current file automatically.<\/dd><dt> <code>extract-code-line-or-region<\/code> <\/dt><dd>A key feature for everyrefactoring maniac, the ability to extract pieces of code to anew value or method. it just put the result to the first emptyline it finds backwards, so you can then move it wherever youwant, and substitute the code you selected with the name ofthat method\/value. It might not be super precise as well, butit\'s a start.<\/dd><\/dl><p>IDEs have definitely a longer list of features, but I added the mostimportant for me. A possible improvement would be to add a commandfor unimplemented methods\/values, in case you are creating somethingthat should satisfy a specific contract, like a trait\/interface\/typeclass.<\/p><p>At the time of writing, I\'ve implemented them for: Haskell, Scala and Typescript.<\/p><\/div><\/div><div id=\"outline-container-ArticleContentAbstactPattern\" class=\"outline-3\"><h3 id=\"ArticleContentAbstactPattern\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract the Pattern<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContentAbstactPattern\"><p>The previous section is my <i>Letter of Intent<\/i> about what I need andI want to do, but the number of languages out there arecountless. Do I need to implement the same exact features countlesstimes (or at least for every language I\'m interested in) over andover again??<\/p><p>Answer: <b>Yes and No<\/b> 😄<\/p><p>Every language\/tool out there has different syntax, so we need to addsome specific logic to each one of them. Luckily, there areoperations that are common whatever the technology, such as copying theresulting import to the starting file or copying the extract code tothe nearest available position. How can we factor out theseoperations and share them between the specific implementations?<\/p><p>Answer: <b>Higher-order Functions<\/b><\/p><p>We can have common patterns\/templates for the features that get, as arguments,the language-specific functions and apply them accordingly.<\/p><\/div><div id=\"outline-container-ArticleContentAbstactPatternExample\" class=\"outline-4\"><h4 id=\"ArticleContentAbstactPatternExample\"><a id=\"sec-\" name=\"sec-\"><\/a>Example<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentAbstactPatternExample\"><p>I will show you how the higher-order function behindthe feature <code>extract-code-line-or-region<\/code> is built.<\/p><div class=\"org-src-container\"><pre class=\"src src-emacs-lisp\">(<span style=\"color: #00ffff;\">defun<\/span> <span style=\"color: #87cefa;\">extract-code-line-or-region-template<\/span> (value function postDefinitionSyntaxValue postDefinitionSyntaxFunc EndSyntaxValue EndSyntaxFunc name parameters from to) <span style=\"color: #ffa07a;\">\"Template for extracting code to value or function:<\/span><span style=\"color: #ffa07a;\"> Based on the input it this extract the selected code to the closest empty line above.<\/span><span style=\"color: #ffa07a;\"> - Value: syntax for values in target laguage<\/span><span style=\"color: #ffa07a;\"> - function: syntax for function in target laguage<\/span><span style=\"color: #ffa07a;\"> - postDefinitionSyntax: what you put between the name + parameters and the body of the function\/value. eg (= in scala)<\/span><span style=\"color: #ffa07a;\"> - EndSyntax: what to put at the end of the definition of value\/function body. Eg in js it\'s \';\' for value but \'<\/span><span style=\"color: #7fffd4;\">}<\/span><span style=\"color: #ffa07a;\">\' for functionsxs<\/span><span style=\"color: #ffa07a;\"> - name: name of the extracted value\/function<\/span><span style=\"color: #ffa07a;\"> - from: start of the region<\/span><span style=\"color: #ffa07a;\"> - to: end of the region<\/span><span style=\"color: #ffa07a;\"> \"<\/span> <span style=\"color: #ff7f24;\">;; <\/span><span style=\"color: #ff7f24;\">extract code, cut if region or cut from point to end of the line<\/span> (<span style=\"color: #00ffff;\">setq<\/span> code (buffer-substring from to)) (delete-region from to) <span style=\"color: #ff7f24;\">;; <\/span><span style=\"color: #ff7f24;\">Generate code<\/span> (<span style=\"color: #00ffff;\">setq<\/span> resultDefinition (<span style=\"color: #00ffff;\">if<\/span> (string-blank-p parameters) (concat value name postDefinitionSyntaxValue code EndSyntaxValue) (concat function name parameters postDefinitionSyntaxFunc code EndSyntaxFunc))) (<span style=\"color: #00ffff;\">setq<\/span> resultReference (<span style=\"color: #00ffff;\">if<\/span> (string-blank-p parameters) name (concat name parameters))) <span style=\"color: #ff7f24;\">;; <\/span><span style=\"color: #ff7f24;\">Put the resultReference at point<\/span> (insert resultReference) <span style=\"color: #ff7f24;\">;; <\/span><span style=\"color: #ff7f24;\">Move to the closest ^$ line and insert the resultDefinition<\/span> (re-search-backward <span style=\"color: #ffa07a;\">\"^$\"<\/span>) (insert resultDefinition) )<\/pre><\/div><p>The first thing you notice is how this function takes a lot ofparameters. That\'s due to his nature of higher order function, sinceit needs to apply the language specific functions in input andchange its behavior based on the values it gets.<\/p><p>These are the operations performed:<\/p><ul class=\"org-ul\"><li>Grabs the code to extract and delete the region selected.<\/li><li>Compose the input values to create the extracted code and therelated reference. It choose if it will be a value of a methodbased on the presence of parameters.<\/li><li>Insert the reference where there was the code.<\/li><li>Search back for the first empty line.<\/li><li>Insert the computed value, method.<\/li><\/ul><p>Notice how there\'s no reference about a specific language syntaxinto this function.<\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleContentLanguageSpecific\" class=\"outline-3\"><h3 id=\"ArticleContentLanguageSpecific\"><a id=\"sec-\" name=\"sec-\"><\/a>Language Specific<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContentLanguageSpecific\"><p>Now that we have templates to use, we can focus on crafting thelanguage specific functions to pass into the templates. Most of thetime, them rely on Regexp to instruct: what to search for or what isthe right syntax of an import for instance. Of course, some are morecomplex then other and can be quite tricky to set themproperly. Plus, sometimes you need to override a specific template functionbecause the target language is quite <i>special<\/i>.<\/p><\/div><div id=\"outline-container-ArticleContentLanguageSpecificExample\" class=\"outline-4\"><h4 id=\"ArticleContentLanguageSpecificExample\"><a id=\"sec-\" name=\"sec-\"><\/a>Example<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentLanguageSpecificExample\"><p>Still using the <code>extract-code-line-or-region-template<\/code> but theHaskell version<\/p><div class=\"org-src-container\"><pre class=\"src src-emacs-lisp\">(<span style=\"color: #00ffff;\">defun<\/span> <span style=\"color: #87cefa;\">hs-extract-code-line-or-region<\/span> (name <span style=\"color: #98fb98;\">&optional<\/span> parameters from to) <span style=\"color: #ffa07a;\">\"Extract the code to val or def:<\/span><span style=\"color: #ffa07a;\"> Require:<\/span><span style=\"color: #ffa07a;\"> - Name of the val\/def<\/span><span style=\"color: #ffa07a;\"> - Optional list of parameters (if empty it will be a val)<\/span><span style=\"color: #ffa07a;\"> if no code region is selected then it extracts the rest of the line from current position<\/span><span style=\"color: #ffa07a;\"> Return type not specified.<\/span><span style=\"color: #ffa07a;\"> \"<\/span> (<span style=\"color: #00ffff;\">interactive<\/span> (list (read-string <span style=\"color: #ffa07a;\">\"value\/function name: \"<\/span> ) (<span style=\"color: #00ffff;\">progn<\/span> (<span style=\"color: #00ffff;\">setq<\/span> param (read-string <span style=\"color: #ffa07a;\">\"param name (RET to finish): \"<\/span>) params nil ) (<span style=\"color: #00ffff;\">while<\/span> (not (equal <span style=\"color: #ffa07a;\">\"\"<\/span> (s-trim param))) (<span style=\"color: #00ffff;\">push<\/span> (s-trim param) params) (<span style=\"color: #00ffff;\">setq<\/span> param (read-string <span style=\"color: #ffa07a;\">\"param name (RET to finish): \"<\/span>)) ) (concat <span style=\"color: #ffa07a;\">\" \"<\/span> (mapconcat \'identity (reverse params) <span style=\"color: #ffa07a;\">\" \"<\/span>)) ) (<span style=\"color: #00ffff;\">if<\/span> (use-region-p) (region-beginning) (point)) (<span style=\"color: #00ffff;\">if<\/span> (use-region-p) (region-end) (line-end-position)) )) (funcall \'extract-code-line-or-region-template <span style=\"color: #ffa07a;\">\"\"<\/span> <span style=\"color: #ffa07a;\">\"\"<\/span> <span style=\"color: #ffa07a;\">\" = \"<\/span> <span style=\"color: #ffa07a;\">\" = \"<\/span> <span style=\"color: #ffa07a;\">\"\"<\/span> <span style=\"color: #ffa07a;\">\"\"<\/span> name parameters from to) )<\/pre><\/div><p>Here, the most part of the function is about fetching the input. Inparticular:<\/p><ul class=\"org-ul\"><li>The name of the outcome value\/method.<\/li><li>The list of parameters. See how it loops until the user inserts theempty string.<\/li><li>The region, if present, Otherwise a new region is taken, startingfrom the current position to the end of the line.<\/li><\/ul><p>After all the inputs are set, the function just calls the relativetemplate function, passing some constants, where the language-syntaxspecific is required, and the variables, where the values are drivenby the input. Here you can see how most of the constants are emptystrings since Haskell has a very concise syntax compared to otherlanguages like Scala or Typescript.<\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>Usually, you write the conclusions at the end of the article, but inthis case, I already know what to write in here, so I have writtenthese sections pretty much at the beginning of the article.<\/p><p>There\'s a high chance that <a href=\"https:\/\/scalameta.org\/metals\/\">Metals<\/a>, or something similar, now worksflawlessly and it\'s easy to install. So, my recommendation is to useit, the less configuration you have in place, the better.<\/p><p>By the way, this was a very cool exercise to do and it\'s easy toremove because they are just a bunch of (almost) stand-alonefunctions. Plus, right now they aren\'t perfect, but knowing that youcould change them and make them work better is reassuring.<\/p><p>In summary, if you want to really make a difference and improve thestate of software, don\'t code for your own configuration, but go andcontribute to those projects to make them better.<\/p><p>PS: basically, every conclusion I make at the end of my articles issomething like. Don\'t do what you just read!. 😄<\/p><\/div><div id=\"outline-container-sec-\" class=\"outline-4\"><h4 id=\"sec-\">References<\/h4><div class=\"outline-text-4\" id=\"text-\"><ul class=\"org-ul\"><li><a href=\"https:\/\/github.com\/benkio\/emacs-config\/blob\/master\/config.org\">Emacs configuration<\/a><\/li><li><a href=\"https:\/\/github.com\/benkio\/emacs-config\/blob\/master\/config.org?plain=1#L2099\">Template Functions<\/a><\/li><li><a href=\"https:\/\/github.com\/benkio\/emacs-config\/blob\/master\/config.org?plain=1#L2324\">Scala Functions<\/a><\/li><li><a href=\"https:\/\/github.com\/benkio\/emacs-config\/blob\/master\/config.org?plain=1#L2519\">Haskell Functions<\/a><\/li><li><a href=\"https:\/\/github.com\/benkio\/emacs-config\/blob\/master\/config.org?plain=1#L2735\">Typescript Functions<\/a><\/li><\/ul><\/div><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Project Euler 11-20<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-08-11 Tue><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p><a href=\"https:\/\/projecteuler.net\/\">Project Euler<\/a> is a collection of math based problems you can try tosolve using a programming language, for fun.<\/p><p>Follow-up, from problem 11 to 20.<\/p><p><b>Edit:<\/b> <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-09-09 Thu> <\/span><\/span> add the Scala 3 Solutions.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-ArticleContentEx11\" class=\"outline-4\"><h4 id=\"ArticleContentEx11\"><a id=\"sec-\" name=\"sec-\"><\/a>Largest Product in a Grid<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx11\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=11\">Largest Product in a Grid<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L12-81&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L7-L47&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx12\" class=\"outline-4\"><h4 id=\"ArticleContentEx12\"><a id=\"sec-\" name=\"sec-\"><\/a>Highly Divisible Triangular Number<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx12\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=12\">Highly Divisible Triangular Number<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L83-99&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L49-L55&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Note that <code>find<\/code> function from <code>Data.List<\/code> is more inefficientthen using the handmade, recursive function I used. Just trySomething like<\/p><div class=\"org-src-container\"><pre class=\"src src-haskell\">fromJust <span style=\"color: #87cefa;\">$<\/span> find ((<span style=\"color: #eedd82;\">==<\/span>500) <span style=\"color: #eedd82;\">.<\/span> length <span style=\"color: #eedd82;\">.<\/span> findDivisors) triangleNumbers<\/pre><\/div><p>and you will see it hangs, never returning<\/p><\/div><\/div><div id=\"outline-container-ArticleContentEx13\" class=\"outline-4\"><h4 id=\"ArticleContentEx13\"><a id=\"sec-\" name=\"sec-\"><\/a>Large Sum<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx13\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=13\">Large Sum<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L103-209&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L57-L159&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx14\" class=\"outline-4\"><h4 id=\"ArticleContentEx14\"><a id=\"sec-\" name=\"sec-\"><\/a>Largest Collatz Sequesnce<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx14\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=14\">Largest Collatz Sequnece<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L211-229&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L161-L192&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx15\" class=\"outline-4\"><h4 id=\"ArticleContentEx15\"><a id=\"sec-\" name=\"sec-\"><\/a>Lattice Path<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx15\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=15\">Lattice Path<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L231-239&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L194-L197&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx16\" class=\"outline-4\"><h4 id=\"ArticleContentEx16\"><a id=\"sec-\" name=\"sec-\"><\/a>Power Digit Sum<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx16\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=16\">Power Digit Sum<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L241-244&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L199-L200&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx17\" class=\"outline-4\"><h4 id=\"ArticleContentEx17\"><a id=\"sec-\" name=\"sec-\"><\/a>Number Letter Count<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx17\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=17\">Number Letter Count<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L246-324&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L202-L238&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx18\" class=\"outline-4\"><h4 id=\"ArticleContentEx18\"><a id=\"sec-\" name=\"sec-\"><\/a>Maximum Path Sum I<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx18\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=18\">Maximum Path Sum I<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L326-354&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L240-L262&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx19\" class=\"outline-4\"><h4 id=\"ArticleContentEx19\"><a id=\"sec-\" name=\"sec-\"><\/a>Counting Sundays<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx19\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=19\">Counting Sundays<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L356-390&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L264-L295&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx20\" class=\"outline-4\"><h4 id=\"ArticleContentEx20\"><a id=\"sec-\" name=\"sec-\"><\/a>Factorial Digit Sum<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx20\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=20\">Factorial Digit Sum<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler2.hs%23L392-395&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler2.scala%23L296-L302&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>learning never ends 😃<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Project Euler 1-10<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-20 Mon><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p><a href=\"https:\/\/projecteuler.net\/\">Project Euler<\/a> is a collection of math based problems you can try tosolve using a programming language, for fun.<\/p><img src=\"https:\/\/i.pinimg.com\/originals\/e5\/49\/15\/e54915681f932a51e33bb1dec210faba.jpg\" style=\"width:50%; max-height: 300px; margin-bottom: 1em;\"><\/img><p>Therefore, I decided to start doing some of them, using Haskell as alanguage. That\'s quite similar to other site like <a href=\"https:\/\/www.hackerrank.com\/\">HackerRank<\/a>. Youmight want to look at them if you are searching a way to trainyourself on a specific language, plus learn some math in theprocess.<\/p><p><b>Edit:<\/b> <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2021-08-28 Sat><\/span><\/span>Added Scala 3 solutions<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-ArticleContentEx1\" class=\"outline-4\"><h4 id=\"ArticleContentEx1\"><a id=\"sec-\" name=\"sec-\"><\/a>Multiple of 3 and 5<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx1\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=1\">Multiples of 3 and 5<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L5-7&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L5-L8&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx2\" class=\"outline-4\"><h4 id=\"ArticleContentEx2\"><a id=\"sec-\" name=\"sec-\"><\/a>Even Fibonacci Numbers<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx2\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=2\">Even Fibonacci Numbers<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L9-13&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L10-L16&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx3\" class=\"outline-4\"><h4 id=\"ArticleContentEx3\"><a id=\"sec-\" name=\"sec-\"><\/a>Large Prime Factor<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx3\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=3\">Largest Prime Factor<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L15-30&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L18-L27&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx4\" class=\"outline-4\"><h4 id=\"ArticleContentEx4\"><a id=\"sec-\" name=\"sec-\"><\/a>Largest Palindrome Product<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx4\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=4\">Largest Palindrome Product<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L32-37&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L29-L31&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx5\" class=\"outline-4\"><h4 id=\"ArticleContentEx5\"><a id=\"sec-\" name=\"sec-\"><\/a>Smallest Multiple<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx5\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=5\">Smallest Multiple<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L39-41&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L33-L34&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx6\" class=\"outline-4\"><h4 id=\"ArticleContentEx6\"><a id=\"sec-\" name=\"sec-\"><\/a>Sum Square Difference<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx6\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=6\">Sum Square Difference<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L43-49&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L36-L39&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx7\" class=\"outline-4\"><h4 id=\"ArticleContentEx7\"><a id=\"sec-\" name=\"sec-\"><\/a>10001st prime<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx7\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=7\">10001st prime<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L51-53&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L41-L46&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx8\" class=\"outline-4\"><h4 id=\"ArticleContentEx8\"><a id=\"sec-\" name=\"sec-\"><\/a>Largest product in a series<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx8\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=8\">Largest product in a series<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L55-67&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L48-L51&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx9\" class=\"outline-4\"><h4 id=\"ArticleContentEx9\"><a id=\"sec-\" name=\"sec-\"><\/a>Special Pythagorean triplet<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx9\"><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=9\">Special Pythagorean triplet<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L69-80&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L53-L62&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><div id=\"outline-container-ArticleContentEx10\" class=\"outline-4\"><h4 id=\"ArticleContentEx10\"><a id=\"sec-\" name=\"sec-\"><\/a>Summation of Primes<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentEx10\"><p>This one never terminates. I searched for a solution on the web,but they provide the simplistic solution. Plus, <code>GHCI<\/code> doesn\'t seemultiple cores.<\/p><p>Problem: <a href=\"https:\/\/projecteuler.net\/problem=10\">Summation of primes<\/a><\/p><p>Solution Haskell:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerhaskell%2Fsrc%2FProjectEuler.hs%23L90-92&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><p>Solution Scala:<\/p><script src=\"https:\/\/emgithub.com\/embed.js?target=https%3A%2F%2Fgithub.com%2Fbenkio%2FGeneralExercises%2Fblob%2Fmaster%2FProjectEuler%2Fprojecteulerscala%2Fsrc%2Fmain%2Fscala%2FProjectEuler.scala%23L64-L71&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on\"><\/script><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>Doing the exercises in <code>Scala<\/code> I encountered some problems using<code>Long<\/code> and ranges. So, some solutions are not ideal, but themworks. Here the (frustration) <a href=\"https:\/\/twitter.com\/benkio89\/status\/1431546742953152516\">tweet<\/a> as a referencelearning never ends<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Gilded Rose Kata<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-06-30 Tue><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>Not so long ago. I watched the mighty <a href=\"https:\/\/twitter.com\/NicolasRinaudo\">Nicolas Rinaudo<\/a> dealing withthe <a href=\"https:\/\/github.com\/emilybache\/GildedRose-Refactoring-Kata\">Gilded Rose Kata<\/a> on the <a href=\"https:\/\/www.twitch.tv\/scalalove\">Scalalove\'s twitch channel<\/a>, and thatinspired me into trying it out using my favorite languages: Scala &Haskell.<\/p><p>I\'ll structure the article in this way:<\/p><ol class=\"org-ol\"><li>I get familiar with the actual code-base, analyze it, andexplain what are the (main) flaws I can see.<\/li><li>Following the previous point, I write down the approach I\'lluse.<\/li><li>Execution time with a brief log journal of what was done.<\/li><li>Then, It\'s time to point out the difficulties I have found withboth Scala and Haskell and I will try to do a comparison between thetwo.<\/li><\/ol><p>Both points 1 and 2 will be language-independent<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><\/div><div id=\"outline-container-CodebaseStateContent\" class=\"outline-4\"><h4 id=\"CodebaseStateContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Codebase State<\/h4><div class=\"outline-text-4\" id=\"text-CodebaseStateContent\"><p>In order to write this section, I just need to access the <a href=\"https:\/\/github.com\/emilybache\/GildedRose-Refactoring-Kata\">Github katarepo<\/a> and do a good, old, code review. I will take Scala as areference since I feel more comfortable with the technology.<\/p><p><b>Disclaimer:<\/b> this code is intentionally bad. If you will everencounter a code-base like this one in your life, listen to me…<\/p><div class=\"video-container\"><iframe src=\"https:\/\/giphy.com\/embed\/A6PcmRqkyMOBy\" frameBorder=\"0\" class=\"giphy-embed responsive-iframe\" allowFullScreen><\/iframe><\/div><p>let\'s start…<\/p><p>First thing first, I see there are no tests. That needs to befixed ASAP. We will talk about that in the <a href=\"#SolutionApproachTestsContent\">specific section<\/a>.<\/p><p>Then, I see an attempt of a <i><a href=\"https:\/\/en.wikipedia.org\/wiki\/Domain_model\">Domain Model<\/a><\/i>. That\'s actually a goodpractice: design your types first and be as picky as possible inorder to <a href=\"https:\/\/fsharpforfunandprofit.com\/posts\/designing-with-types-making-illegal-states-unrepresentable\/\">make the illegal states unrepresentable<\/a>. <\/p><p>Moving on to The problems I found doing the code review:are several<\/p><ul class=\"org-ul\"><li>The <code>var<\/code> keyword, that means mutability. As a software engineer, Iknow that mutable state can be hard to manage and lead to unintendedbehaviours. Doing a quick sneak peek to the Haskell code I seethere\'s none. Mutation is possible in Haskell too, but luckily we gotspared this time. I will talk a little bit more about mutationin the <a href=\"#LaguagesComparisonConclusions\">Languages Comparison Section<\/a>.<\/li><li>Just Item Type!! There are two possible cases for this: the problem isextremely simple or we are missing some types here.<\/li><li>No validations. I expect to see some data validation hidingsomewhere in the algorithm. Basically every piece of code has itbecause we need to ensure that the values we are going to manageremains valid throughout the program execution.<\/li><\/ul><p>Regarding the main algorithm, we have:<\/p><ol class=\"org-ol\"><li>One giant function. OK, that is, in the end, what the CPU willexecute: 1 operation at a time(mono-core,single-thread). However, the code needs to behuman-readable. <i>Any fool can write code that a computer can understand. Good programmers write code that humans can understand.<\/i> (Martin Fowler) Plus, it violates the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Single-responsibility_principle\">SingleResponsibility Principle<\/a>.<\/li><li>Mutation again! Apart from the items fields, that was expected,there\'s mutation regarding the index variable <code>i<\/code>. Luckily, it is justused to select the current element to evaluate, it could havebeen way worse, like a different algorithm for odd and evenelements, checks on the index, and so on. 😖<\/li><li>There should be Types and a compiler somewhere, but I don\'t seeany usage of those in here. The code is nearly indistinguishablefrom Javascript.<\/li><li>Nested Ifs statements. No need to tell you why it is bad.<\/li><\/ol><\/div><\/div><div id=\"outline-container-SolutionApproachContent\" class=\"outline-4\"><h4 id=\"SolutionApproachContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Solution Approach<\/h4><div class=\"outline-text-4\" id=\"text-SolutionApproachContent\"><p>After the code review we know the issues of the code and we cancome up with a plan. Here, I\'ll prioritise the tasks we need to doin order to make this code better and solve the Kata.<\/p><\/div><div id=\"outline-container-SolutionApproachTestsContent\" class=\"outline-5\"><h5 id=\"SolutionApproachTestsContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Tests<\/h5><div class=\"outline-text-5\" id=\"text-SolutionApproachTestsContent\"><p>The first thing needed is a good test suite. With proper testing,we will be sure that the changes we are going to make arecorrect, Meaning that the behavior of the program ispreserved. After all, the definition of <i><a href=\"https:\/\/en.wikipedia.org\/wiki\/Code_refactoring\">Code Refactoring<\/a><\/i> is to<b>NOT<\/b> change the program behavior.<\/p><p>Now, the test suite can be crafted using multiple techniques and it\'s ahuge topic by itself. In this case, testing is not the main focus ofthe kata but is still a key point. Fortunately, it\'s a simple, little, localprogram. So we should be fine with just <i><a href=\"https:\/\/en.wikipedia.org\/wiki\/Unit_testing\">Unit Testing<\/a><\/i> and <i><a href=\"https:\/\/en.wikipedia.org\/wiki\/System_testing\">System Testing<\/a><\/i>.<\/p><p>Usually, testing is done by:<\/p><ul class=\"org-ul\"><li>Providing some specific input data to the program.<\/li><li>Fetch the result of the computation.<\/li><li>Compare it with what is expected.<\/li><\/ul><p>That\'s completely fine and it\'s what the majority of thecompanies do nowadays. The downside of this approach is: it\'sstatic. Let say your program has a bug and will crash underspecific conditions, for a corner case you didn\'t think about.Testing by example will not help you there, if you didn\'t catchthat specific case beforehand.<\/p><p>A different approach I like more is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Property_testing\">Property Testing<\/a>, where theinput data is <b>generated<\/b>. The software engineer defines theinput generation rules, used by the supporting library\/framework,and then the expected behavior as a property. I won\'t go deeperinto it, but the typical example is the <i>associativity law<\/i> ofthe sum. Definitely, the most complicated part is to identify theproperties of your program, but in my honest opinion, I still seebenefits in just take advantage of the generated inputs andhaving a \"normal\" test built on top. Having to write your testonce and run them multiple times with several inputs, it\'sstraight away a big plus. Used this way, we probably can\'t talkabout <i>Property<\/i> testing anymore, but I can\'t think of a properterm, if you know it, please tell me.<\/p><p>The biggest downside of this approach is the computational cost:generating inputs and having multiple runs will increase thetesting time. In fact, if you mess up the way you generate yourinput, you couldn\'t even be able to run your test at all,obtaining the \"infinite loop effect\" at the time ofexecution. For example, I learnt by experience that, operatorslike <code>suchAs<\/code> must be used very carefully.<\/p><p><b><b>Edit:<\/b><\/b> Since we have even the specs of the program theproperty base testing fits super nicely: convert phrases like<\/p><p><i>“Sulfuras”, being a legendary item, never has to be sold ordecreases in Quality<\/i><\/p><p>to a property becomes quite a natural process.<\/p><\/div><\/div><div id=\"outline-container-SolutionApproachMutationContent\" class=\"outline-5\"><h5 id=\"SolutionApproachMutationContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Dealing With Mutation once and for All<\/h5><div class=\"outline-text-5\" id=\"text-SolutionApproachMutationContent\"><p>Everything is in its own box, each one returning a value 💜,However… it still mutates and internal field!! 😞<\/p><p>Anyway, due to the previous steps, we can easily remove thatmutation from the algorithm and the model itself, returning a newcopy of the input with the requested changes.<\/p><p><b><b>Edit:<\/b><\/b> Reading the specification I found I\'m not allowed to touch the <code>Item<\/code> class, unfortunately. Then, what we can do is to add other types around the main one, those will helps us to structure our code and give to each bit of logic its own context. A good Idea could be to add an internal representation of the <code>Item<\/code>, not visible from outside. Doing so will add the conversion overhead from <code>Item<\/code> to the internal type, but we are the owner of it, so it can change and be immutable. Basically, we are going to decouple ourselves from the foreign <code>Item<\/code> type.<\/p><\/div><\/div><div id=\"outline-container-SolutionApproachRefinementNewFeatureContent\" class=\"outline-5\"><h5 id=\"SolutionApproachRefinementNewFeatureContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Refinement & New feature<\/h5><div class=\"outline-text-5\" id=\"text-SolutionApproachRefinementNewFeatureContent\"><p>Finally, with this new working codebase, we should be able to:<\/p><ul class=\"org-ul\"><li>Merging conditions together.<\/li><li>Adding fields validation or other helpful functions,<\/li><li>Adding new types.<\/li><li>Structuring the code: moving the functions to the model companionobjects, in case of Scala, or to a separate module.<\/li><\/ul><p>Once the code is finally in good shape, we can:<\/p><ol class=\"org-ol\"><li>See some pattern emerge clearly and properly design the restof the code to expose them: moving the logic to proper newtypes that communicate the intention of the code.<\/li><li>easily add the new feature. It should be quite simple sincethe new item to evaluate, require a logic that is already sharedwith another item.<\/li><\/ol><p>Now that we have a plan, let\'s start the fun part…let\'sexecuted it!<\/p><p><b><b>Edit:<\/b><\/b> Sometimes, you need to do some refinements listed herein earlier stages. That\'s because, it helps you a lot ratherthen postpone the change. Anyway, my suggestion is to refine aslittle as possible in the beginning, only when it\'s really necessary.<\/p><\/div><\/div><\/div><div id=\"outline-container-ScalaContent\" class=\"outline-4\"><h4 id=\"ScalaContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Scala Kata<\/h4><div class=\"outline-text-4\" id=\"text-ScalaContent\"><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-02 Thu><\/span><\/span>: Started the scala exercise from testing and<a href=\"https:\/\/www.scalacheck.org\/\">Scalacheck<\/a>. Get stuck on some test corner case, but with some<code>println<\/code> I figured it out. <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/8e066e5cc678518f6f10147f7280969dce245be2\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-03 Fri><\/span><\/span>: Finished Scala\'s tests. Fixed some errors inthe making. Now we can move to the actual code. <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/19c8b9e\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-04 Sat><\/span><\/span>: Split the monster. Now, I have severalfunctions, returning Items containing only one if statement asa body. No more two level if indentation anywhere. Plus:<\/p><ul class=\"org-ul\"><li>Introduced new types wrapper and aliases for the fields<\/li><li>Defer\/remove mutation as much as possible. Only at the end ofthe <code>foreach<\/code>.<\/li><li>Extracted main operation on types to companion objects.<\/li><li>Merged the functions into one calling only basic operations<\/li><\/ul><p><a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/9fdc7be\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-05 Sun><\/span><\/span>: Changed the return type of the <code>updateQuality<\/code>to be the new value. In this way, we avoid mutation of the itemarray. After that, we can see the pattern composed by apre-quality calculation, an increase in sellIn value, a finaladjustment. Then, we can create subtypes of the item class and putthe specific logic into each class. This way we can get rid of thebig remaining functions. Afterward, we can add the new class forthe new item easily. Work is done! <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/03ac121\">commit<\/a><\/p><\/div><\/div><div id=\"outline-container-HaskellContent\" class=\"outline-4\"><h4 id=\"HaskellContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Haskell Kata<\/h4><div class=\"outline-text-4\" id=\"text-HaskellContent\"><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-08 Wed><\/span><\/span>: Started the tests: Sulfuras + AgedBrie. <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/ea0b859\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-13 Mon><\/span><\/span>: Added the BackstagePasses test + testrefactoring <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/8a9a98b\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-14 Tue><\/span><\/span>: Tests completed. <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/f19fa11\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-15 Wed><\/span><\/span>: Started to split the monster. AddedHlint, you can\'t imagine how many <i>redundant brackets<\/i> I removed. <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/acce770\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-16 Thu><\/span><\/span>: Add a <code>newtype<\/code> for the quality field, add thespecial item type, setup the <code>HasQuality<\/code> typeclass, merged iffunctions together into a guard function. <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/89db36e\">commit<\/a><\/p><p><span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-07-16 Thu><\/span><\/span>:<\/p><ul class=\"org-ul\"><li>Implemented typeclass instances and used instead of plainfunctions<\/li><li>Moved all the logic about items in a separate file<\/li><li>Finished the refactoring<\/li><li>Added the new special item Conjured<\/li><li>Added the new test<\/li><li>Implemented the type class instances<\/li><\/ul><p><b>KATA DONE<\/b> <a href=\"https:\/\/github.com\/benkio\/GeneralExercises\/commit\/9205f58\">commit<\/a><\/p><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><\/div><div id=\"outline-container-MainChallengesConclusion\" class=\"outline-4\"><h4 id=\"MainChallengesConclusion\"><a id=\"sec-\" name=\"sec-\"><\/a>Main Challenges<\/h4><div class=\"outline-text-4\" id=\"text-MainChallengesConclusion\"><p>The first challenge that comes into my mind is testcrafting. During this kata you really understand the importance ofhaving a very well done test code. Most of the time we think testcode is B-class code, but this kata shows it\'s not. It needs to bevery fine-tuned and precise in order to catch bugs ahead oftime. Plus, test-code is code you need to maintain and writedaily, to add your features. That makes it at the same level ofimportance as the production code.<\/p><p>I agree, it\'s not great fun to write such code, that\'s why Isuggest to use the compiler as much as possible to reduce thedesign space and the amount of tests you need to write to coveryour application. If a behavior is impossible, you don\'t need toadd a test for it.<\/p><p>A second relevant challenge is about beingdisciplined. Refactoring should be a sequence of very smallchanges, but this is not always possible. Sometimes, you need tobreak quite some amount of code at once, but that must be anexception to the regular refactoring process. Said from a guy who usually opens refactoring PRs of hundreds of files. 🙊 😬<\/p><p>What can happen is that we might see multiple applicableimprovements at the same time. Therefore, we are tempted to applythem all at once, instead, we should hold back and do one single,small, consistent change, commit it, and then move on to thenext. This requires great self-discipline.<\/p><p>Finally, the last challenge, is definitely <i>dependencies<\/i>. Whenyou have constraints from different libraries or legacy code thatyou are not allowed to change, it makes things very difficult andrequires you to put workarounds in place. That defiles the goal ofrefactoring itself. This shows the importance of keeping projectssmall, self-contained and decoupled from each other as much aspossible, with the minimal set of dependencies as possible.<\/p><\/div><\/div><div id=\"outline-container-LaguagesComparisonConclusions\" class=\"outline-4\"><h4 id=\"LaguagesComparisonConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Languages Comparison<\/h4><div class=\"outline-text-4\" id=\"text-LaguagesComparisonConclusions\"><p>Before diving into the differences I found between the twotechnologies, I feel the need to do a little disclaimer. What Iwill write is a personal opinion, after all, it\'s not based onfacts, but on personal experience. Therefore, it depends on: myemotions throughout the exercise, how fluent I am on bothlanguages, and how much I know about the problem (the order inwhich I solved the Kata might have influenced me). Therefore,what\'s following may not apply to you and you disagree, that\'scompletely fine. Probably this disclaimer should have been placedat the very start of this article 😺.<\/p><p>One of the first differences I felt, when dealing with tests inHaskell, was that I was pushed way more into abstracting thingscompared to Scala and I might figure out why.<\/p><p>In Scala, if a test fails, what I usually do to debug it is:putting a break point or a <code>println<\/code> and see what is the input tothe test, the result an so on. Once I know what is going on, it\'susually an easy fix: the generator wasn\'t right, the successcondition wasn\'t correct etc… and that\'s it.<\/p><p>This approach doesn\'t hold in Haskell because you can\'t put asimple <code>println<\/code> into the tests. You know, <a href=\"https:\/\/www.youtube.com\/watch?v=fCoQb-zqYDI\">IO Monad<\/a> andstuff. So, you need to work differently and, as a result, you endup with better code. Let\'s see why.<\/p><p>The only way to inspect your test I know in Haskell is to use theREPL. When you get into it, you want to be able to execute exactlythe bit of code you are interested in, the minimal amount of codewhere the bug might hide. To achieve this goal, you need toextract the logic of your tests into small functions and enhancethe re-usability as much as possible instead of just care aboutfixing the single test.<\/p><p>This should always be the case, for whatever language you areusing, but I strongly believe that, if something is not forced bythe technology and the responsibility of \"doing the right thing\"is on the shoulder of the developer, laziness and negligence willeventually kick in and drive it to a sub-optimal result.<\/p><p>Moving on, the next difference, we can see from the initialcodebase status that Haskell comes with immutability straightaway. Even if it doesn\'t seem very important, this is a big plus,especially when things scale up in size. Then, you could achievemutation in Haskell as well using specific constructions like <a href=\"https:\/\/www.oreilly.com\/library\/view\/parallel-and-concurrent\/9781449335939\/ch07.html\">MVar<\/a>or <a href=\"https:\/\/wiki.haskell.org\/Software_transactional_memory\">STM<\/a> (used mainly for concurrency purposes), but even then,mutation is enclosed and accessible under a specific contractwhere particular constraints hold. Having immutability as adefault saves you time, since we can skip a step in ourrefactoring plan.<\/p><p>Moreover, I found the starting impact of the refactoring lessscary than the Scala one. I guess the reason is because Ialready know the context, but I was able to give a meaningful nameto the extracted functions straight away because of the usage ofthe <code>where<\/code> and <code>let-in<\/code> constructs.<\/p><hr\/><p>let me open a small parenthesis in here<\/p><div class=\"org-src-container\"><pre class=\"src src-haskell\"> src<span style=\"color: #eedd82;\">\/<\/span>GildedRose.hs<span style=\"color: #98fb98;\">:<\/span>53<span style=\"color: #98fb98;\">:<\/span>29<span style=\"color: #98fb98;\">:<\/span> <span style=\"color: #98fb98;\">Warning:<\/span> <span style=\"color: #98fb98;\">Redundant<\/span> <span style=\"color: #00ffff;\">if<\/span><span style=\"color: #98fb98;\">Found<\/span><span style=\"color: #87cefa;\">:<\/span> <span style=\"color: #00ffff;\">if<\/span> sellIn <span style=\"color: #eedd82;\"><<\/span> 6 <span style=\"color: #00ffff;\">then<\/span> <span style=\"color: #00ffff;\">if<\/span> quality <span style=\"color: #eedd82;\"><<\/span> 48 <span style=\"color: #00ffff;\">then<\/span> 1 <span style=\"color: #00ffff;\">else<\/span> 0 <span style=\"color: #00ffff;\">else<\/span> 0<span style=\"color: #98fb98;\">Perhaps<\/span><span style=\"color: #87cefa;\">:<\/span> <span style=\"color: #00ffff;\">if<\/span> (sellIn <span style=\"color: #eedd82;\"><<\/span> 6) <span style=\"color: #eedd82;\">&&<\/span> (quality <span style=\"color: #eedd82;\"><<\/span> 48) <span style=\"color: #00ffff;\">then<\/span> 1 <span style=\"color: #00ffff;\">else<\/span> 0<\/pre><\/div><p><b><a href=\"https:\/\/github.com\/ndmitchell\/hlint\">Hlint<\/a> is cheating in this exercise<\/b> and it works only becauseeach redundant ifs has to return a value. Scala also has similartools, like <a href=\"https:\/\/github.com\/scalacenter\/scalafix\">Scalafix<\/a>, but it slipped out of my mind. With it, Iwould have struggled less.<\/p><hr\/><p>Finally, a downside of plain Haskell, in comparison to Scala, isthe amount of machinery you have to create in order to do simplethings, such as just access fields. Don\'t get me wrong here, I\'msure that through some specific compiler extension, or using<a href=\"http:\/\/wiki.haskell.org\/Template_Haskell\">Template Haskell<\/a>, a cleaver\/experienced haskeller can easilyovercome this limitation, auto-generating instances forexample. Anyway, I\'m not a great Haskell developer and I justwanted to compare the two languages out of the box, with minimaloverhead possible. A possible justification I can think of is thatHaskell prefers to keep a minimal core, and then it allow you tointegrate the language using externalmodules\/extensions\/libraries. As a result, it\'s often told thatCommercial Haskell is far different from the plain language, dueto the high amount of pieces involved that completely turnupside-down the syntax. As a reference to it I link a proper bookdiscussing <a href=\"https:\/\/github.com\/sdiehl\/wiwinwlh\">What I Wish I Knew When Learning Haskell<\/a>. I hope tofind the time to read this one day.<\/p><\/div><\/div><div id=\"outline-container-LaguagesComparisonConclusions\" class=\"outline-4\"><h4 id=\"LaguagesComparisonConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Final words<\/h4><div class=\"outline-text-4\" id=\"text-LaguagesComparisonConclusions\"><p>To be completely honest, I must say that most of the concepts Ishowed here are quite old and you can find them into details fromseveral famous books, such as <a href=\"https:\/\/martinfowler.com\/books\/refactoring.html\">Refactoring by Martin Fowler<\/a>.<\/p><p>Anyway, you can see here the application of some of thoseprinciples, plus what does in means to refactor in a FP oriented language.<\/p><p>I really hope you enjoyed reading this article and that itinspired you into trying this kata, especially if you aren\'tconfident with Refactoring practices. Feel free to contact me ifyou have any feedback.<\/p><p>May the force be with you<\/p><\/div><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Spark Amp Review<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-06-16 Tue><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>A personal compact and simple review of the <a href=\"https:\/\/www.positivegrid.com\/spark\">Positive Grid Spark<\/a>Amp. This will be more a review from my personal point of view,without inspecting feature by feature, but pointing outstrengths, weaknesses and especially personal considerations.<\/p><a href=\'#\' class=\'pop\'><img src=\"https:\/\/s.yimg.com\/uu\/api\/res\/1.2\/SIEC_NxAvIkJg8UERCTJyQ--~B\/aD0xMDAwO3c9MTYwMDtzbT0xO2FwcGlkPXl0YWNoeW9u\/https:\/\/o.aolcdn.com\/images\/dims?resize=2000%2C2000%2Cshrink&image_uri=https:\/\/s.yimg.com\/os\/creatr-uploaded-images\/2019-10\/cddb9c30-f765-11e9-9aef-750a36a00aff&client=a1acac3e1b3290917d92&signature=62b42b0e3fe8343d14186d7ccb05b484d51f35d7\" style=\"width:240px;height:150px;\"><\/img><\/a><a href=\'#\' class=\'pop\'><img src=\"https:\/\/content.invisioncic.com\/w286537\/monthly_2019_10\/gallery-top.jpg.39a628b0b493947d800b5f628afe1635.jpg\" style=\"width:240px;height:150px;\"><\/img><\/a><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><p>The standard guitarist usually is also a gear fanatic and he\'salways in search of something new to try and experiment with, itcan be a new amp, software, guitars, pedals…<\/p><p>So you might think: <i>\"Well this guy is also a nerd, so he should bethe best one when it comes to gear reviews!\"<\/i>. Nothing could be morewrong than that 😅 When it comes to the guitar I\'m the most pragmaticperson, focused on doing exercises and practice, spending no time onstaying up to date with the latest news. Then it makes me the mostunqualified person for the job.<\/p><p>Anyway, I\'ll give you my honest opinion on this amp, and hopefully,you could find it useful. My main suggestion still, is to check outthe Youtube reviews I linked in the <a href=\"#ArticleContentYoutubeLinks\">Other Youtube Reviews linksSection<\/a>. I can tell you I decided to pre-order this amp in late2019 by looking at those first.<\/p><figure><div class=\"video-container\"><iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/mT1lF6Efi1E?rel=0\" allowfullscreen><\/iframe><\/div><figcaption>Positive Grid Spark Unboxing<\/figcaption><\/figure><\/div><div id=\"outline-container-ArticleContentSituationMotivationFirstImpact\" class=\"outline-4\"><h4 id=\"ArticleContentSituationMotivationFirstImpact\"><a id=\"sec-\" name=\"sec-\"><\/a>Situation, Motivation & First Impact<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentSituationMotivationFirstImpact\"><p>Before starting, I want to explain my current situation, so you willunderstand also the follow-up motivations. At the moment, I\'mworking abroad, and that means: away from my hometown, on rent, witha fraction of what I usually have (both in terms of space availableand gear). Plus, I wish to keep my belongings compact because Imight need to move again and again. If you ever had to move to adifferent location you know what I mean. However, you know, youcan\'t stay without playing for longer 😛<\/p><p>Then, I bought a cheap <a href=\"https:\/\/www.jacksonguitars.com\/en-GB\/guitars\/js-series\/js-series-dinky®-arch-top-js22-7-dka-ht\/2910132568.html\">7-string guitar<\/a> (because I never had one, sowhy not), a ~100$ <a href=\"https:\/\/m-audio.com\/m-tracks\/2x2\">external audio interface<\/a>, a small <a href=\"https:\/\/www.blackstaramps.com\/uk\/products\/fly-3\">Blackstar miniamp<\/a> (I got it from 6 months Metalhammer subscription 😎) and a pairof <a href=\"https:\/\/en-uk.sennheiser.com\/monitoring-headphone-studio-headphone-professional-audio-hd-380-pro\">headphones<\/a>. Using the guitar plug-ins on the PC, I was able to:play, record, jam along, and going back to business. In fact, I usedthis setup to record several songs you could find on my Youtubechannel. All is well that ends well, but there\'s always room forimprovement, especially in this case.<\/p><p>I started to see <a href=\"https:\/\/www.positivegrid.com\/spark\">Positive Grid Spark<\/a> commercials, reviews at the endof the last year when they started to sell it in preview. So lookingat the specs I noticed it did exactly what all my current setup didand more. In particular, what attracted me was:<\/p><ul class=\"org-ul\"><li>The ability to record<\/li><li>I can use it as an external speaker for my PC<\/li><li>Its 40W, it\'s always good to have some power 😛<\/li><li>All the amp simulations, available also on-line. Since I\'m a lazyperson, if I can get a good tone without spending time tweakingthis setting or the other, the better.<\/li><li><b>The Price<\/b> Last, but not least! Just ~300$ 😲<\/li><\/ul><p>It also comes with other features, but not very appealing to mehonestly. You can find the details on the <a href=\"https:\/\/www.positivegrid.com\/spark\">amp page<\/a>.<\/p><p>For all those reasons I decided to do me a Christmas gift and Iordered. Unfortunately, It took a long time to arrive, 6 months, dueto the unexpected amount of orders and a little pandemic inbetween. The first impact I had was great, it does exactly what itclaimed. I had some troubles setting it up, with the Bluetoothpairing between the app and the PC, for instance, I discovered it can\'tbe controlled by the phone app (changing the settings) and receivethe audio from the PC at the same time, but apart from these littledetails, I don\'t have any complaints about the sound.<\/p><p>In the next section, I will record some licks and riffs so you canhear the factory presets. I will not apply any post-productioneffect or changes so you can hear the raw sound out of the unititself.<\/p><p>I leave you with a lesson I learned about practice amps: <i>In order tobe good, it should not have the battery supply option!<\/i><\/p><\/div><\/div><div id=\"outline-container-ArticleContentDemos\" class=\"outline-4\"><h4 id=\"ArticleContentDemos\"><a id=\"sec-\" name=\"sec-\"><\/a>Demos<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentDemos\"><\/div><div id=\"outline-container-ArticleContentDemosTechnicalDifficulties\" class=\"outline-5\"><h5 id=\"ArticleContentDemosTechnicalDifficulties\"><a id=\"sec-\" name=\"sec-\"><\/a>Technical Difficulties<\/h5><div class=\"outline-text-5\" id=\"text-ArticleContentDemosTechnicalDifficulties\"><p>Yesterday, I tried recording something specifically for thisreview, but I encountered some technical difficulties. Inparticular, the problem was with the connectivity between the ampand the PC.<\/p><p>Positive Grid provides a specific ASIO driver for the Spark amp,but even if I have that installed, it seems that the USBconnection is not reliable. It often disconnects and wasimpossible to take a recording or jam along. Meanwhile, theBluetooth connection works without any problem.<\/p><p>In the end, I realized that they keep updating the driver. Infact, going to the driver page right now I see <i>Updated:Yesterday<\/i>. Therefore, I just installed the new driver, restartedthe PC and it seemed to work fine.<\/p><p>In the following sections, I will expose the criteria behind thedemos I will record. So you can hear the Spark sound.<\/p><p><b>Edit:<\/b> I just received right now an email telling the sparkusers to update the amp firmware to improve connectivity.<\/p><p><b>Edit:<\/b> <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-06-19 Fri> <\/span><\/span> What still happens is that, when yourecord using USB, the connection is quite unstable. When adisconnection happens, the recording stops and you need tounplug-plug the cable again and exit the DAW and you <b>MAY<\/b> solvethe issue. Otherwise the recording will present problems(clipping and various noises). I add a sample of one of theinterferences<\/p><audio controls> <source src=\"2020-06-16-SparkReview\/RecordingInterference.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element.<\/audio><p>It could also happen that the recording works and then degradeover time. As a result you will hear back the clips\/noises\/interferences.<\/p><\/div><\/div><div id=\"outline-container-ArticleContentDemosTheDemoPlan\" class=\"outline-5\"><h5 id=\"ArticleContentDemosTheDemoPlan\"><a id=\"sec-\" name=\"sec-\"><\/a>The Demo Plan<\/h5><div class=\"outline-text-5\" id=\"text-ArticleContentDemosTheDemoPlan\"><p>The main problem with doing Spark\'s demos is that it canreproduce an infinite variety of sounds you can craft by the appor download from the cloud. Then, I will just restrict the numberof sounds to the factory ones (those you can select by the fistknob on the amp itself).<\/p><p>Moving on, another decision to take is, what to play as a propersample? My idea is to select 3 different licks:<\/p><ul class=\"org-ul\"><li>One for testing clean tones (3)<\/li><li>One for testing crunch\/blues tones (2)<\/li><li>One for testing high gain tones (2)<\/li><\/ul><p>Then, I also want to play the same lick with different magnetsetup:<\/p><ul class=\"org-ul\"><li>Bridge magnet<\/li><li>Neck magnet<\/li><li>Bridge and Neck magnets<\/li><\/ul><p>This strategy will bring at least 21 samples, but I might play alittle more then that 😄. I hope those willgive you quite a clear idea of the amp.<\/p><p>About the sound manipulation, I will not add any mixing\/masteringto the files: I\'ll just click record, and export the results. Soyou will have the exact raw sound files.<\/p><p><b>Edit<\/b>: for space reason I had to convert the wav files to mp3<\/p><\/div><\/div><div id=\"outline-container-ArticleContentDemosRecordings\" class=\"outline-5\"><h5 id=\"ArticleContentDemosRecordings\"><a id=\"sec-\" name=\"sec-\"><\/a>Recordings<\/h5><div class=\"outline-text-5\" id=\"text-ArticleContentDemosRecordings\"><\/div><ul class=\"org-ul\"><li><a id=\"sec-\" name=\"sec-\"><\/a>Acoustic<br ><div class=\"outline-text-6\" id=\"text-\"><p>This tone is probably reserved for the acustic guitar since sparkpromote himself as an amp for that as well.<\/p> <table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorAcusticBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorAcusticBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorAcusticNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Cemetery Gates<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesAcusticBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesAcusticBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesAcusticNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Mutter<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterAcusticBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterAcusticBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterAcusticNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody><\/table><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>Bass<br ><div class=\"outline-text-6\" id=\"text-\"><p>This tone is probably reserved for the Bass guitar since sparkpromote himself as an amp for that as well.<\/p> <table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorBassBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorBassBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorBassNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Cemetery Gates<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesBassBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesBassBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesBassNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Mutter<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterBassBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterBassBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterBassNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody><\/table><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>Clean<br ><div class=\"outline-text-6\" id=\"text-\"> <table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorCleanBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorCleanBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorCleanNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Cemetery Gates<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesCleanBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesCleanBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesCleanNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Mutter<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterCleanBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterCleanBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/MutterCleanNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody> <\/table> <p>Dynamic Response - Bridge & Neck Pickups<\/p> <audio controls> <source src=\"2020-06-16-SparkReview\/DynamicResponseCleanBridgeNNeck.mp3\" type=\"audio\/mp3\">Your browser does not support the audio element. <\/audio><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>Glassy<br ><div class=\"outline-text-6\" id=\"text-\"><p>For glassy and crunch I took as example the following two licksfrom Danny Page:<\/p> <figure><div class=\"video-container\"> <iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/UVepVNuitvw\" allowfullscreen><\/iframe><\/div> <figcaption> Lick Friday 247 <\/figcaption> <\/figure> <figure><div class=\"video-container\"> <iframe class=\"responsive-iframe\" src=\"https:\/\/www.youtube.com\/embed\/cuhAXtjgTos\" allowfullscreen><\/iframe><\/div> <figcaption> Lick Friday 250 <\/figcaption> <\/figure> <table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorGlassyBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorGlassyBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorGlassyNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\"><a href=\"https:\/\/youtu.be\/UVepVNuitvw\">Lick 247<\/a><\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick247GlassyBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick247GlassyBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick247GlassyNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\"><a href=\"https:\/\/youtu.be\/cuhAXtjgTos\">Lick 250<\/a><\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick250GlassyBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick250GlassyBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick250GlassyNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody><\/table><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>Crunch<br ><div class=\"outline-text-6\" id=\"text-\"> <table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorCrunchBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorCrunchBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorCrunchNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\"><a href=\"https:\/\/youtu.be\/UVepVNuitvw\">Lick 247<\/a><\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick247CrunchBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick247CrunchBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick247CrunchNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\"><a href=\"https:\/\/youtu.be\/cuhAXtjgTos\">Lick 250<\/a><\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick250CrunchBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick250CrunchBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/Lick250CrunchNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody><\/table><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>High Gain<br ><div class=\"outline-text-6\" id=\"text-\"> <table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorHighGainBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorHighGainBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorHighGainNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">New Level<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/NewLevelHighGainBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/NewLevelHighGainBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/NewLevelHighGainNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Cemetery Gates<\/th> <td colspan=\"3\"> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesHighGainBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody><\/table><\/div><\/li><li><a id=\"sec-\" name=\"sec-\"><\/a>Metal<br ><div class=\"outline-text-6\" id=\"text-\"><table class=\"table table-dark\"> <thead> <tr> <th scope=\"col\">Lick<\/th> <th scope=\"col\">Bridge Pickup<\/th> <th scope=\"col\">Middle Position Pickup<\/th> <th scope=\"col\">Neck Pickup<\/th> <\/tr> <\/thead> <tbody> <tr> <th scope=\"row\">C Major Scale<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorMetalBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorMetalBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CMajorMetalNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">A New Level<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/NewLevelMetalBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/NewLevelMetalBridgeNNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/NewLevelMetalNeck.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <tr> <th scope=\"row\">Cemetery Gates<\/th> <td> <audio controls> <source src=\"2020-06-16-SparkReview\/CemeteryGatesMetalBridge.mp3\" type=\"audio\/mp3\"> Your browser does not support the audio element. <\/audio> <\/td> <\/tr> <\/tbody><\/table><\/div><\/li><\/ul><\/div><\/div><div id=\"outline-container-ArticleContentYoutubeLinks\" class=\"outline-4\"><h4 id=\"ArticleContentYoutubeLinks\"><a id=\"sec-\" name=\"sec-\"><\/a>Other Youtube Reviews links<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentYoutubeLinks\"><ul class=\"org-ul\"><li><a href=\"https:\/\/youtu.be\/-BRU7Hd_3dI\">Review by Tom Quayle<\/a><\/li><li><a href=\"https:\/\/youtu.be\/FubvySS-Xo8\">Review by Sophie Burrell<\/a><\/li><li><a href=\"https:\/\/youtu.be\/tbSrPRI4rXM\">Review by Fluff (Riffs, Beards & Gear)<\/a><\/li><li><a href=\"https:\/\/youtu.be\/6Y3zYsLfFGw\">Beat It by Kfir Ochaion<\/a><\/li><\/ul><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>The final judgment on the amp can\'t be anything else then a positiveone. For such a price, I really doubt you could find a product thatgives you so much flexibility in terms of tones and features(recording, playback, connectivity..).<\/p><p>I\'m not saying it\'s perfect, since the technical issues I found whilerecording are particularly annoying. Plus I didn\'t had the chance torecord a full song yet. I really hope it\'s just a software issue andthat it could be resolved with future releases of the ASIO driver.<\/p><p>In summary, If you are looking into a practice amp that is cheap,flexible and sounds great, this is definitely awesome.<\/p><p>Of course, don\'t hesitate into give me some feedback: if you havethe amp, what do you think about it, if you liked the articleetc. you can find me from the links into the main page 😉. Also, Imight come back here and update this article with new content in thefuture<\/p><\/div><\/div><\/div>","<div id=\"outline-container-Article\" class=\"outline-2\"><h2 id=\"Article\"><a id=\"sec-\" name=\"sec-\"><\/a>Static Personal Website with Org Mode<\/h2><div class=\"outline-text-2\" id=\"text-Article\"><p><b>Created: <span class=\"timestamp-wrapper\"><span class=\"timestamp\"><2020-06-11 Thu><\/span><\/span><\/b><\/p><\/div><div id=\"outline-container-ArticleAbstract\" class=\"outline-3\"><h3 id=\"ArticleAbstract\"><a id=\"sec-\" name=\"sec-\"><\/a>Abstract<\/h3><div class=\"outline-text-3\" id=\"text-ArticleAbstract\"><p>In this article I will show you how I built this site using:<\/p><ul class=\"org-ul\"><li>Org-mode<\/li><li>Bootstrap (<a href=\"https:\/\/github.com\/marsmining\/ox-twbs\">ox-twbs<\/a>)<\/li><li>HTML(autogenerated) + CSS(minimal) + Javascript (<a href=\"https:\/\/learn.jquery.com\/\">JQuery<\/a>)<\/li><\/ul><p>You can find the source code of this madness in <a href=\"https:\/\/github.com\/benkio\/benkio.github.io\">here<\/a>.<\/p><p>As you can see it is hosted into Github pages, therefore it\'s astatic webpage. However, we can get some backendfeatures using Org and Elisp. Of course, it\'s anything like aproject who has a real backend available, and some times we wouldneed to do some proper hacks.<\/p><p>Keep reading to discover the pitfalls and advantages of this approach.<\/p><\/div><\/div><div id=\"outline-container-ArticleContent\" class=\"outline-3\"><h3 id=\"ArticleContent\"><a id=\"sec-\" name=\"sec-\"><\/a>Content<\/h3><div class=\"outline-text-3\" id=\"text-ArticleContent\"><p>In this section, I will run you through the main features of the site, insertinglinks to the code where necessary.<\/p><\/div><div id=\"outline-container-ArticleContentFeatures\" class=\"outline-4\"><h4 id=\"ArticleContentFeatures\"><a id=\"sec-\" name=\"sec-\"><\/a>Features<\/h4><div class=\"outline-text-4\" id=\"text-ArticleContentFeatures\"><\/div><div id=\"outline-container-ArticleContentFeaturesHTML\" class=\"outline-5\"><h5 id=\"ArticleContentFeaturesHTML\"><a id=\"sec-\" name=\"sec-\"><\/a>Autogenerated HTML and Org-mode typing comfort<\/h5><div class=\"outline-text-5\" id=\"text-ArticleContentFeaturesHTML\"><p>One of the biggest advantages of choosing org-mode as atechnology is that you will get access to the comfort of writinga blog using a markdown language. Then, it looks clean and comeswith a lot of features (integrated into Emacs):<\/p><ul class=\"org-ul\"><li>It\'s easy to manipulate: tables, dates, links<\/li><li>Organize your text in sections<\/li><li>Create lists like this one and so on<\/li><\/ul><p>Basically you can do what you could do in a <code>.md<\/code> file andmore.<\/p><p>That\'s great!!! But what do you want at the end of the day is thewell-known trinity: HTML, CSS, JS. So, we need a way to generateat least HTML from that. Luckily, org-mode comes with a nativeexport system to HTML, with its own CSS too! However, the outcomeis far from being <i>modern<\/i>.<\/p><p>How to make things better? For sure I don\'t want to mess up withthe CSS: I\'m not a frontend dev, plus I learned a long time ago thepain of web designing. The only other option is to <b>steal it<\/b>!Fortunately, I have a go-to option: <span class=\"underline\">Bootstrap<\/span>.<\/p><p>Anyway, Bootstrap comes with its own set of rules and structure.Aligning the native autogenerated HTML code to that is stillvery hard, but apparently someone else had my same idea and hecreated an org extension that generates a Bootstrap-friendly HTMLoutput. This extension is called <a href=\"https:\/\/github.com\/marsmining\/ox-twbs\">ox-twbs<\/a> and it provides acommand (<code>org-twbs-export-to-html<\/code>) to compile the org to HTML.<\/p><p>In this way I\'m writing Org text right now and what you see it\'sthe final result. I got the responsiveness and all the styling(almost) for free, icons included (+ <a href=\"https:\/\/fontawesome.com\/\">fontawesome<\/a> for the homepage).<\/p><\/div><\/div><div id=\"outline-container-ArticleContentFeaturesDynamic\" class=\"outline-5\"><h5 id=\"ArticleContentFeaturesDynamic\"><a id=\"sec-\" name=\"sec-\"><\/a>Article List and Blog Dynamic Content<\/h5><div class=\"outline-text-5\" id=\"text-ArticleContentFeaturesDynamic\"><p>I already told you that this site is static, then how can weachieve some dynamic behavior?<\/p><p>The idea here is to take advantage of the org compilation toexecute some Elisp code (call it server-side if you wish). Thiscode will create some Js to be embedded into the page itself toserve as data. Later, on the javascript client-side, we will usethose variables to render the data into the page.<\/p><p>Using this method we can access other HTML files, such as thisarticle page, manipulate their content, and generate our data forthe targeted page. Two examples are The <a href=\"https:\/\/benkio.github.io\/blog.html\">Blog<\/a> page and the <a href=\"https:\/\/benkio.github.io\/articleList.html\">ArticleList<\/a> page.<\/p><p>Both occasions, there are two Js arrays, called<code>htmlArticles<\/code> and <code>htmlArticlesPaths<\/code>. The former contains asubset of the HTML structure (Title and Abstract) of theArticles, and the latter contains the relative path of them.Starting from those arrays we can zip it and create the list ofarticles: minimal in the case of the <a href=\"https:\/\/benkio.github.io\/articleList.html\">Article List<\/a> page and withsome preview in the case of the <a href=\"https:\/\/benkio.github.io\/blog.html\">Blog<\/a> page.<\/p><p>In addition, in order to achieve some simple sorting, we justneed to store the date into the articles filenames, as you cansee directly from the URL of this page.<\/p><p>What described before is achieved by the Elisp code into the file<a href=\"https:\/\/github.com\/benkio\/benkio.github.io\/blob\/master\/templates.org\">templates.org<\/a>(omitted for brevity) and then integrated into thepage in this way:<\/p><div class=\"org-src-container\"><pre class=\"src src-org\"><span style=\"color: #ff7f24;\">#+html: <ul id=\"pagination\" class=\"pagination pagination-lg\"><\/ul><\/span><\/pre><\/div><p>After that, we need to add some custom js to treat those variables andpopulate the empty article tags:<\/p><p><a id=\"populateArticlesSnippet\" name=\"populateArticlesSnippet\"><\/a><\/p><div class=\"org-src-container\"><pre class=\"src src-javascript\"><span style=\"color: #ff7f24;\">\/\/ <\/span><span style=\"color: #ff7f24;\">Pagination 0 based<\/span><span style=\"color: #00ffff;\">const<\/span> <span style=\"color: #eedd82;\">htmlArticlesPaginated<\/span> = htmlArticles.slice(page, page + perPage);<span style=\"color: #00ffff;\">const<\/span> <span style=\"color: #eedd82;\">htmlArticlesPathsPaginated<\/span> = htmlArticlesPaths.slice(page, page + perPage);<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">articleDivs<\/span> = $(<span style=\"color: #ffa07a;\">\"[data-include]\"<\/span>).map(<span style=\"color: #00ffff;\">function<\/span>() { <span style=\"color: #00ffff;\">return<\/span> <span style=\"color: #7fffd4;\">this<\/span>.id; });<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">articlesZip<\/span> = [];<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">articlesElementsZip<\/span> = [];<span style=\"color: #00ffff;\">for<\/span> (<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">i<\/span> = 0; i < htmlArticlesPaginated.length; i++) { articlesZip.push([htmlArticlesPaginated[i], htmlArticlesPathsPaginated[i]]);}<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">htmlArticlesTitle<\/span> = articlesZip.map(<span style=\"color: #00ffff;\">function<\/span>(<span style=\"color: #eedd82;\">tuple<\/span>) { <span style=\"color: #00ffff;\">const<\/span> [<span style=\"color: #eedd82;\">articleContent<\/span>, <span style=\"color: #eedd82;\">articlePath<\/span>] = tuple; <span style=\"color: #00ffff;\">return<\/span> $($.parseHTML(articleContent)).find(<span style=\"color: #ffa07a;\">\"#Article\"<\/span>).wrap(<span style=\"color: #00ffff;\">function<\/span> (){ <span style=\"color: #00ffff;\">return<\/span> <span style=\"color: #ffa07a;\">\"<a href=\'\"<\/span> + articlePath + <span style=\"color: #ffa07a;\">\"\'><\/a>\"<\/span> }).parent();});<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">htmlArticlesAbstract<\/span> = htmlArticlesPaginated.map(<span style=\"color: #00ffff;\">function<\/span>(<span style=\"color: #eedd82;\">articleContent<\/span>) { <span style=\"color: #00ffff;\">return<\/span> $($.parseHTML(articleContent)).find(<span style=\"color: #ffa07a;\">\"#outline-container-ArticleAbstract\"<\/span>);});<span style=\"color: #00ffff;\">for<\/span> (<span style=\"color: #00ffff;\">var<\/span> <span style=\"color: #eedd82;\">i<\/span> = 0; i < htmlArticlesPaginated.length; i++) { articlesElementsZip.push([articleDivs[i], htmlArticlesTitle[i], htmlArticlesAbstract[i]]);}articlesElementsZip.forEach(<span style=\"color: #00ffff;\">function<\/span>(<span style=\"color: #eedd82;\">tuple<\/span>) { <span style=\"color: #00ffff;\">const<\/span> [<span style=\"color: #eedd82;\">element<\/span>, <span style=\"color: #eedd82;\">title<\/span>, <span style=\"color: #00ffff;\">abstract<\/span>] = tuple; $(<span style=\"color: #ffa07a;\">\"#\"<\/span> + element).html($(<span style=\"color: #ffa07a;\">\'<div>\'<\/span>).append(title).append(<span style=\"color: #00ffff;\">abstract<\/span>));});#+end_src#+call: templates.org:inline-js(blk=<span style=\"color: #ffa07a;\">\"blog_populateArticles\"<\/span>)<\/pre><\/div><p>Obviously, all of this came at a cost, in particular:<\/p><ul class=\"org-ul\"><li>In case of an article addition\/deletion\/renaming, we need torecompile both pages. This could be a nasty problem if we wouldneed to add multiple dynamic behaviors to several pages. A possibleworkaround could be to trigger the whole site compilation from asingle page, like the root page of the site. It might still takequite some time and this leads to the next point.<\/li><li>Not scalable compilation time: if we are going to have hundreds ofarticles it might take a long time and space (size of jsvariables). A workaround could be to split the articles intomultiple folders, by year or with an archive folder, then keep thedynamic nature only to a subset of the articles.<\/li><\/ul><\/div><\/div><div id=\"outline-container-ArticleContentFeaturesPagination\" class=\"outline-5\"><h5 id=\"ArticleContentFeaturesPagination\"><a id=\"sec-\" name=\"sec-\"><\/a>Pagination<\/h5><div class=\"outline-text-5\" id=\"text-ArticleContentFeaturesPagination\"><p>Pagination is no different from the previous section, in fact, Ittakes advantage of the same data.<\/p><p>If you take a look closely at the previous <a href=\"#populateArticlesSnippet\">snippet<\/a> you will seetwo additional variables, called <code>page<\/code> and <code>perPage<\/code>, used toslice the starting data arrays to the specific selectedpage. Both of those variables are defined in another snippet, inparticular, the <code>page<\/code> is selected from the URL query string,meanwhile, the <code>perPage<\/code> is fixed to 5.<\/p><p>Here you can see the complete js code snippet for the pagination:<\/p><div class=\"org-src-container\"><pre class=\"src src-org\">const page = ((new URLSearchParams(window.location.search).get(\'page\') || 1) - 1) * 5;const perPage = 5;for (var i = 0; i < (htmlArticles.length\/perPage); i++) { var active = \"\"; if ((page\/perPage) <span style=\"color: #b3b3b3;\">== i) {<\/span><span style=\"color: #b3b3b3;\"> active = \"class=<\/span>\'active\'\"; } $(\"#pagination\").append( \'<li \' + active + \'><a href=\"\' + window.location.href.split(\'?\')[0] + \'?page=\' + (i+1) + \'\">\' + (i+1) + \'<\/a><\/li>\' );}<span style=\"color: #ff7f24;\">#+END_SRC<\/span><span style=\"color: #ff7f24;\">#+CALL: templates.org:inline-js(blk=\"pagination\")<\/span><\/pre><\/div><\/div><\/div><\/div><\/div><div id=\"outline-container-ArticleConclusions\" class=\"outline-3\"><h3 id=\"ArticleConclusions\"><a id=\"sec-\" name=\"sec-\"><\/a>Conclusions<\/h3><div class=\"outline-text-3\" id=\"text-ArticleConclusions\"><p>In conclusion, <b><b>DON\'T DO WHAT I HAVE DONE<\/b><\/b>. I\'m sure there areseveral solutions that are more convenient than the one I implementedhere, like Wordpress, Blogspot, Blogger…<\/p><p>Anyway, even if I have to re-implement several well-known frameworks, Ialso gain these benefits:<\/p><ul class=\"org-ul\"><li>I have the chance to learn org better<\/li><li>I am free from a specific framework<\/li><li>It\'s fun (for me)<\/li><\/ul><\/div><\/div><\/div>"];
var htmlArticlesPaths = ["./articles/2024-09-24-YTDLPJQYoutubeExtraction.html","./articles/2024-09-23-AdventOfCode2023.html","./articles/2023-12-02-MountErrorWrongFSTypeBadOption.html","./articles/2023-03-25-AdventOfCode2022.html","./articles/2022-10-08-ScriptingOsLib.html","./articles/2022-07-16-ScalaExampleTestContainerMunit.html","./articles/2022-01-12-AdventOfCode2021.html","./articles/2021-12-03-godsThemselvesInstructions.html","./articles/2021-07-04-MusicTalkShow4.html","./articles/2021-06-02-MusicTalkShow3.html","./articles/2021-05-20-MusicTalkShow2.html","./articles/2021-05-13-MusicTalkShow1.html","./articles/2021-03-25-AdventOfCode2016.html","./articles/2021-02-19-PasswordManagerMigration.html","./articles/2021-02-04-NixOs.html","./articles/2021-01-29-NixMigration.html","./articles/2021-01-24-AdventOfCode2015.html","./articles/2020-12-27-AdventOfCode2020.html","./articles/2020-10-03-KenIlGuerrieroChords.html","./articles/2020-08-31-Nettuno.html","./articles/2020-08-29-EmacsIDE.html","./articles/2020-08-11-ProjectEuler2.html","./articles/2020-07-20-ProjectEuler.html","./articles/2020-06-30-GildedRoseKata.html","./articles/2020-06-16-SparkReview.html","./articles/2020-06-11-OrgBlog.html"];
</script>
<script type="text/javascript">
const page = ((new URLSearchParams(window.location.search).get('page') || 1) - 1) * 5;
const perPage = 5;
for (var i = 0; i < (htmlArticles.length/perPage); i++) {
var active = "";
if ((page/perPage) == i) {
active = "class='active'";
}
$("#pagination").append(
'<li ' + active + '><a href="' + window.location.href.split('?')[0] + '?page=' + (i+1) + '">' + (i+1) + '</a></li>'
);
}
</script>
<script type="text/javascript">
// Pagination 0 based
const htmlArticlesPaginated = htmlArticles.slice(page, page + perPage);
const htmlArticlesPathsPaginated = htmlArticlesPaths.slice(page, page + perPage);
var articleDivs = $("[data-include]").map(function() { return this.id; });
var articlesZip = [];
var articlesElementsZip = [];
for (var i = 0; i < htmlArticlesPaginated.length; i++) {
articlesZip.push([htmlArticlesPaginated[i], htmlArticlesPathsPaginated[i]]);
}
var htmlArticlesTitle = articlesZip.map(function(tuple) {
const [articleContent, articlePath] = tuple;
return $($.parseHTML(articleContent)).find("#Article").wrap(function (){
return "<a href='" + articlePath + "'></a>"
}).parent();
});
var htmlArticlesAbstract = htmlArticlesPaginated.map(function(articleContent) {
return $($.parseHTML(articleContent)).find("#outline-container-ArticleAbstract");
});
for (var i = 0; i < htmlArticlesPaginated.length; i++) {
articlesElementsZip.push([articleDivs[i], htmlArticlesTitle[i], htmlArticlesAbstract[i]]);
}
articlesElementsZip.forEach(function(tuple) {
const [element, title, abstract] = tuple;
$("#" + element).html($('<div>').append(title).append(abstract));
});
</script>
</div>
</div>
</div></div></div>
</body>
</html>