From 69162953956ab45a8f8a3afd56b0018bbd416dc4 Mon Sep 17 00:00:00 2001 From: Giacomo Debidda Date: Sun, 6 Oct 2024 21:32:32 +0200 Subject: [PATCH] project revamp --- .github/workflows/ci-cd.yml | 90 ++++++++ .github/workflows/ci.yml | 23 -- README.md | 35 ++- bb.edn | 333 +++++++++++++---------------- bb/demo_jsoup.bb | 9 +- bb/tasks.bb | 33 ++- build.clj | 80 +++++-- deps.edn | 53 +++-- dev/user.clj | 95 ++++---- devenv.nix | 43 ++-- devenv.yaml | 4 + download_pod_jackdbd_jsoup.sh | 27 +++ fosdem-dl.sh | 4 + resources/img/namespaces.png | Bin 16404 -> 80079 bytes script/compile.sh | 7 + src/fosdem_dl/cli.clj | 41 ++++ src/fosdem_dl/date_util.cljc | 10 + src/fosdem_dl/defaults.cljc | 7 + src/fosdem_dl/download.cljc | 113 ++++++++++ src/fosdem_dl/fosdem_dl.clj | 145 ------------- src/fosdem_dl/jsoup.cljc | 58 ----- src/fosdem_dl/scraping.cljc | 61 ++++++ src/fosdem_dl/shared_cli.clj | 30 +++ src/fosdem_dl/talks_cli.clj | 73 +++++++ src/fosdem_dl/tracks_cli.clj | 40 ++++ src/fosdem_dl/util.cljc | 123 ----------- src/fosdem_dl/validators.cljc | 33 +++ test-runner.clj | 18 -- test/fosdem_dl/core_test.clj | 21 -- test/fosdem_dl/scraping_test.clj | 56 +++++ test/fosdem_dl/validators_test.clj | 33 +++ test_runner.clj | 27 +++ 32 files changed, 1015 insertions(+), 710 deletions(-) create mode 100755 .github/workflows/ci-cd.yml delete mode 100755 .github/workflows/ci.yml create mode 100755 download_pod_jackdbd_jsoup.sh create mode 100755 fosdem-dl.sh create mode 100755 script/compile.sh create mode 100755 src/fosdem_dl/cli.clj create mode 100644 src/fosdem_dl/date_util.cljc create mode 100644 src/fosdem_dl/defaults.cljc create mode 100644 src/fosdem_dl/download.cljc delete mode 100755 src/fosdem_dl/fosdem_dl.clj delete mode 100644 src/fosdem_dl/jsoup.cljc create mode 100644 src/fosdem_dl/scraping.cljc create mode 100644 src/fosdem_dl/shared_cli.clj create mode 100644 src/fosdem_dl/talks_cli.clj create mode 100644 src/fosdem_dl/tracks_cli.clj delete mode 100644 src/fosdem_dl/util.cljc create mode 100644 src/fosdem_dl/validators.cljc delete mode 100755 test-runner.clj delete mode 100644 test/fosdem_dl/core_test.clj create mode 100644 test/fosdem_dl/scraping_test.clj create mode 100644 test/fosdem_dl/validators_test.clj create mode 100755 test_runner.clj diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100755 index 0000000..5380cd1 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,90 @@ +name: 'CI/CD' + +on: + pull_request: + push: + branches: + - canary + - main + +env: + APP_ID: fosdem-dl + APP_VERSION: 0.1.0-RC.1 + GRAALVM_VERSION: 23.0.0 + HEAP_SIZE_AT_BUILD_TIME: '-R:MaxHeapSize=1024m' + JAVA_VERSION: 21 + OPTIMIZATION_LEVEL: '-O2' + +jobs: + linux: + name: Build (Linux) + runs-on: ubuntu-latest + + steps: + - name: 🛎️ Checkout + uses: actions/checkout@v4 + + - name: 🔧 Setup GraalVM + uses: graalvm/setup-graalvm@v1 + with: + check-for-updates: true + distribution: graalvm-community + java-version: ${{ env.JAVA_VERSION }} + native-image-job-reports: true + native-image-pr-reports: true + version: ${{ env.GRAALVM_VERSION }} + + - name: 🔧 Setup Babashka + uses: DeLaGuardo/setup-clojure@12.5 + with: + bb: 'latest' + + - name: ⬇️ Download com.github.jackdbd/pod-jackdbd-jsoup + run: ./download_pod_jackdbd_jsoup.sh + + - name: 🔍 Run all tests + run: bb test_runner.clj + + # https://github.com/liquidz/babashka-test-action/ + # - name: Run babashka-test-action + # uses: liquidz/babashka-test-action@v1 + # with: + # source-paths: 'src' + # test-paths: 'test' + # test-file-pattern: '_test.clj$' + + - name: 📦 Compile uberjar to x86_64-linux binary with GraalVM native-image + run: ./script/compile.sh + + macos: + name: Build (macOS) + runs-on: macos-latest + + steps: + - name: 🛎️ Checkout + uses: actions/checkout@v4 + + - name: 🔧 Setup GraalVM + uses: graalvm/setup-graalvm@v1 + with: + check-for-updates: true + distribution: graalvm-community + java-version: ${{ env.JAVA_VERSION }} + native-image-job-reports: true + native-image-pr-reports: true + version: ${{ env.GRAALVM_VERSION }} + + - name: 📦 Compile uberjar to AArch64-macOS binary with GraalVM native-image + run: | + native-image \ + -jar ${{ env.APP_ID }}-${{ env.APP_VERSION }}-standalone.jar \ + '-H:ReflectionConfigurationFiles=reflection.json' \ + '-H:+ReportExceptionStackTraces' \ + '-J-Dclojure.compiler.direct-linking=true' \ + ${{ env.HEAP_SIZE_AT_BUILD_TIME }} \ + ${{ env.OPTIMIZATION_LEVEL }} \ + '-march=armv8-a' \ + '--initialize-at-build-time' \ + '--native-image-info' \ + '--no-fallback' \ + '--report-unsupported-elements-at-runtime' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100755 index 3c6c607..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Continuous Integration - -on: - # Trigger the workflow on push or pull request, - # but only for the main branch - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - continuous-integration: - name: Run all tests - runs-on: ubuntu-latest - steps: - - name: Run babashka-test-action - uses: liquidz/babashka-test-action@v1 - with: - source-paths: 'src' - test-paths: 'test' - test-file-pattern: '_test.clj$' diff --git a/README.md b/README.md index 93e6e23..045fbbe 100755 --- a/README.md +++ b/README.md @@ -1,43 +1,39 @@ # fosdem-dl (FOSDEM talks downloder) +![CI/CD](https://github.com/jackdbd/fosdem-dl/actions/workflows/ci-cd.yaml/badge.svg) + [babashka](https://github.com/babashka/babashka) script to download talks and attachments from the 2003-2020 [FOSDEM](https://fosdem.org/2021/) websites. -## Usage +![Dependency graph of all the namespaces](./resources/img/namespaces.png) -### Parameters +## Usage -```text - -y, --year YEAR Select year (default 2020) - -t, --track TRACK Select conference track (e.g. web_performance) - -f, --format FORMAT Select video format (default webm) - -a, --attachments Download each talk's attachments like PDFs and slides (default false) - -h, --help Show help +```sh +fosdem-dl talks --help +fosdem-dl tracks --help ``` -### Examples +## Examples -Download all the 2018 talks from the python track in .webm format; include no attachments. +Download all talks from the python track of FOSDEM 2018, in .webm format, with no attachments. ```sh -./fosdem-dl.sh -y 2018 -t python +fosdem-dl talks -y 2018 -t python ``` -Download all the 2020 talks from the web performance track in .mp4 format; include all attachments. +Download all talks of the web performance track from FOSDEM 2020, in .mp4 format, with all attachments. ```sh -./fosdem-dl.sh -y 2020 -t web_performance -f mp4 -a +fosdem-dl -y 2020 -t web_performance -f mp4 -a ``` -## Build - -In order to use this script you need to generate a GraalVM native image of the [pod-jaydeesimon-jsoup](https://github.com/jaydeesimon/pod-jaydeesimon-jsoup) babashka pod and place it in this project's root. This is necessary because pod-jaydeesimon-jsoup is not yet published on the [Pod registry](https://github.com/babashka/pod-registry). - ## Tests -Run all tests with: +Run all tests with either one of the following commands: ```sh -./test-runner.clj +./test_runner.clj +bb test ``` ## TODO @@ -50,7 +46,6 @@ Run all tests with: - Fix Babashka pods on NixOS or find alternative solutions. - Improve error handling. - Add more tests. -- How do I run `.github/workflows/ci.yml` when the pod-jaydeesimon-jsoup binary is not tracked in git? - Save edn file with all conference tracks over the years (to validate the `--track` option for a given `--year`). - Accept destination directory to let the user decide where to download the files. - Include the talk's links too? Maybe write them in a text/markdown file? diff --git a/bb.edn b/bb.edn index 32da368..90eae9b 100644 --- a/bb.edn +++ b/bb.edn @@ -1,196 +1,151 @@ {:deps {local/deps {:local/root "."}} :min-bb-version "1.3.189" :paths ["bb" "src" "resources"] - :pods {pod.jackdbd.jsoup {:path "./resources/pod/pod-jackdbd-jsoup-0.1.0"} - ;; org.babashka/postgresql {:version "0.1.2"} - ;; justone/tabl {:version "0.3.0"} + :pods {com.github.jackdbd/pod-jackdbd-jsoup {:path "./resources/pod/pod-jackdbd-jsoup"} + ;; com.github.jackdbd/pod-jackdbd-jsoup {:path "./resources/pod/pod-jackdbd-jsoup-0.2.1"} + ;; com.github.jackdbd/pod-jackdbd-jsoup {:version "0.3.0"} } - :tasks {:requires ([babashka.process :refer [process check]] - [clojure.edn :as edn] - [tasks] - [clojure.string :as str]) - build:bb-uberjar {:doc "Build the uberjar (it must be called with bb foo.jar, not java -jar foo.jar)" - :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) - name (first (str/split (str (:name project)) (re-pattern "/"))) - jar (format "target/%s-%s-standalone.jar" name (:version project)) - cmd (format "bb uberjar %s --main fosdem-dl.fosdem-dl " jar)] - (println cmd) - (shell cmd))} - build:uberjar {:doc "Build the uberjar (in the host, not in a container)" - :task (clojure "-T:build uber")} - build:native-image {:doc "Build the GraalVM native image (in the host, not in a container)" - ;; :depends [build:uberjar] - :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) - name (first (str/split (str (:name project)) (re-pattern "/"))) - jar (format "target/%s-%s-standalone.jar" name (:version project)) - working-dir (-> (process ["pwd"] {:out :string}) - check :out str/split-lines first) - jvm-max-heap-size "-Xmx4500m" - optimization-level "-Ob" - ;; optimization-level "-O1" - graal-opts ["-jar" jar - ;; https://www.graalvm.org/22.0/reference-manual/native-image/HostedvsRuntimeOptions/ - "-H:Name=fosdem-dl" - ;; This directory must exist - "-H:Path=target" - "-H:+ReportExceptionStackTraces" - ;; By default, the native-image tool will not integrate - ;; any of the resources that are on the classpath into - ;; the native executable. - ;; https://www.graalvm.org/22.1/reference-manual/native-image/Resources/ - ;; "-H:IncludeResources=\"^pod-.*\"" - "-H:Log=registerResource:5" - optimization-level - ;; If GraalVM fails to generate the standalone executable, - ;; don't create a fallback image (which would require - ;; a JDK to run), but report the failure instead. - "--no-fallback" - ;; The Serial garbage collector is the default one. - "--gc=serial" - - ;; https://www.graalvm.org/22.1/reference-manual/native-image/StaticImages/ - ;; Use these 2 options to compile a mostly statically-linked binary - "-H:+StaticExecutableWithDynamicLibC" - "--libc=glibc" - ;; Use these 2 options to compile a completely statically-linked binary - ;; "--static" - ;; "--libc=musl" - ;; "--initialize-at-build-time=pod.tabl.fancy$eval138$loading__6789__auto____139" - "--initialize-at-run-time=pod.tabl.fancy$eval138$loading__6789__auto____139" - "--trace-class-initialization=pod.tabl.fancy$eval138$loading__6789__auto____139,pod.tabl.fancy$eval142$fn__143" - ;; "--initialize-at-run-time=pod.tabl.doric$eval152$fn__153,pod.tabl.fancy$eval138$loading__6789__auto____139,pod.tabl.fancy$eval142$fn__143" - ;; "--trace-class-initialization=pod.tabl.doric$eval152$fn__153,pod.tabl.fancy$eval138$loading__6789__auto____139,pod.tabl.fancy$eval142$fn__143" - ;; "--initialize-at-build-time=pod.tabl.doric$eval148$loading__6789__auto____149,pod.tabl.fancy$eval142$fn__143" - ;; "--initialize-at-build-time=pod.tabl.doric,pod.tabl.fancy" - ;; "--trace-class-initialization=pod.tabl.doric$eval152$fn__153,pod.tabl.fancy$eval138$loading__6789__auto____139,pod.tabl.fancy$eval142$fn__143" - - "-H:+PrintClassInitialization" - "--diagnostics-mode" - "--native-image-info" - "--verbose" - "-Dclojure.compiler.direct-linking=true" - ;; "--initialize-at-build-time=pod.jaydeesimon.jsoup$eval142$fn__143,pod.jaydeesimon.jsoup$eval138$loading__6789__auto____139" - (format "-J%s" jvm-max-heap-size)] - graal-optz ["-jar" jar - "--allow-incomplete-classpath" - (format "-H:Name=%s" name) - optimization-level - "-H:+PrintClassInitialization" - "-H:+ReportExceptionStackTraces" - ;; https://www.graalvm.org/22.1/reference-manual/native-image/Resources/ - "-H:IncludeResources=\"^pod-.*\"" - "-H:Log=registerResource:5" - "-H:ReportAnalysisForbiddenType=java.awt.Toolkit:InHeap,Allocated" - "--diagnostics-mode" - "--native-image-info" - "--features=InitAtBuildTimeFeature" - ;; "--initialize-at-run-time=pod.jaydeesimon.jsoup" - "--initialize-at-build-time=pod.jaydeesimon.jsoup$eval142$fn__143,pod.jaydeesimon.jsoup$eval138$loading__6789__auto____139" - "--report-unsupported-elements-at-runtime" - ;; if GraalVM fails to generate the standalone executable, - ;; don't create a fallback image (which would require - ;; a JDK to run), but report the failure instead - "--no-fallback" - "--gc=serial" - "-H:+StaticExecutableWithDynamicLibC" - "--libc=glibc" - "--verbose" - "--trace-class-initialization=pod.jaydeesimon.jsoup$eval142$fn__143,pod.jaydeesimon.jsoup$eval138$loading__6789__auto____139" - "--trace-object-instantiation=java.io.FileDescriptor" - "--trace-object-instantiation=java.lang.ProcessImpl" - "-g" - (format "-J%s" jvm-max-heap-size)] - cmd (format "native-image %s" (str/join " " graal-opts))] - (println "=== Build GraalVM native image ===") - (println cmd) - (println "=== === === === === === === === ==") - (shell cmd))} - classpath {:doc "Print the classpath" - :task (tasks/print-classpath)} - clean {:doc "Remove compilation artifacts and downloads (in parallel)" - :task (run '-clean {:parallel true})} - -clean {:depends [clean:artifacts clean:downloads]} - clean:artifacts {:doc "Remove compilation artifacts" - :task (clojure "-T:build clean")} - clean:downloads {:doc "Remove downloads" - :task (shell "rm -rf ./downloads")} - demo:main {:doc "Show how to use the CLI (runs the uberjar)" - :task (let [year 2020 - track "web_performance" - video-format "webm" - ;; The usage of clojure.java.io/resource assumes a classpath, so - ;; when this is used in your uberscript, you still have to set a - ;; classpath and bring the resources along. - ;; https://book.babashka.org/#_classpath - ;; cmd (format "bb --debug --classpath src:resources -x fosdem-dl.fosdem-dl/-main") - ;; cmd (format "bb --debug --classpath src:resources -x fosdem-dl.fosdem-dl/-main --help") - cmd (format "bb --debug -x fosdem-dl.fosdem-dl/-main --year %s --track %s --format %s" year track video-format)] - (println cmd) - (shell cmd))} - demo:native-image {:doc "Show how to use the CLI (runs the GraalVM native image)" - ;; :depends [build:native-image] - :task (let [year 2020 - track "web_performance" - video-format "webm" - cmd (format "./target/fosdem-dl --year %s --track %s --format %s" year track video-format)] - (println cmd) - (shell cmd))} - demo:uberjar {:doc "Show how to use the CLI (calls the -main function of the fosdem-dl.fosdem-dl namespace)" - ;; :depends [build:uberjar] - :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) - name (first (str/split (str (:name project)) (re-pattern "/"))) - version (:version project) - year 2020 - track "web_performance" - video-format "webm" - ;; cmd (format "java -jar target/%s-%s-standalone.jar" name version) - ;; cmd (format "java -jar target/%s-%s-standalone.jar --help" name version) - cmd (format "java -jar target/%s-%s-standalone.jar --year %s --track %s --format %s" name version year track video-format)] - (println cmd) - (shell cmd))} - dep:upgrade {:doc "Upgrade all dependencies" - :task (shell "neil dep upgrade")} - dk:build {:doc "Build the container image that builds the uberjar and the GraalVM native image" - :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) - name (first (str/split (str (:name project)) (re-pattern "/"))) - version (:version project) - options ["--file" "Dockerfile" - "--build-arg" (str "APP_NAME=" name) - "--build-arg" (str "APP_VERSION=" version) - #_"--quiet" - "--tag" (str name ":" version)] - cmd (format "docker build ./ %s" (str/join " " options))] - (shell cmd))} - dk:cp {:doc "Copy the build artifacts (jar, native image) from the Docker container" - :depends [dk:build] - :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) - image-name (first (str/split (str (:name project)) (re-pattern "/"))) - image-tag (:version project) - image (str image-name ":" image-tag) - working-dir (-> (process ["pwd"] {:out :string}) - check :out str/split-lines first) - container-id (-> (process ["docker" "create" image] {:out :string}) - check :out str/split-lines first) - cmd (format "docker cp %s:/app %s/output" container-id working-dir)] - (println cmd) - (shell cmd))} - dk:run {:doc "Execute the fosdem-dl CLI in a Docker container" + + :tasks + {:requires ([babashka.process :refer [process check]] + [clojure.edn :as edn] + [clojure.string :as str] + [fosdem-dl.talks-cli :refer [talks-cli]] + [fosdem-dl.tracks-cli :refer [tracks-cli]] + [tasks]) + + build:bb-uberjar + {:doc "Build the uberjar (it must be called with bb foo.jar, not java -jar foo.jar)" + :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) + name (first (str/split (str (:name project)) (re-pattern "/"))) + jar (format "target/%s-%s-standalone.jar" name (:version project)) + cmd (format "bb uberjar %s --main fosdem-dl.fosdem-dl " jar)] + (println cmd) + (shell cmd))} + + build:uber + {:doc "Build the uberjar" + :task (clojure "-T:build uber")} + + build:binary + {:doc "Compile the CLI into a statically-linked binary with GraalVM native-image (Linux only)" +;; :depends [build:uber] + :task (shell "script/compile.sh")} + + cp + {:doc "Print the classpath" + :task (tasks/print-classpath)} + + clean + {:doc "Remove compilation artifacts and downloads (in parallel)" + :task (run '-clean {:parallel true})} + + -clean + {:depends [clean:artifacts clean:downloads]} + + clean:artifacts + {:doc "Remove compilation artifacts" + :task (clojure "-T:build clean")} + + clean:downloads + {:doc "Remove downloads" + :task (shell "rm -rf ./downloads")} + + cli + {:doc "fosdem-dl CLI (e.g. 'bb cli talks -y 2019 -t llvm', 'bb cli tracks -y 2020')" + :task (let [list *command-line-args* + cmd (conj list "./src/fosdem_dl/cli.clj")] + (shell cmd))} + + cli:debug + {:doc "Launch the CLI with bb --debug -x fosdem-dl.cli/-main" + ;; The usage of clojure.java.io/resource assumes a classpath, so + ;; when this is used in your uberscript, you still have to set a + ;; classpath and bring the resources along. + ;; https://book.babashka.org/#_classpath + ;; cmd (format "bb --debug --classpath src:resources -x fosdem-dl.fosdem-dl/-main") + ;; cmd (format "bb --debug --classpath src:resources -x fosdem-dl.fosdem-dl/-main --help") + :task (let [cmd (into ["bb" "--debug" "-x" "fosdem-dl.cli/-main"] *command-line-args*)] + (println cmd) + (shell cmd))} + + dep:upgrade + {:doc "Upgrade all dependencies" + :task (shell "neil dep upgrade")} + + dk:build + {:doc "Build the container image that builds the uberjar and the GraalVM native image" + :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) + name (first (str/split (str (:name project)) (re-pattern "/"))) + version (:version project) + options ["--file" "Dockerfile" + "--build-arg" (str "APP_NAME=" name) + "--build-arg" (str "APP_VERSION=" version) + #_"--quiet" + "--tag" (str name ":" version)] + cmd (format "docker build ./ %s" (str/join " " options))] + (shell cmd))} + dk:cp + {:doc "Copy the build artifacts (jar, native image) from the Docker container" + :depends [dk:build] + :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) + image-name (first (str/split (str (:name project)) (re-pattern "/"))) + image-tag (:version project) + image (str image-name ":" image-tag) + working-dir (-> (process ["pwd"] {:out :string}) + check :out str/split-lines first) + container-id (-> (process ["docker" "create" image] {:out :string}) + check :out str/split-lines first) + cmd (format "docker cp %s:/app %s/output" container-id working-dir)] + (println cmd) + (shell cmd))} + + dk:run + {:doc "Execute the fosdem-dl CLI in a Docker container" ;; :depends [dk:cp] - :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) - image-name (first (str/split (str (:name project)) (re-pattern "/"))) - image-tag (:version project) - image (str image-name ":" image-tag) + :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) + image-name (first (str/split (str (:name project)) (re-pattern "/"))) + image-tag (:version project) + image (str image-name ":" image-tag) ;; https://www.graalvm.org/22.0/reference-manual/native-image/MemoryManagement/#printing-garbage-collections - gc-options "-XX:+PrintGC -XX:+VerboseGC" - args "--help" + gc-options "-XX:+PrintGC -XX:+VerboseGC" + args "--help" ;; args "--year 2020 --track web_performance --format webm" - cmd (format "docker run -it --rm %s fosdem-dl %s %s" image gc-options args)] - (println cmd) - (shell cmd))} - graph {:doc "Generate a graph of dependencies between namespaces" - :task (clojure "-X:hiera" :layout :vertical)} - graph:copy {:depends [graph] - :doc "Copy the image of the graph of namespace to resources/img" - :task (shell "cp target/hiera/namespaces.png resources/img")} - install {:doc "Install dependencies" - :task (shell "clj -A:dev")}}} + cmd (format "docker run -it --rm %s fosdem-dl %s %s" image gc-options args)] + (println cmd) + (shell cmd))} + + -graph:gen + {:task (clojure "-X:hiera" :layout :vertical)} + + -graph:copy + {:task (shell "cp target/hiera/namespaces.png resources/img")} + + graph + {:depends [-graph:gen -graph:copy] + :doc "Generate a graph of dependencies between namespaces and copy the image to resources/img" + :task (shell "feh resources/img/namespaces.png")} + + pod:download + {:doc "Download com.github.jackdbd/pod-jackdbd-jsoup" + :task (shell "./download_pod_jackdbd_jsoup.sh")} + + talks + {:doc "fosdem-dl talks CLI (e.g. 'bb talks -y 2019 -t llvm')" + :task (talks-cli *command-line-args*)} + + test + {:doc "Run all tests" + :task (shell "bb test_runner.clj")} + + ;; https://github.com/liquidz/babashka-test-action/?tab=readme-ov-file#local-usage + test:action + {:task (let [working-dir (-> (process ["pwd"] {:out :string}) + check :out str/split-lines first) + cmd (format "docker run --rm -v %s:/tmp -w /tmp uochan/babashka-test 'src' 'test' '_test.clj$'" working-dir)] + (println cmd) + (shell cmd))} + + tracks + {:doc "fosdem-dl tracks CLI (e.g. 'bb tracks -y 2020')" + :task (tracks-cli *command-line-args*)}}} diff --git a/bb/demo_jsoup.bb b/bb/demo_jsoup.bb index cabe596..62a3e43 100644 --- a/bb/demo_jsoup.bb +++ b/bb/demo_jsoup.bb @@ -1,8 +1,9 @@ (ns demo-jsoup - (:require [babashka.pods :as pods] - [cheshire.core :as json] - [clojure.string :as str] - [pod.jackdbd.jsoup :as jsoup])) + (:require + [babashka.pods :as pods] + [cheshire.core :as json] + [clojure.string :as str] + [pod.jackdbd.jsoup :as jsoup])) (def pod-name "pod-jackdbd-jsoup") (def pod-version "0.1.0") diff --git a/bb/tasks.bb b/bb/tasks.bb index 56e37e8..f523010 100644 --- a/bb/tasks.bb +++ b/bb/tasks.bb @@ -1,12 +1,15 @@ (ns tasks - (:require [babashka.classpath :refer [get-classpath split-classpath]] - [babashka.pods :as pods] - [cheshire.core :as json] - [clojure.string :as str])) + (:require + [babashka.classpath :refer [get-classpath split-classpath]] + [babashka.pods :as pods] + [cheshire.core :as json] + [clojure.string :as str])) +(def pod-id "pod.jackdbd.jsoup") (def pod-name "pod-jackdbd-jsoup") -(def pod-version "0.1.0") -(def exe-file (format "resources/pod/%s-%s" pod-name pod-version)) +(def pod-version (System/getenv "POD_JACKDBD_JSOUP_VERSION")) +(def uber-file (format "resources/pod/%s-%s-standalone.jar" pod-id pod-version)) +(def exe-file (format "resources/pod/%s" pod-name)) (def html (str/join "" ["" "" @@ -28,12 +31,24 @@ (println path)) (println "=== CLASSPATH END ===")) -(comment +(comment + (pods/load-pod ["java" "-jar" uber-file]) (pods/load-pod exe-file) + (require '[pod.jackdbd.jsoup :as jsoup]) - + + (jsoup/select html "div.foo") + (def parsed (jsoup/select html "div.foo")) (def filepath "target/test-html.json") (spit filepath (json/generate-string {:html html :parsed parsed})) - (println (str "wrote " filepath)) + (println (str "wrote " filepath)) + + (require '[babashka.http-client :as http]) + + (-> (http/get "https://clojure.org") + :body + (jsoup/select "div p") + first + :text) ) diff --git a/build.clj b/build.clj index cbff54d..86d86c9 100644 --- a/build.clj +++ b/build.clj @@ -3,29 +3,71 @@ [clojure.tools.build.api :as b])) (def project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project)) -(def lib (or (:name project) 'my/lib1)) +(def lib (:name project)) + (def version (or (:version project) "latest")) (def class-dir "target/classes") (def basis (b/create-basis {:project "deps.edn"})) (def uber-file (format "target/%s-%s-standalone.jar" (name lib) version)) -(defn clean [_] +(defn clean "Remove all compilation artifacts." [_] (b/delete {:path "target"})) -(defn uber - "Builds the uberjar. - - Invoke this function with clojure -T:build uber" - [_] - (clean nil) - (b/copy-dir {:src-dirs ["src" "resources"] - :target-dir class-dir}) - ;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-compile-clj - (b/compile-clj {:basis basis - :src-dirs ["src"] - :class-dir class-dir}) - ;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-uber - (b/uber {:class-dir class-dir - :uber-file uber-file - :basis basis - :main 'fosdem-dl.fosdem-dl})) +;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-write-pom +(defn- pom-template [{:keys [version]}] + [[:description "CLI to download the talks given at FOSDEM over the years."] + [:url "https://github.com/jackdbd/fosdem-dl"] + [:licenses + [:license + [:name "The MIT License"] + ;; https://www.tldrlegal.com/license/mit-license + [:url "https://opensource.org/license/MIT"]]] + [:developers + [:developer + [:name "Giacomo Debidda"]]] + [:scm + [:url "https://github.com/jackdbd/fosdem-dl"] + [:connection "scm:git:https://github.com/jackdbd/fosdem-dl.git"] + [:developerConnection "scm:git:ssh:git@github.com:jackdbd/pod-jackdbd-jsoup.git"] + [:tag (str "v" version)]]]) + +(defn- shared-config [opts] + (let [vers (:version project)] + (assoc opts + :basis (b/create-basis {:project "deps.edn"}) + :class-dir "target/classes" + :jar-file (format "target/%s-%s.jar" (name lib) vers) + :lib lib + :main 'pod.jackdbd.jsoup + :pom-data (pom-template {:version vers}) + :src-dirs ["src"] + :target "target" + :uber-file (format "target/%s-%s-standalone.jar" (name lib) vers) + :version vers))) + +(defn uber "Build the uber-JAR." [opts] + (let [config (shared-config opts) + {:keys [basis class-dir main pom-data src-dirs target uber-file version]} config] + + (clean nil) + + (println "\nWriting" (b/pom-path (select-keys config [:lib :class-dir])) "...") + (b/write-pom {:basis basis + :class-dir class-dir + :lib lib + :pom-data pom-data + :src-dirs src-dirs + :target target + :version version}) + + (println "\nCopying src and resources ...") + (b/copy-dir {:src-dirs ["src" "resources"] :target-dir class-dir}) + + (println "\nBuilding" uber-file "...") + ;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-compile-clj + (b/compile-clj {:basis basis :class-dir class-dir :src-dirs ["src"]}) + ;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-uber:version version}) + (b/uber {:basis basis + :class-dir class-dir + :main main + :uber-file uber-file}))) diff --git a/deps.edn b/deps.edn index 90b597b..8b7bebe 100644 --- a/deps.edn +++ b/deps.edn @@ -1,19 +1,34 @@ -{:aliases {:build {:deps {io.github.clojure/tools.build {:git/tag "v0.8.4" :git/sha "8c3cd69"}} - :ns-default build} - :dev {:extra-deps {cuic/cuic {:mvn/version "1.1.0"} - etaoin/etaoin {:mvn/version "1.1.41"} - djblue/portal {:mvn/version "0.57.3"}} - :extra-paths ["bb" "dev"]} - :exec {:extra-deps {org.babashka/cli {:mvn/version "0.8.60"}} - :main-opts ["-m" "babashka.cli.exec"]} - :hiera {:deps {io.github.greglook/clj-hiera {:git/tag "2.0.0", :git/sha "b14e514"}} - :exec-args {} - :exec-fn hiera.main/graph} - :graph {:replace-deps {org.clojure/tools.deps.graph {:mvn/version "1.1.68"}} - :ns-default clojure.tools.deps.graph} - :neil {:project {:name fosdem-dl/fosdem-dl}}} - :deps {babashka/babashka {:mvn/version "1.4.192"} - justone/tabl {:git/url "https://github.com/justone/tabl.git" :git/sha "6de881ff75db1a7325fa4ec429c98ce8d055c309"} - org.clojure/clojure {:mvn/version "1.12.0"} - progrock/progrock {:mvn/version "0.1.2"}} - :paths ["src" "resources"]} +{:aliases + {:build {:deps {io.github.clojure/tools.build {:git/tag "v0.10.5" + :git/sha "2a21b7a"} + slipset/deps-deploy {:mvn/version "0.2.2"}} + :ns-default build} + :dev {:extra-deps {djblue/portal {:mvn/version "0.57.3"}} + :extra-paths ["dev"]} + :exec {:extra-deps {org.babashka/cli {:mvn/version "0.8.60"}} + :main-opts ["-m" "babashka.cli.exec"]} + + :hiera + {:deps {io.github.greglook/clj-hiera {:git/tag "2.0.0", :git/sha "b14e514"}} + :exec-args {} + :exec-fn hiera.main/graph} + + :neil {:project {:name com.github.jackdbd/fosdem-dl + :version "0.1.0-RC.1"}} + :test {:extra-deps {babashka/babashka.pods {:mvn/version "0.2.0"} + io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" + :git/sha "dfb30dd" + :git/url "https://github.com/cognitect-labs/test-runner"}} + :extra-paths ["test"] + :exec-fn cognitect.test-runner.api/test + :main-opts ["-m" "cognitect.test-runner"]}} + + :deps + {babashka/babashka {:mvn/version "1.4.192"} +;; com.github.jackdbd/pod.jackdbd.jsoup {:local/root "./resources/pod/pod.jackdbd.jsoup-0.3.0-standalone.jar"} +;; com.github.jackdbd/pod.jackdbd.jsoup {:mvn/version "0.3.0"} + org.clojure/clojure {:mvn/version "1.12.0"} + progrock/progrock {:mvn/version "0.1.2"}} + + :paths + ["src" "resources"]} diff --git a/dev/user.clj b/dev/user.clj index 00d02b3..0301b9e 100644 --- a/dev/user.clj +++ b/dev/user.clj @@ -1,63 +1,58 @@ (ns user - (:require [clojure.java.io :as io] - [cuic.core :as c] - [cuic.chrome :as chrome] - [fosdem-dl.jsoup :as jsoup] - [fosdem-dl.util :as util])) - -(prn "hello from user.clj") - -;; (require '[babashka.deps :as deps]) -;; (deps/add-deps '{:deps {etaoin/etaoin {:mvn/version "1.0.38"}}}) - -;; (eta/go driver "https://en.wikipedia.org/") -;; wait for the search input to load -;; (eta/wait-visible driver [{:id :simpleSearch} {:tag :input :name :search}]) + (:require + [babashka.http-client :as http] + [clojure.java.io :as io] + [fosdem-dl.download :refer [download-all!]] + [fosdem-dl.scraping :refer [attachment-urls + conference-track-urls + jsoup-node-element->href + maybe-video-url]] + [fosdem-dl.talks-cli :refer [talks-cli]] + [fosdem-dl.tracks-cli :refer [tracks-cli]] + [pod.jackdbd.jsoup :as jsoup])) (comment - (def launch-options {:disable-gpu false - :headless false - :no-first-run true - :remote-debugging-port 9222}) + (def url "https://archive.fosdem.org/2020/schedule/event/seccomp/") + (def timeout 10000) + (def response (http/get url {:as :stream :throw false :timeout timeout})) + (def html (slurp (:body response))) - ; be careful on how you specify the path to your Chrome binary -; https://github.com/milankinen/cuic/issues/18 - (def browser - (chrome/launch launch-options (.toPath (io/file "/usr/bin/google-chrome-stable")))) + (jsoup/select html "div") + (jsoup/select html "video>source") - (c/set-browser! browser) +;; This page has no video + (def url "https://fosdem.org/2023/schedule/event/test_talk1/") + (def response (http/get url {:throw false})) + (def html (:body response)) + (maybe-video-url {:html html}) - ;; (def url "https://archive.fosdem.org/2003/index/schedule.html") - ;; (def url "https://archive.fosdem.org/2003/index/tracks.html") - ;; (def url "https://archive.fosdem.org/2004/2004/index/schedule.html") - ;; (def url "https://archive.fosdem.org/2005/2005/index/tracks.html") - ;; (def url "https://archive.fosdem.org/2020/schedule/track/databases/") + (tracks-cli ["-y" 2020]) - (def year 2020) - (def track "web_performance") - (def url (str "https://archive.fosdem.org/" year "/schedule/track/" track "/")) + (talks-cli ["-y" 2020 "-t" "databases"]) - (c/goto url) + ;; This page has 1 attachment (the slides of the presentation) + (def url "https://archive.fosdem.org/2020/schedule/event/dqlite/") + (def response (http/get url {:throw false})) + (def html (:body response)) + (def links (jsoup/select html ".event-attachments>li>a[href]")) + (attachment-urls {:html html}) - (def url "https://video.fosdem.org/2020/H.1309/webperf_boomerang_optimisation.webm") - (util/get-video url) + (def directory "./downloads") + (.mkdir (io/file directory)) -;; (require '[clojure.java.io :as io] - ;; '[cuic.core :as c] - ;; '[cuic.chrome :as chrome]) - ;; (require '[etaoin.api :as e] - ;; '[etaoin.keys :as k] + (def avi-url (maybe-video-url {:html html :video-format :avi})) + (def webm-url (maybe-video-url {:html html :video-format :webm})) -;; https://github.com/clj-commons/etaoin/blob/master/doc/01-user-guide.adoc#installing-the-browser-webdrivers - ;; (def driver (e/firefox {})) - ;; (def driver (eta/chrome {})) - ;; (eta/driver? driver :chrome) - ) + (download-all! {:url url :directory directory :timeout timeout}) -(comment - (def html - "

a foo paragraph

a bar paragraph

") + (def year 2020) + (def track "databases") + (def url (str "https://archive.fosdem.org/" year "/schedule/track/" track "/")) + (def response (http/get url {:as :stream :throw false})) + (def html (slurp (:body response))) + + (conference-track-urls {:html html}) - (jsoup/select html "p.foo") - (jsoup/select html "p.bar") - (jsoup/select html "p.baz")) + (def nodes (jsoup/select html "table:last-child tr>td:nth-child(2)>a[href]")) + (doseq [node nodes] + (prn (jsoup-node-element->href node)))) diff --git a/devenv.nix b/devenv.nix index ceeaa5d..01c6e90 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,7 +1,8 @@ {pkgs, ...}: { enterShell = '' - hello versions + # export JAVA_HOME=$(which java) + export APP_VERSION=$(app-version) ''; enterTest = '' @@ -9,13 +10,27 @@ git --version | grep --color=auto "${pkgs.git.version}" ''; - env.GREET = "devenv"; + env = { + GH_TOKEN = builtins.readFile "/run/secrets/github-tokens/semantic_release_bot"; + GREET = "devenv"; + POD_JACKDBD_JSOUP_VERSION = "0.3.0"; + }; - languages.clojure.enable = true; + languages = { + clojure.enable = true; + nix.enable = true; + }; packages = with pkgs; [ babashka # Clojure interpreter for scripting git + + # musl is required by GraalVM native-image when compiling a statically + # linked executable. I guess I need to include musl if I use graalvm-ce, but + # not if I use graalvmCEPackages.graalvm-ce-musl. + graalvmCEPackages.graalvm-ce-musl + + neil ]; pre-commit.hooks = { @@ -27,25 +42,29 @@ detect-private-keys.enable = true; end-of-file-fixer.enable = true; # https://github.com/lycheeverse/lychee - lychee = { - enable = true; - excludes = [ - "src/fosdem_dl/util.cljc" - ]; - }; + # lychee = { + # enable = true; + # excludes = [ + # "src/fosdem_dl/cli.clj" + # ]; + # }; # markdownlint.enable = true; - shellcheck.enable = true; + # shellcheck.enable = true; statix.enable = true; }; scripts = { - hello.exec = '' - echo hello from $GREET + app-version.exec = '' + bb -e '(-> (slurp "deps.edn") edn/read-string :aliases :neil :project :version)' | tr -d '"' ''; versions.exec = '' echo "=== Versions ===" bb --version + dot --version git --version + java --version + native-image --version + neil --version echo "=== === ===" ''; }; diff --git a/devenv.yaml b/devenv.yaml index 116a2ad..6601adc 100644 --- a/devenv.yaml +++ b/devenv.yaml @@ -13,3 +13,7 @@ inputs: # If you have more than one devenv you can merge them #imports: # - ./backend + +# In order to access absolute paths in `devenv.nix`, we need to set `impure: true` here. +# https://devenv.sh/reference/yaml-options/ +impure: true diff --git a/download_pod_jackdbd_jsoup.sh b/download_pod_jackdbd_jsoup.sh new file mode 100755 index 0000000..a38131e --- /dev/null +++ b/download_pod_jackdbd_jsoup.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +# https://github.com/jackdbd/pod-jackdbd-jsoup/releases + +POD_ID=pod.jackdbd.jsoup +POD_NAME=pod-jackdbd-jsoup +POD_VERSION=$POD_JACKDBD_JSOUP_VERSION +ARCH=x86_64 +OS=ubuntu-latest + +echo "Download assets published on GitHub release" +UBERJAR="$POD_ID-$POD_VERSION-standalone.jar" +ZIP_BINARY="$POD_NAME-$POD_VERSION-$OS-$ARCH.zip" + +gh release download "v$POD_VERSION" \ + --clobber \ + --dir resources/pod \ + --repo jackdbd/pod-jackdbd-jsoup \ + --pattern "$UBERJAR" \ + --pattern "$ZIP_BINARY" + +pushd . && cd resources/pod && unzip -o "$ZIP_BINARY" && popd + +chmod +x "resources/pod/$POD_NAME" + +rm "resources/pod/$ZIP_BINARY" diff --git a/fosdem-dl.sh b/fosdem-dl.sh new file mode 100755 index 0000000..2353d2e --- /dev/null +++ b/fosdem-dl.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +SCRIPT_DIR=$(cd $(dirname $0); pwd) +bb --classpath "$SCRIPT_DIR/src" --main fosdem-dl.cli -- $@ diff --git a/resources/img/namespaces.png b/resources/img/namespaces.png index fa3dfcf5c323e2abc2d1aa9c5f3ee2e83c6ad1ce..a0b309c6939c4617c094ecda3924b8441fea2696 100644 GIT binary patch literal 80079 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 Lwz`&GNY`6)7d3s=`Jf89C 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

M!jtN*s}1dN9+ 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 JTh80`e*h;YWorNc literal 16404 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

7F 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 diff --git a/script/compile.sh b/script/compile.sh new file mode 100755 index 0000000..8435942 --- /dev/null +++ b/script/compile.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_ID=fosdem-dl +echo "APP_ID is $APP_ID" + +native-image --version diff --git a/src/fosdem_dl/cli.clj b/src/fosdem_dl/cli.clj new file mode 100755 index 0000000..865949a --- /dev/null +++ b/src/fosdem_dl/cli.clj @@ -0,0 +1,41 @@ +#!/usr/bin/env bb + +(ns fosdem-dl.cli + "CLI to download the talks given at FOSDEM over the years." + (:gen-class) + (:require + [fosdem-dl.talks-cli :refer [talks-cli]] + [fosdem-dl.tracks-cli :refer [tracks-cli]])) + +(def available-subcommands #{"talks" "tracks"}) + +(defn default-cli [args] + (let [subcommand (first args)] + (println (format "[ERROR] Unknown subcommand: %s" subcommand)) + (println "Available subcommands:" (str available-subcommands)) + {:exit-code 1})) + +(defn -main + [args] + + ;; (println "=== args ===" args) + ;; (println "=== *command-line-args* ===" *command-line-args*) + ;; (prn "*file* is" *file*) + ;; (prn "(System/getProperty babashka.file) is" (System/getProperty "babashka.file")) + + (let [subcommand (first args) + result (case subcommand + "talks" (talks-cli (rest args)) + "tracks" (tracks-cli (rest args)) + (default-cli args))] + ;; (System/exit {:exit-code result}) + {:exit-code result})) + +(comment + (-main ["tracks" "-y" 2023]) + (-main ["talks" "-y" 2023 "-t" "test"])) + +;; (def schedule-page "https://fosdem.org/2021/schedule/") +;; (def video-page "https://video.fosdem.org/") + +(-main *command-line-args*) diff --git a/src/fosdem_dl/date_util.cljc b/src/fosdem_dl/date_util.cljc new file mode 100644 index 0000000..f359baa --- /dev/null +++ b/src/fosdem_dl/date_util.cljc @@ -0,0 +1,10 @@ + +(ns fosdem-dl.date-util + (:import + [java.time LocalDate])) + +(defn current-year [] + (.getYear (LocalDate/now))) + +(comment + (current-year)) diff --git a/src/fosdem_dl/defaults.cljc b/src/fosdem_dl/defaults.cljc new file mode 100644 index 0000000..c34930d --- /dev/null +++ b/src/fosdem_dl/defaults.cljc @@ -0,0 +1,7 @@ +(ns fosdem-dl.defaults) + +(def download-attachments? false) +(def timeout 10000) +(def track :web_performance) +(def year 2023) +(def video-format :webm) diff --git a/src/fosdem_dl/download.cljc b/src/fosdem_dl/download.cljc new file mode 100644 index 0000000..9c05772 --- /dev/null +++ b/src/fosdem_dl/download.cljc @@ -0,0 +1,113 @@ +(ns fosdem-dl.download + (:require + [babashka.http-client :as http] + [clojure.core :refer [format slurp]] + [clojure.java.io :as io] + [clojure.string :as str] + [fosdem-dl.defaults :as default] + [fosdem-dl.scraping :refer [maybe-video-url attachment-urls]] + ;; [progrock.core :as pr] + [taoensso.timbre :refer [debug trace] :as timbre]) + (:import + [java.lang Exception])) + +;; TODO: render a meaningful progress in the progress bar +;; bar (pr/progress-bar 100) +;; (pr/print (pr/tick bar 25)) + +(defn- download! + "Downloads an asset hosted at URL `url`, to directory `directory`." + [{:keys [directory url timeout] + :or {timeout default/timeout}}] + (let [directory (io/file directory) + filename (-> (str/split url #"/") last) + file (io/file directory filename) + filepath (str file) + result-dir (try + (debug "Trying to create/access directory" directory) + (.mkdir (io/file directory)) + {:error false} + (catch Exception e + (let [msg (.getMessage ^Throwable e)] + (trace (ex-info msg {:causes #{:fs} + :directory directory})) + {:error msg})))] + + (if-let [msg (:error result-dir)] + {:error msg :ok false} + (try + (debug "Trying to download" url "to" directory) + (let [response (http/get url {:as :stream :throw false :timeout timeout}) + status (:status response)] + (if (not= 200 status) + {:error (format "Could not download %s (HTTP %s)" url status) :ok false} + (do + (debug "Downloaded" url) + (try + (debug "Trying to create file" filepath) + (io/copy (:body response) file) + {:message (format "Wrote %s" filepath) :ok true} + (catch Exception e + (let [msg (.getMessage ^Throwable e)] + (trace (ex-info msg {:causes #{:fs} + :filepath filepath})) + {:error msg})))))) + (catch Exception e + (let [msg (.getMessage ^Throwable e)] + (trace (ex-info msg {:causes #{:http} + :url url})) + {:error msg})))))) + +(comment + (def directory "./downloads") + (.mkdir (io/file directory)) + + (def url "http://joeconway.com/presentations/seccomp-FOSDEM2020.pdf") + (def timeout 12345) + + (def response (http/get url {:as :stream :throw false :timeout timeout})) + (def response-status (:status response)) + (def response-body (:body response)) + + (timbre/set-level! :trace) + (timbre/set-level! :debug) + (download! {:url url :directory directory :timeout timeout}) + + (def url "https://archive.fosdem.org/2020/schedule/event/seccomp/") + (def timeout default/timeout) + (def response (http/get url {:as :stream :throw false :timeout timeout})) + (def html (slurp (:body response))) + + (def avi-url (maybe-video-url {:html html :video-format :avi})) + (def mp4-url (maybe-video-url {:html html :video-format :mp4})) + (def webm-url (maybe-video-url {:html html :video-format :webm})) + + (download! {:url webm-url :directory directory :timeout timeout}) + (download! {:url mp4-url :directory directory :timeout timeout})) + +(defn download-all! + "Downloads all assets (video and attachments) found at URL `url`, to directory + `directory`." + [{:keys [directory download-attachments? timeout url video-format] + :or {download-attachments? true + timeout default/timeout + video-format default/video-format} + :as _opts}] + (if download-attachments? + (debug "Scrape" url "and download attachments and video as" video-format) + (debug "Scrape" url "and download video as" video-format)) + (let [response (http/get url {:throw false}) + html (:body response)] + (when download-attachments? + (let [hrefs (attachment-urls {:html html})] + (doseq [url hrefs] + (download! {:url url :directory directory :timeout timeout})))) + (let [url (maybe-video-url {:html html :video-format video-format})] + (when url + (download! {:url url :directory directory :timeout timeout}))))) + +(comment + (def timeout default/timeout) + (def url "https://archive.fosdem.org/2020/schedule/track/databases/") + (def response (http/get url {:throw false :timeout timeout})) + (def html (:body response))) diff --git a/src/fosdem_dl/fosdem_dl.clj b/src/fosdem_dl/fosdem_dl.clj deleted file mode 100755 index 6b9c556..0000000 --- a/src/fosdem_dl/fosdem_dl.clj +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env bb - -(ns fosdem-dl.fosdem-dl - "CLI script to download the talks given at FOSDEM over the years" - (:require [babashka.cli :as cli] - [babashka.curl :as curl] - [clojure.string :as str] - [fosdem-dl.jsoup :as jsoup] - [fosdem-dl.util :refer [m->href platform-info valid-year? download-all!]]) - ;; (:import [java.lang Thread]) - (:gen-class)) - -(def spec {:from {:ref "" - :desc "The input format. can be edn, json or transit" - :alias :i} - - :year {:desc "The year" - :alias :y - :coerce :int - :validate valid-year? - :default 2020} - - ;; -a, --attachments Download also the talk's attachments (PDFs, slides, etc) - - :track {:desc "The Conference track" - :alias :t - ;; :require true - :coerce :keyword - :default :web_performance} - - :format {:desc "The video format" - :alias :f - :coerce :keyword - :default :webm}}) - -(defn url->hrefs! - "Fetch `url` and extract the href property from all tags." - [url] - (let [resp (curl/get url {:throw false}) - status (:status resp)] - (if (= 200 status) - (let [links (-> resp - :body - (jsoup/select "table:last-child tr>td:nth-child(2)>a[href]")) - hrefs (map m->href links)] - hrefs) - (println (str "Cannot fetch " url " (status: " status ")"))))) - -(defn print-help - [] - (println (str/trim " -FOSDEM Downloader - -Usage: fosdem-dl -")) - (println (cli/format-opts {:spec spec :order [:year :track :format :from]}))) - -(defn custom-error-fn - [{:keys [spec type cause msg option] :as data}] - (println "spec" spec) - (println "type" type) - (println "msg" msg) - (println "option" option) - (println "cause" cause) - (println "data" data)) - -(defn -main - [& args] - (platform-info) - - ;; (println "=== *command-line-args* ===" *command-line-args*) - ;; (println "=== args ===" args) - ;; (prn "*file*" *file*) - ;; (prn "===" (System/getProperty "babashka.file")) - (when (= *file* (System/getProperty "babashka.file")) - (println "=== apply command line args ===" *command-line-args*)) - - (let [m (cli/parse-opts args {:spec spec})] - (prn "m" m) - (if (or (nil? args) (:help m)) - (do - (print-help) - (System/exit 0)) - (let [year (:year m) - track (name (:track m)) - ;; video-format (:format m) - video-format "webm" - dest "./downloads/" - should-download-attachments true - url (str "https://archive.fosdem.org/" year "/schedule/track/" track "/") - hrefs (url->hrefs! url)] - (println "url" url) - (prn hrefs) - (doseq [url hrefs] - (download-all! {:url url - :dest dest - :format video-format - :should-download-attachments should-download-attachments})) - (System/exit 0))))) - -;; from 2013 to 2021 the structure of the webpage of any conference track stays the same - -;; (def schedule-page "https://fosdem.org/2021/schedule/") -;; (def video-page "https://video.fosdem.org/") - -;; (defn shutdown-hook -;; [] -;; (-> (Runtime/getRuntime) -;; (.addShutdownHook (Thread. #(println "..."))))) - -;; (defn -main -;; "You can run this program with [babashka](https://github.com/babashka/babashka): -;; - chmod +x src/fosdem_dl/core.clj -;; - bb src/fosdem_dl/core.clj" -;; [& args] -;; (when (= 0 (count args)) -;; (println usage-help) -;; (System/exit 1)) -;; (shutdown-hook) -;; (let [options (:options (parse-opts *command-line-args* cli-options)) -;; coll-contains? (partial contains? options)] -;; (when (coll-contains? :help) -;; (println usage-help) -;; (System/exit 0)) -;; (let [should-download-attachments (coll-contains? :attachments) -;; year (:year options) -;; track (:track options) -;; url (str "https://archive.fosdem.org/" year "/schedule/track/" track "/") -;; ;; TODO: make sure that this directory exists, otherwise curl fails (silently because we use :throw false) -;; dest (str "./downloads/") -;; hrefs (url->hrefs! url)] -;; (doseq [url hrefs] -;; (download-all! url dest (:format options) should-download-attachments))))) - -;; ;; check if the current file was the file invoked from the command line -;; ;; https://book.babashka.org/#main_file -;; (when (= *file* (System/getProperty "babashka.file")) -;; (-main)) -;; ;; (-main *command-line-args*) - -;; (comment -;; (-main {:year 2020 -;; :track "web_performance" -;; :attachments true -;; :format "webm"})) diff --git a/src/fosdem_dl/jsoup.cljc b/src/fosdem_dl/jsoup.cljc deleted file mode 100644 index c237e84..0000000 --- a/src/fosdem_dl/jsoup.cljc +++ /dev/null @@ -1,58 +0,0 @@ -(ns fosdem-dl.jsoup - (:require [clojure.string :as str])) - -;; pod-jaydeesimon-jsoup is not yet published on the babashka pod registry, so -;; we need to compile it as a GraalVM native-image -;; https://github.com/jaydeesimon/pod-jaydeesimon-jsoup - -#?(:bb (do (require '[babashka.pods :as pods]) - (pods/load-pod 'org.babashka/postgresql "0.1.2") - ;; (pods/load-pod "./resources/pod-jaydeesimon-jsoup") - (require #_'[pod.jaydeesimon.jsoup :as jsoup] - '[pod.babashka.postgresql :as pg])) - :clj (do (set! *warn-on-reflection* true) - (import [org.jsoup Jsoup] - [org.jsoup.nodes Attribute Document Element]))) - -;; https://jsoup.org/cookbook/extracting-data/selector-syntax -;; https://jsoup.org/apidocs/org/jsoup/select/Selector.html - -#?(:bb (defn select - [html selector] - (jsoup/select html selector)) - - :clj (do - (defn elem->m - "Maps a jsoup Element into a Clojure map." - [^Element elem] - {:id (.id elem) - :class-names (.classNames elem) - :tag-name (.normalName elem) - :attrs (->> (.attributes elem) - .iterator - iterator-seq - (map (juxt (memfn ^Attribute getKey) (memfn ^Attribute getValue))) - (into {})) - :own-text (.ownText elem) - :text (.text elem) - :whole-text (.wholeText elem) - :inner-html (.html elem) - :outer-html (.outerHtml elem)}) - - (defn select - [^String html ^String selector] - (let [^Document doc (Jsoup/parse html "UTF-8") - elements (.select doc selector)] - (map elem->m elements))))) - -(comment - (def html (str/join "" ["

"])) - - (def selector "p.foo") - (select html selector) - (def selector "p.bar") - (select html selector)) diff --git a/src/fosdem_dl/scraping.cljc b/src/fosdem_dl/scraping.cljc new file mode 100644 index 0000000..c1b0b43 --- /dev/null +++ b/src/fosdem_dl/scraping.cljc @@ -0,0 +1,61 @@ +(ns fosdem-dl.scraping + (:require + [clojure.core :refer [format]] + [clojure.string :as str] + [fosdem-dl.defaults :as default] + [pod.jackdbd.jsoup :as jsoup] + [taoensso.timbre :refer [debug warn]])) + +(defn maybe-video-url + "Extracts the