diff options
Diffstat (limited to '')
-rwxr-xr-x | roles/caption/templates/process-captions.py | 12 | ||||
-rw-r--r-- | roles/edit/defaults/main.yml | 3 | ||||
-rw-r--r-- | roles/edit/tasks/main.yaml | 11 | ||||
-rw-r--r-- | roles/edit/tasks/main.yml | 32 | ||||
-rw-r--r-- | roles/edit/templates/emacsconf-edit.el | 52 | ||||
-rw-r--r-- | roles/obs/overlay.svg | 301 | ||||
-rw-r--r-- | roles/obs/tasks/main.yml | 14 | ||||
-rw-r--r-- | roles/obs/tasks/obs-setup.yml | 32 | ||||
-rw-r--r-- | roles/obs/tasks/track.yml | 15 | ||||
-rw-r--r-- | roles/obs/tasks/user.yml | 3 | ||||
-rw-r--r-- | roles/obs/templates/mpv.conf | 2 | ||||
-rw-r--r-- | roles/obs/templates/scenes.json | 77 | ||||
-rw-r--r-- | roles/publish/defaults/main.yml | 4 | ||||
-rw-r--r-- | roles/publish/tasks/main.yml | 93 | ||||
-rw-r--r-- | roles/publish/templates/emacsconf-config.el | 28 | ||||
-rw-r--r-- | roles/publish/templates/git-config | 8 | ||||
-rw-r--r-- | roles/user/tasks/main.yml | 31 |
17 files changed, 610 insertions, 108 deletions
diff --git a/roles/caption/templates/process-captions.py b/roles/caption/templates/process-captions.py index 50c62d1..223531b 100755 --- a/roles/caption/templates/process-captions.py +++ b/roles/caption/templates/process-captions.py @@ -95,7 +95,7 @@ def get_files_to_work_on(directory): else: val['base'] = os.path.join(os.path.dirname(val['video'] or val['audio']), base_name(val['video'] or val['audio'])) - if ALWAYS or (not 'vtt' in val or (DO_SRV2 and not 'srv2' in val)): + if ALWAYS or (not 'vtt' in val or (DO_SRV2 and not 'srv2' in val) or (not 'txt' in val)): if not 'audio' in val and 'video' in val: # No audio, need to convert it val = extract_audio(val) @@ -142,12 +142,18 @@ def generate_captions(work): result = clean_up_timestamps(result) with open(new_file, 'w') as vtt: whisper.utils.write_vtt(result['segments'], file=vtt) - with open(work['base'] + '.txt'): + with open(work['base'] + '.txt') as txt: whisper.utils.write_txt(result['segments'], file=txt) work['vtt'] = new_file if 'srv2' in work: del work['srv2'] return work +def generate_text(work): + with open(work['base'] + '.txt') as txt: + for i, caption in enumerate(webvtt.read(work['vtt'])): + txt.write(caption.text) + work['text'] = work['base'] + '.txt' + def generate_srv2(work): """Generate a SRV2 file.""" log("Generating SRV2") @@ -218,6 +224,8 @@ if len(needs_work) > 0: # word_cuts = align_words(cuts) # convert_cuts_to_word_timing(audio_file, word_cuts) log("Done %s" % str(work['base'])) + if not 'txt' in work: + work = generate_text(work) needs_work = get_files_to_work_on(directory) else: log("No work needed.") diff --git a/roles/edit/defaults/main.yml b/roles/edit/defaults/main.yml index ea0fbdb..d57c511 100644 --- a/roles/edit/defaults/main.yml +++ b/roles/edit/defaults/main.yml @@ -5,3 +5,6 @@ emacsconf_edit_packages: - vertico - magit - modus-themes + - hydra + - orderless + - projectile diff --git a/roles/edit/tasks/main.yaml b/roles/edit/tasks/main.yaml deleted file mode 100644 index f77535f..0000000 --- a/roles/edit/tasks/main.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -- name: Set up Emacs configuration for interactive editing - template: - src: emacsconf-edit.el - dest: "{{ emacs_config_dir }}/emacsconf-edit.el" -- name: Check if Emacs base configuration already exists - lineinfile: - dest: "{{ emacs_config_dir }}/init.el" - state: present - regexp: "emacsconf-edit" - line: "(load-file \"emacsconf-edit.el\")" diff --git a/roles/edit/tasks/main.yml b/roles/edit/tasks/main.yml new file mode 100644 index 0000000..f864b23 --- /dev/null +++ b/roles/edit/tasks/main.yml @@ -0,0 +1,32 @@ +--- +- name: Check if Emacs is already set up for publishing + stat: + path: "{{ emacs_config_dir }}/emacsconf-config.el" + register: publish_config +- name: Set up for publishing + include_role: + name: publish + when: not publish_config.stat.exists +- name: Install Emacs packages + shell: | + emacs --batch --exec "(progn (require 'package) + (let ((packages (seq-remove #'package-installed-p '({% for package in emacsconf_edit_packages %}{{ package }} {% endfor %})))) + (when packages + (package-refresh-contents) + (mapc #'package-install packages))))" + +- name: Set up Emacs configuration for interactive editing + template: + src: emacsconf-edit.el + dest: "{{ emacs_config_dir }}/emacsconf-edit.el" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" +- name: Check if Emacs base configuration already exists + lineinfile: + dest: "{{ emacs_config_dir }}/init.el" + state: present + regexp: "emacsconf-edit" + line: "(load-file \"{{ emacs_config_dir }}/emacsconf-edit.el\")" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" + create: yes diff --git a/roles/edit/templates/emacsconf-edit.el b/roles/edit/templates/emacsconf-edit.el index 2e360c7..481892b 100644 --- a/roles/edit/templates/emacsconf-edit.el +++ b/roles/edit/templates/emacsconf-edit.el @@ -1,7 +1,10 @@ ;; {{ ansible_managed }} -(let ((packages '({% for package in emacsconf_edit_packages %}{{ package }}{% endfor %}))) - (mapc (lambda (package) (unless (package-installed-p package) (package-install package))) packages)) +(progn (require 'package) + (let ((packages (seq-remove #'package-installed-p '({% for package in emacsconf_edit_packages %}{{ package }} {% endfor %})))) + (when packages + (package-refresh-contents) + (mapc #'package-install packages)))) ;; Configuration (vertico-mode) (show-paren-mode) @@ -19,16 +22,45 @@ (split-window-horizontally) (magit-status "{{ emacsconf_edit_wiki_dir }}") (global-auto-revert-mode 1) -(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) -(projectile-mode +1) -(setq projectile-completion-system 'default) -(setq projectile-enable-caching t) -(setq projectile-indexing-method 'alien) -(add-to-list 'projectile-globally-ignored-files "node_modules") -(add-to-list 'projectile-globally-ignored-files ".cache") -(add-to-list 'projectile-globally-ignored-files "_cache") +(with-eval-after-load 'projectile + (projectile-mode +1) + (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) + (setq projectile-completion-system 'default) + (setq projectile-enable-caching t) + (setq projectile-indexing-method 'alien) + (add-to-list 'projectile-globally-ignored-files "node_modules") + (add-to-list 'projectile-globally-ignored-files ".cache") + (add-to-list 'projectile-globally-ignored-files "_cache")) (setq completion-styles '(orderless)) (setq completion-category-defaults nil) (setq completion-category-overrides '((file (styles orderless)))) + +(defhydra hydra-emacsconf () + ("e" embark-act "embark") + ("t" emacsconf-go-to-talk "talk") + ("c" + (find-file emacsconf-org-file) "conf.org") + ("C" + (let ((default-directory (file-name-directory emacsconf-org-file))) + (call-interactively #'projectile-find-file)) + "org dir") + ("w" + (let ((default-directory emacsconf-directory)) + (call-interactively #'projectile-find-file)) + "wiki") + ("o" (find-file (expand-file-name (concat emacsconf-year "/organizers-notebook/index.org") emacsconf-directory)) + "org notes") + ("a" (let ((default-directory emacsconf-ansible-directory)) + (call-interactively #'projectile-find-file)) + "ansible") + ("i" (switch-to-buffer (erc-get-buffer "#emacsconf-org"))) + ("l" (let ((default-directory "{{ emacsconf_el_dir }}")) + (call-interactively #'projectile-find-file)) + "lisp") + ("b" emacsconf-backstage-dired "backstage") + ("u" emacsconf-upload-dired "upload")) +(global-set-key (kbd "C-c e") #'hydra-emacsconf/body) + (require 'ox-md) +(server-start) diff --git a/roles/obs/overlay.svg b/roles/obs/overlay.svg new file mode 100644 index 0000000..73a7e6f --- /dev/null +++ b/roles/obs/overlay.svg @@ -0,0 +1,301 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="1280" + height="720" + viewBox="0 0 338.66667 190.5" + version="1.1" + id="svg2360" + inkscape:version="1.2 (1:1.2.1+202207142221+cd75a1ee6d)" + sodipodi:docname="overlay.svg" + xml:space="preserve" + inkscape:export-filename="video.svg" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"><defs + id="defs2354" /><sodipodi:namedview + id="base" + pagecolor="#000000" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="0.62998576" + inkscape:cx="543.66308" + inkscape:cy="369.05596" + inkscape:document-units="in" + inkscape:current-layer="layer1" + inkscape:document-rotation="0" + showgrid="false" + units="in" + inkscape:window-width="1366" + inkscape:window-height="709" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:showpageshadow="0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" /><metadata + id="metadata2357"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"><image + width="28.984987" + height="28.984987" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAMAAABrrFhUAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC +AK7OHOkAAAMAUExURUxpcVhRpFhSpohOtnVRsZdMun1Qs1lVqZxMvHpQsoFPtJJNuWlTrVlUp3dQ +sYRPtWZUrHNRsHFSr1tVqV5VqmJUq3tXtW5Rrm1SrlxSp1lSpW1SrGJTq2BRp29Tr2pQq4ViuVtS +pVlRol1Spl5TqnJPrVlUqFxSp3ZPrmdSrFpTpnNRr2dPppaRxFxTqXtOr3BRrnJRr1pSpoFOsKFK +vJNLtXFrtYllu15Qo3pPr4dMsqxKwMvI469Jwbat2HJRrqpKv3p1uurp85VLto6HxKtKv9vZ68rI +44ZMsqxKv8vI5O3r9WZgr/Hr941OtdfW6/Px+Kuo093b0dHEzf///1lQn1lQoFpQn1pQoFpRn11Q +oahKvrNJwcBIxZ9LvadKvrVIwo9Nt2BPo6JLvaRLvmdPpYtNtrxIxK5JwFhQomRPpKpKv1tQoL5H +xLpIw8JMxLhIw8dXw2xPp3BOqcRQxF5TocVUw8phwclewtFxv9uMvFpPoNeAvXVOqv7+/9mGvN2R +u+ist89swMhbwvf2+lhPoKxKv3hOrMxlwYJNr+OfueWluN+WutV8vvr5/P38/uqxt21ZpP7lsHlx +sodMsuGbus1owZNLtrFJwWRWonFprfLx+H1NrvDBtKeizWVcpey2turn82BXo51Lue27tdN4vtJ1 +v5uWxvPItNPR5u/s9mpiqfvdsdrY6nNipL252rSv1JhMt+Xi8PfUspCKv+Dd7qCbyYuFvK2p0c/M +48PA3Xxnpa5cuoZzpfXOs353tY1Yr35Vq4Z/uri115Ntq4NdqoplqnhYqIJ8uMjF4Kdps7ZruJON +wdzO6aaczLtav5disJR9p6mIzKN/rMGMs8uVtMem2550radsxZ9Xt/bksMV3urGTrOzgsLKEsJdX +uLx6tr+irsy44LdQwaBis5t7xLSh1LJytbqZrZyOp6pRu9zBrs2isdbD5ruU1L51zs6Autmq4siF +t960tODbsN+otdems/np6OC55ePJru7S6cy0seizy9aX1od9r/ziv2qV4xcAAABldFJOUwD+/v7+ +/v79/v7+/v3+/v7+/v39/v0MEv607CBP/v3+BdX34mD+eZDsLci29v08atp+n9ZUOf7+9Zemic+y +I8vt/Sx5/tJJs7tqkLT9oObbaNf27f/////////////+///////+N+r7SgAAIABJREFUeNrsXEtP +G1kWbpsY/BiDDATahu7wJtBJpifdIZPpTE9nJj3RRJFmY5dJkQAuUw52SJq2QzbIWeFY3oSFLYGM +hYVZIEAWD/HeRAobI9GMlMVo1tPqLOc3TN16V7nKdcsPTCK+FZSLkr9zz/nO497is8/OcY5znOMc +5zjHOc5xjlNCY09zR1dXt9Vq7e1tt9na21tbW3ut3V0dzT2Nnzbzhmsd3dZeWxUPXgcPIa+3rb33 +ete1hk9w1Zu7e21ec5UYAgMwQLy23u7mT8cbmjqsNi8CmEEagMKDq7fv/f0T8Prr7RxJbZYBEEnu +E9GdgwU7wNw/73//Eft9R68mJPBtMX9zNvmH0ZP9KTsPr9L3/vpJsJeKAY3o8/jSwYpdApsfmw1a +mnslo9ubwwFSW2sLdlm8St//eHJDU7dNTtw0MgoQ31l+Y1fA1O2PQxObW70heW3n6aCZtlJya23F +Doft+y1n3fc7WrMCW5TkGR3QkusfnVdeej7m7p3lrNDYZZPPbZwJNFqtBghk8vhgxa4aU7e/P9v0 +ibV1QICI+ml7fnhzJk3Q0GVTKG94hU5mfcFeCM6eCVq6bDnSuwCp3bUVe8GYunemsuK1VkF605bC +8cVYuX9m6PdYRQWeVrLF00T3tl/Zi4j02agLGq5ntTjZBkDq7x7M2YuN6dtnIA6u2ZCsLjdLAzT1 +mcwdAn198fhqksBDkS4kkw/6+vruZO4e78yvry1vwtYGW83lTn1Wr0SXi+QogGUEUtQl9K9m5vch +rJBGrWUdnDS3IRJdrjgCQhAjAI2EDyV3lNNFBvlj+ZygweqVHHR4c5LTKPUIvJtSB0oGOHBgn98q +kxL02ELi+l56zGVWGoKIjVRXx1jp4ZpSb4D+7ssvv+4pB/8uHtGQNqvHy98AdXV19aybTCiVjO9q +AX5/+u7fK+x5mYmvRiLERVlC0gAhPv36eu6DPQUDrAP+FstXDaft/hJNnkYjLXAwHkCLAEW/niej +80p5wELhdMOgo82hAloIESR1hKZfX88Zsn9TqSm4YLFcALh8itmgG1HDX0kjmbsY9hfr+7mrh4qJ +8CZJv5rAH06r87Oq4w9VCJGeAthfvPgFdyV5oFwL/VbN4tapDMwaWh2qoYFoFKmGSTPGGxsswfTN +7wnmBhqnIYVNNkce0FBRYNZIaKdX2qHie3Bjgw8GDpXfNJ1N/nSSyB4Xa0jpM4tNkMrsbcK2hCeA +OItSW6BHVv5DCIKotoqWUX7uT1czS2vbaoYmO9Uc/Uqj8XJPWfgjWsrDQyr504nv4kUi4uO782vL +6qdlW5gBEKfRiVy5UcLev01J5MxqvACh6D+4c3d+bX8h31HZWwcaZvn7nQ5H6SzQ06ac5lRYgFjy +nfWD5YXCxmTTKeJRvk5jTU2NIYCSDy6VBXpsWmkXR3KPw0Jer6D4SUYJ4kfpleLMB7fpp46hnOmv +lEQHmtq5Da1cpS4irQ6OVPzd0t4B/LQLEutSS1IKC1D8IdpdLtWnVqNvd6+e7B0e7G+vvLGXBlHJ +8LrSVPz6T3amJZx41fdljucPj9KbJaMsGQFiXC7yqLDFKt/P0hJQ13d172h7zn662JKT2K+L2xd0 +5+rnqqoe3D1Mv7GXAel+2Rz7VVHHX7mmvvEib/io2SSNy2fZsVtFnH7LH+2Z2Erby4elXHUGWrRB +YZNNbuNj4mSujPSlUyAH7EbREwANphx6t2AvK/+HCrXmleKMB6xV0gaYOHxVTvrTO8rV9l+KIoAy +5ztT+2Vd/oW3EO3GZBGEsMcrfb5zYrmc9Kf2UlANV+Ey0GDLPt8ZghvXlpD++ipsy1mwDJATYG32 +3udu2di/2b+aUjF0KLAeavaKtv6YANgsz9Iv7+3Ks3diGFrkaqCxTWbks3Q6fOcWNreXl48O1tb3 +5pd23+Z0fF9Ep9frXeExcRAU0hZZQ7zjneYqs5bpdUtW/k2vbP/y33//7/3u23iyX818MainYcJF +hvlTATNAufnWavEje3P/8GT3t5s1BCoqKrDcbNGwx2SKgQEgg/AgC72PHxZhvf5GIRlAGkWUwFcL +++tLGdq5cYp+RSA3f9xELbaHDXl8kAfTJHdrhPj9x3wzwXXZIXdxJGAqfXg1KtQ0n5GgX+PPzd+n +I+kT1FxMwLvcfARFjvFdniWQ/IB3p2CPT68fx9kYRzsrjQYqdJ0+3KcU+y6SPIkwXe8I+LtdbB00 +6HaD2/IbEebYBD0uaOH35zMTgog2gKF2TQ6/R4OxigQucndAVUdd8o8IMMgGAG2RP+dVAsg5ABJy +RPMW+fSHdxZLrVDjyKE+AUwh5hNO1q2ZtR6h/iYoNMAIIwI64mfyvm/zmALaZPbAQGdgzmsMsLK2 +lbxkIQ90CLhW0tIvF/oYFfN62uETNHcSlOIHhgRwM3/IGMT9Y0seTWCuPaA19Wq/v0v4PUbyr67+ +nP9QIyX9HrkYSNCSN6gnVT/oZngNDY2gNFOBAWYYzwG/ULf+I+8aUHoPrE/tTt4yNb/3WcjjLIYL +/KcaAH+Px4PLGMBEi57bTZrIR5MH2KBvmRngI8w3AGWEH9Smwu5Q7j2wI3XOz0yvfeRpHoOhWpDW +KP5GofpjgQAdKHpW8yhmLnapB5iE5x94ymGRSY7YwAB769+K4QC8nqhPzRQ8zRbxKMXfcEnwXD/J +X6ALkwkQ+BGSygwteiMjVJBgi4A7AJvvHDGO/wCnJYvkXaQBfmgsQg3Eb4r34Pkf8ZIeTp7nqRZ1 +LKg/gAvXP0zpXoRNZqSW0ZWfj/B4QDXGPcYZeU1jhKelEdIglA2+K9wBBLOxum3o8BckfV+nobqT +KWGdeGenT3Kor6OFD7gFukhHfYL1j0BsZiZCagbCwD/zdHR0dCiG0b+Thh2hnYKAKhfoVt4Gr+qb +gnzVMSlf4HSC4JcSf5zW/UFK9vRA80YiTuHXEQPF/T7RpfDr10xgDKhwgQaZkxDC0RhkOfjhC/mi +nhQ/DyrxCSN8lD+PBWOeCJ6LvBxio6OMDVQkgi6ZLsgsnA2vQxngZu0luZoyAPi7XLygdeLhIPB6 +REcJ36CEcRBVCIPAoGwAXwvItcFe4WwYajA8XVtby4a574Khkyf2AZK/i1taJAJCHxgkMEgKX7gw +9gBYbASYYMiFXy64CxC8+03clITZGTLUWpikh4ODbJWcBTBA3+XhtDxASr8OXAgQdbw+WCh7Cngg +gBPPxL4tuA0MmYWzwSiEEL63WJjCt5o8z9bJ0zrCAh5eGohR2kfFBOZzFoM9B8c3kHuhOf61S4g+ +88ocFXqnXA+lLRdoD5gkj/PV8KtAFPfx3S1Gap/b7yjc8yU9Ae7gzHXlI6/cl96ahnABRskMRtD4 +Mh6AZbe/AVL5N9CS0CcANR9tIXIgGAFXmbUwZ/6WFHdIp9hzTLiRa/tRo8tVyTHFwgkiFJwxQvoW +/aVhTyAI0xV3INx/fIIxgbIFVla5Qriyml53AyF/JlbkcB2t/n4qDZaEPoKMwchgK//1BpiDn8eK +UbAgcZLF4zKZTDU89RscnMkZ+oFIglfn+QIBNA8LQMhgo/A1R3kLeMF/wiA/PVZUwrlM1p/XEPxN +bHHvAuKny0EfWxweHh+OOWk1W3zy5Mn4jHoTBJQbgi5zFcw7Tsx2IXlgKqOYDafXQUc06ePcGyc8 +wIXxxvdud0xM3xkZebrhB1/cuThOIkbxf/SMxBBlgX4aEAaYVB4L9IpPQyC5SyLSQFHlU+7puAMz +VlQY2KrHFwxyGoh63G4XKl7+2Ozs7KNRHKwcYP/8+fNHY+D60LNnj0l4GO5oYmNxI4IqG0IxBhqV +34UXFcWkgVaVNwunD42SvR8Wm0kgwqSIhcNgbTGC/vDw8AzxYwTQJ9z+GbCG/zGNn8YnKcrBX38C ++DXcr+QN/kaVESBjAK341GAK4v2uhfdE7U9V935jGOWFv7Dk9w8QfT3BNADoD48PEd87Adg/Ibye +uNwfAdwBXrzwUfxfMIj09yvYQCkGWuEMYOa96UtfOoGYk27/xxWgBv06HR3xY6D2EUQ/sjg6OjtL +rDsO6I+PL4KFI9k/ezwLmEUAdYCXL0kDYP/6+eeXFF7g/QomUIiBBg2cAXj82fd8ozCt0S9kVZTQ +EaBdwMQOO8Eg1EcE/2si9Gf/T9u1BkV1nuGBUYJElFBJaacLbVLaTGfsn8Y6TScznWk7Y/7kl+xN +mcrqclkWgV2PIBdBYLjJxQWCAYFF7gqsEu8uS0BZqBIFRGJVVFCigNpY7xoz0+9y9uw5Z885+22E +99duos4+z3nv7/t9ZzP4sd3I8Rnh796LzD4+A+IyQOjbtyclJY0o4Pe+JKfcUiqlKTD+2k0h6O2R +BgD8P3Ouxx8ibZAbAf536VJHF9ldSHtGw7/y4jLk8iZg+xuh5av3JibG5OMUJjI6ImIjbePzCGtO +Tk4f+tqTgiQHSo9SKU2Bm1zoF/yrHERufPBm8K9iE/QNSZts57kKfe1S3AUw5rPzvu68mBjw5A1x +GzdtwEmP2pH7AEdvMChoUNoRABVA7tGjryPJWBALDHJ1YdP8fHetmk/BOnetEP93CO4EkjP4V3EI +6ugimxJVOKbXm31Zwa8JdjWh+8/P4OQ4/Keq1PZAwLfosNdTgASRMOL4I/0jwCNsS0jYVMtj4CN3 +lTBZJhjrwM+zEOpuKRkFaM8RRP8tWpqKbp08Hzi/WherVQqJbmiIcnyeqaIFsOAwgb4c6Bi3QQoq +uXagk6qJf+dyBChW4kg4wC9zmeUHr31IOC05g8Z8qP0Trt4Qlwes3lBoJILPFVM2LYCDfvr5Q5eA +KUhIqOUy8HdJF8BlwD9WsjMgdOpXHbjkEuHpx64T4Vo6G9TExcR0CwQtaeQKWmZ2QIEU0ApAAbeA +GEiCDNxRcxhY574bSocCfy/PD8MCBkLDlhwinJ0eOwoXvrW1uvDCvC0GD/ArOKI/34oo2DFMm4Ud +egSnEtRyooGEE/h5rPMksFdsLOkBULVMJuPNuipIN4mPnVCpt8Rt4dd1GXsL9dDzV3ZXqqXBY7EP +t7a2Dtvh/6O9QgEKC5iBbk481IrPB34l1QgTOeUergxaDoU//jhDukv61eq4vDyGAE3UXpDtFoK0 +B/xq9TzIeHCso4aGNGLokVDVFPNZOZwNPSLDwDw3IfjY424g3SIS9ggIf3BwCKeRow/zWUl8mmT/ +f3GyowE0NCVuzUuTbwBpX7xamQ/TvRzo1/oLqrKr+hWkMow8IsPAPDcl+sTTfriXVFaoCcD4Q0K5 +az+w/3mYdIa+v40Cj33Tl1p5DKh61PLNoNq5A5J+AD8lBaR72qrsbODlTKQEnIcukVYCwMBVblIo +Xg584HYe5GoGMhp/YCArImoQ/pU6MzEFBy82NiUmFsqjoqNB8WNIRMHLAOAnJxuVylEAf0dr6yh2 +eSaT3g0BdhwUIANQB/pZBKiNtZ+LVkKx0sMQ5qwA2wMgAiD+QD173xH1f0GO03GWlIJ9d+98CWvg +DGjzoCpC6UxKcgHM90Gca21paUEEDD7KzHw0IE0Adb2VxcCImqkLDMDEEhPf92g1OFb6PnyZQwEC +2aEQ4ccbvzWnSfeJ9tU38mOfbqgaOj47gF9Skm4H2AYyMzNTU1PdMGBvdTAAjKCfiR9R0fHR0VsT +10jlgZ5eg6Sm8b/H8QEGuPnlaP7U3CanwCwXivnqYQA//R74SEH0qVlZqdXSDIy2MAz0Mf9QU0Q8 +IuAzyTxQmgDX2kiGn39wGnfAHxbmHPrqbn1Lagi5h01CUZ8anZoaUMNHC8ADKS4edOcGsBVUJTPP +X1kZQRPwV4+CgDsCwjXBAH+oXjAr1OnpEWDHScKFkvWltzuU2r5+kaRnEIIH8sWAu0hA2c8PD8/Y +nXmUNiGCZuBTj9YCCK5D1KsVjnEfhwdjVCSz/Kfzu0uaF+SerUrBiusKywTBAymy4K8D41esEiyw +dambIeCfHkVBOcGFmDT8Jct8fFjrIGj8H6Vk1l1DjpPeEVP2shWUNf3Dw0MukG4C8EAuo4A4XrR7 +N/hcTcKA/k5CAk3A30QqAbnbrQjJm7DUK+CNRn5OZ2iA4793cbln8PX1zQhvbCPdLSu7XWOCoc8l +96EuFwHUEwjz+G4sExQBAwZQF2IV2CoSB8XOB7Aug5McFYaiG538/Bgr0EACIvFf0Uf6LoVRUf/9 +S0JnUPYwtaUkXcDXWcfHrSgTsuxCAhi4QkBAhpOArWs8K4WYRqn0sPg9jN/PufFhBPgdPiBNqw6H +LlGbb27rIjt0XPY6Kx3YwNC9qQHBImh8l0MmCNxAxvZtjBP42IM0wLEk7+/vzSRBallQQIBLMygM +3+jEXvvXANThugyt4/gCbIWn7Y1UdBwnPHZ2oUJhTQV+f1wI2uweLEAJCAgwAAISJAn4PWn9D+EH +gBqIt8iBVqC4GzDI+pcu9cUp0VLUANRv3pwWLpdXkF2XtvPcRBb0+0J+brIBCqTgGQEB+hEnAZ95 +kAe5iiwoCOFfvpynA2EQvx9/8aUQEBBFH+2rTMOrMbgqq6gn0oMXb4DfFygE08aam5sxB7MKAgau +OgkQzoT+QIZfyeAP5jVBlNoVeA1Yk++3jPEEcO2bGX0Z4RSs0WzuqKmBt4w31tS7Dws7AQWCRk49 +aUbS0GAlIQCHAQkCCG+K9KYNANQAwSLxcGUUKwPSRPlG6swV37WdPHTu1LGD+3h1QS5BnbBz/YEK +hWps4vkkrQgqLNYndXV1gIEx+r9ax2/eHKgWjQPdDAGfvg0BMkYBQkLShFeg0f6nH7447Ou2w6e+ +Ik2DpQYJ00DXm5+YHOCRmGab6+oe2DBQywQMiUXFA2IE6OelCfiQkABGAUJChE/4rUAERNV8c/bA +ArxMw+kL2gHaSRVPqimVCuN/tgflBEVFV8SSQXX3QhFAK4CICYRF3br07YVFuF/mRW/dE5WQwLT4 +AYyHUAWKvrCK1gMZGySKAcLbQpWMAgQKLsJTFfUXFu1WqRfCBAAGbDAg0gRcFiVAKdfVVlYWfv42 +BAAVoBUgFNeAmlDnsY+OtlO56xdTSo+LMDDZTBMAGagWJ0BiNvJL9l0B/t7i94PSfdBQ5AGUofAU +kE+YHr5CpmtxL9bJffFDo0qEgOlmlgpY3oYAuZfbS6CVsoDQADrhwfh9fNbWH1tM7GVdJ0+MjVkg +1sa56ekxis/AghHAvgic4J1Z+iWwCPa79LBsEdHvP3TUzCC1vOoEct/MI2C8uZkhoFj/0wngNMEJ +GNAA/GtvL6LdHzy9uoMNlHpVjuQ+jwALi4Cbip9EwIeuDTA3r86DBCz77sKiGX7pgfoKarKheZKl +8LZyWqw8BhgvWFRsEiVA7pYA/3dI3obg3Iu6uH/xjP7fsNEzCbKfdlb6M1lefg1K+RyPAGqWJoCX +BnhGgJcHLwYG8I8fXCR3//oNHcv1DQB/ex0DM3zuGi1j/DigH3sOCbhpUrizAJHp4G+ECJCwAfPJ +RblMMPfc6prioiI6wVc0A/y97Sxbv0GL2SUZBOmw1cpKAUyDo6ODJqGduT+JlsPe7l8b5kj4ji+G +59v3+s0g6nYzajxZ197bO8u29RtPnwL8cyoBAjgN9Kn0Ejge6jG5ErBOtCHiTagBqqMLb/ulp9o6 +HjmnfrjTq6qerWuf5UT9uadA5lRuCBjMTMcEZBf0uxDwiWhLjNAEvl7wpOfYyx9LKPizH2MdNk3s +emDBlb+ej5WyWiiVGwLsmTQBcDpm5G+N/kW0KSoX3xP1kgFBbUDzAl8lWdZVX6MwzXCGIJd37dnz +3Nn6UFlxFsgV29ycjRLETz1mFKCqIHkkjUeA8Kbcb6XCoDoId8JkStXqfQur+D/usDu73noLduLP +AAEN1Q78lvud5eXT/NTnfnln55FXNiECBlkKUMAMyBkChG+Y+6PrNgCjAF4YP6gDKxbyHrWDZ89Q +QwVVM0oFde86nP7rp7JwRwdoQMNzhwJQIPsFqQ/X7i03AP7OI0d6bQIe4B4ggFGAlJyrvL3pNRJL +ct5CHkC+ilaA5RcXzvfv/9/3cA1E0TcDzH6wpeQR+OFDWcXFEywfQKt6JyLgFYeAH8qhAgAC7lOu +BDzmKEAOb0tK877UcNRLwAP60wrw5wW7SDX35X/wqrfRiKbA9pL06xB3VjG7nUFjHcMEXGPjNzvw +97bbXGPgI44CJN3hKoBBejwe680fhDkU4B8LZP37Tl+KiIjHS81oCQha7Xlk/AMTly0uBFgxAZz6 +x0YbQG97+5wrAVMcBUjiLQp+5G5BAr7wiL0XGRsECVh+eEGcf+ntS1p5RnzEFrzqnlzQI7L9aJud +tjnUHTHASX5tjuffXicwNzvPUYDtkdyTE/9n7tpim8jOcB5AQkLipQKE4KFSUXnoUmi3F9o+tFW7 +r33CdjIxuYwzYNlD4h3nRuJJwhI5GHLBSsA2G0iIydKwbYIhXBM3Fwgpzu4mCEiIsgogYFMBJSzV +rlZbrdQ5Z87MnLnaHnur/G8sQuvvm//855z//P/3v5tWiYwQAn+Vleh3+cqj0n1FLqu3ky/dGKs6 +DPaAYCIRVL77dJ89e+qecNG59LeXcXk2+JKA/5T6WSRv0teLOUAlk5tSufQ2g8eg9X/Mwr2n7vqI +1Rou3bevUyqFoihuBdALvb4FmMUYun8fHQW/AgScFU88EVUKSMD/tVbV4BLuADFF68xv0i0Vtq5f +/+fMo3/bN8ugHcLBEcBAAoj5Q8OwHNAW6W3x+SIoCB6BNQH0MUDAqa48PYu85PF392utn8iCiP/9 +F4SidUivWHiDvnTGZxlnvOruPAzbi+yUlYoWBvhiwMD7h6rn+VKgqRbfYxAKbjVzBPAJnVeAAHkW +vH8I94OuJwD/q37tCBKZEiMgqXAAYkN6pbLZUJDc3ddu9YM+UBbrBBjlCBjmCYjMzMA7wBAgYA69 +9HSf7cbTPvGXXNx7glMQicf79d9EJ6bgEWDMpuwdeydt8ZBPMg3/Z5a9VmuUw293YQRQ04eqJ+XF +YHmL3DmgB53n4/EI9hIYvwTtvxVGd2B5HqSCYQiN3rmd6ZbLj2f40NP4nd/ZYbVSjiKpJYps4g5A +5CTf6DmRuE2I1/h+eUwTwb7kCbh0NyX8NoPmQf1y+a2a+Nszi3913zx1+50B2LAl9kUwpWXl88LP +mznwUe+SLhJh/f8D2RM1/ODc4uK9obxk+GnKC7W29MW2NQvlejJLfZx5nu92+/MVIl+OPWXllSz6 +fR9wBLREhMYPrBUofu9ul8DA0F+RvVLCp+8dOXL8eHPz26AhfiYW5pZguKSDNGgg14qCNzL6/AM9 +lqYCpIKAmRMQIGRqToCCQB74XE3zWyGuRV4d6+4+FkdOEDmFCPhc6fuLPP6GmvqgPn4iBjrR7eGw +0/mekXpClhV0PxzX0UIp3iP2vnO7AUdAgr/G1zQ0NDegssdFkBLo/k8ErYN/neJNGfdvcfg5+A01 +NfULhAZ+SABZ6PFA/BwBv0vWOCmzjzMJAKKSJK5gUBzygl7o8Auv+BMnZybQHaZGKgSnvzoIGYiL +DgHgC+UwUoh4IOKv903o4KcLJPxOo+7pzdZsLoCBCrUcSpOHOwgGrDqNcI9BKwAioOLoQcDAsbiY +5rr75NXnqn0/eFzC70to4wcCFMICcPoN5aSUQWDE/Amg7ZyGHgztBwR4SOkqMD/8TApet2EvBFoC +DwADB4/1G1fFDwnwQQpkShs/4cTwv5ekfV5u5hMgrYNagjguDyBgv1dwAdd01eEThynRn2/V14hB +cOjoUY6CRVsyAoTP39LSO6WJ38oi/BwBfrexsKTiJHDStAM0jigkYWh4BiDsgIBSl0AA6Io68cGM +hIfAtsGh+18/mEvWIBYRPz93AX6mid+6F8Pv/mUSITFZFLhg+vvLlZOsZMgdjnrhauQICImdUY8A +AScSuSm3RNoi/YpCwMfw80P8Bya11SNiYgD0u/+QRE5sE14Rn2c2BdY6rnhEL3ECXRBwGmCj0U6b +SMBYdRXHwGjKBMQfHD165L4sLASR+3P3/4SOekZUwu9OJjS/FX8LGTHrANuV9cJAFyZsj6n6wolh +zgWGCX4jCCbeLMwZdoPdOwLtuKyLYAJ9/gNTpM4FICThz08mLLoR74q4ZhL/TWUvUYwnIKzujK8d +Gx2j+Z0wstDCufIC1v2h7BEdAuA5a26Q5c8mp2D6Z6ZC7wIUkPDnJxUT24Rlw03uAX+vQBJRjhJH +B6ylCjkBA3a/jACKdcnOAs96QV7Id0vaEmqam2UdkvcBeGANimLQ4MToZIW+egwl4U8+amEr9h5k +Lg3YioTk1ziAGnQUMNAECQgX4/JwjrKy8hgmDmCb+ggyIN4L52B/YDOGFXbMgbbBmsdGl1+1eE5U +wF+YXFBw4w9EF6gwtwleQ4KBDkhAIegVqC0GBERJ2X0QXAce4QQcgA2yCeGr1vA9kpK7V/B/Bq2j +b9PCb2X8CH9JCuLK0howN03owx5xvgk/BgC8KNvYUKgTl4nwQj2w8kpsFczADukW4ZH4FmyS5ViQ +FsFb/r9w2/6SEXwN/ahOHn9hKsNGtohvwidNETDLa75TIgGUljJmAChjcS7AYrXcCdAinxBXQD3f +Jl0jrYE5gB2eem4bwNeSELN0QvyFqUhq/vBHQl2MKQJa51dDnUgXLw3N/U9Jrfswu593AQb/5WPP +ZqQigds+Hm29BDbyBp55fL7HtCZ8gt0bCgVIDfwWCxMDQwZSGjWyTSCgywwBnwoyaasRATHNhADt +hww49AVSIm8g1vo3eNnTAjjztSxFNL9+4CnsiyuNkWr8XBxiOkKpCcxvFgjINZMMfc0RAHVxGbgG +CtG8A7WyoZP7sYUuA4GcCbAp+lomZJUft5eWEqieQvEP6SiADxsinIwaP4xLKQ5c2iT8EzOVQA85 +ArxIKJwjQBQJlp4l0BAAgg2wtZL3To4FlRQEEwsLiWAKSV82A1NhAAAKH0lEQVRFb3Spx0lqwLdY +dqYqri78gwETBAClQNQvRTMsQytuxGTIXuQp9CqFkpqmq6urhhkDpSBj9Lm5LIQP5QE4i2nhp1Me +tyR0Dmw3kQUFBGhpCaDP7+BVIlk5A2wlkIupqqYM1ZJ0wQNziJ8fZr8oNX5LyvL6Ymqwq82UBzRp +v7BCnVi7HTLgpGUMPAV6QRwF84aSUUayYuR+6fOD239Ajd/ys9THbAmZsT4zMSBQq09ASZgXSvV4 +8UsRVV5eyUtE5po0qxf7/OD2H1LBt7yTxrgt4Y1s0MQuwNr0HtnBewCvlOvxNOHXQm8ZUAkFFNTi +KnmjMxOkCio9Nj88PD9Wqzj5sAi+kPwKqfBb0hm9KYzZyUv/Veiy7kBweAMAcqGAAS9+LyShTCxH +wTQmoAN1IqtGbUotSV4k7gUlP/gxovfzyY+9KvxUWpOWfm4+DG63GDAAtaK5H+mmZap5sT08BR3S +mhaEQmfk+Kd5lcDK8qeU/N2vCIPPXX7VcSi92asbUU9obvpHgcYRAwaIKGQgrBCOJN1AKbk8SotI +56FqKjAvTsALqCAMlTIL5af+EO/9KPfjVk2nodKcviu4wEj6G0HjoEo8O1roCDHQBYi9fu5erNJO +JDry7SUByd2JSiSce+gQvjN4IXgOPTj0yEmk7NLnd/oDKvLTHcMuzloykxgekE1SZQr42WgBPgzQ +LlJT81rm6gxSTuYMUwrOK4bgy5CyuuLGaxfgO/0x1Ukk/RnkQslYhZnquDNXse9fAGcD5ue72SQD +I/BPXS6IZyMxQN6iAnhw6FNqkHYK8N0hImMHwEZunjSVG78uDpUIoOmI+W5HrQYDJMu61ByQZUg9 +vbw8ip/3RfT74euCYohMyA+GSkWb1BtxOmcAadaIEAZMVci0DSAKYigvwrkAo7oWuuDghCilcoTo +HkFCHkuZWAM8+P1Aar40oPYhgmG9WtPrak1NYBfb6a+aqxGquz4OzgQFDsEF+PmQsm/mLIL66XZV +VHQVIV+P4Zudq5QHDw49RdqJD03baQY/Ji9oukrq4pUuyxqHyIBXkRqoRVcjj0d+LoDcAF3RPftC +8r/YC8GXStM2UoFvIU1OH5eeipMPEdFdCddfDwsMoBGxEgP8BAVIQZMKjo3dGwpQykRStJQfNuKJ +0anCt1h+Ynb4ulQtMJJBoUjdt7PLkIEO5Wt5B38uBhwUpzgmprYjDI584QCdMnwTW6AqDnJ7QWZ9 +Yq3ffrk8TyjrBeDBmKcglPKkHIINBFgidfiW3B05pg0rmurJqFqMPx3cuTZ4Mg+jAOzciIKOTKbI +GdvvzeOXD1+9khVlkNaL1y+cu9reBYM6HKYBKLBT2UTv8rJeSvgb18YMCMAXgcXSns12ycaLfTdu +zn63vLw8/VT+aghWOsOypEn4RFOsAI5t7IQPEvSOnIxMVjlYcfr7kYepazxz8XJf350bA8BuXpj9 +EtoXp5FdOQfsk8HBwavj/2xvBxpcuq7PrC4o4OdWrl7rzXABwJ1gl7xoqG/3yrDG85f77ty8dm78 +pDwBw6wR4IM5LmwGO4CO2nLe9vO7V5aBoLK9HeUvqDUSfI6Atd4tORnbNoWr9Zxu3L3yrLHvNEeC +rRjDD8bZ7cjJgqnaKFKXTP//2vmbz+Xw1/42G/hzNu5SVxAPrEwKdp+ffYjhf3dDVgjI+eku0De6 +XjZG4+PvRz8jCzvKvx8i+MV/2ZyTJduCWsdlFPR8dnFlUtD2GsIvLv5FTtbsx4KKJJZrcq1a9/zT +thVJwSyAX/ynnCzaNkQAxgC1atW6db/+4n/tnb9rImkYx1/QCDM5xtPCH+eAupozu9lddbdIdosN +pMgeFxa2ya6ZTkGLkCqL1XGphOOapLCLywRtJIooYtYkjWCaFUwKi2AXELJdcv/CzTs/nBnd0VdI +dJT5FCHFIHy/8zzP+84777yPGsPgoM3of/uQ+oHLLjjwSWIA4wCG3XbUVw3uwuE/XA9qALA84w3o +HqIYMUED2LPkWncqGxRSsYcaACQbZzy8A93tBibRAYNRZR483AAgd0BWBZwmPgnY8wQNtuq5eqaI +7wB4NAe6OUCJIcA4QDDFIVNRiQcfwKPw1CMzgK0CggMYf8DkVq1UnlX9zJTw+a+ypmIRs+DAgvRl +VJI+2ZtJ/TAG5F3Vdpxm6MBCtHdxIlMvTcyEDfCIPA30HqpP9TRWkTwuNIvlWdPPVMLASE0Yj+p0 +Iz3KfPkgkUufHDeKF6c0uwZWr1cq2dNiWg3xz8fAkxEbUBpi4XbrunNZTikZkUgXzs5L9P1/7Ta3 +oBOO9P0M4l6dvT/Bo+P4bSQD/onF2LPWITftWr3azGaz32ia+fuiUv2ePMzz3xfMz4mE+7IK7QvG +/Y9gDFjcI+jfhh3nRQu+Kl7475yUnj1OoSbSSmRiHYwH+wgBoNPxFnAeUEpGyfTPzUuvC1XRXkjk +1sC48CFXQlynk1kQVbjub7kBc7viYEKjVcDUexcYHyRiKdyBbQelFnxRyoB5OXwOZJpnaGNI7ooE +Y8XhQTZAZoGSAXiPAbBYxJEfr3J0wArGjR0lDT6zrScFCxgPdhUuNPRGAPNghbrQUmjG7WACeFHS +gDAaZRZElGpAWMrNfQP1sXK/UdtcIcFEcCCMh1EcN0o8wBSHS1F9+xr9MaJMH21uuq1gUvh2hnYk +IXCJBcZtxQu/CurRZ72pC7gtmQqCCTK8FkZgAzbBgi8DrtPFblp36OurifMq+4222wEmy+/SIKCi +NpNev+CMyDoQ4DjnAb47wKjDqxFeMqQ6LS6YVvxg4oiVIOQ0wVUyuEJgo6SLBpwFRETxoPb6KMtI +5etbHDcYFphftluAGiC5xyPKZjabBAv0UrWhSDS6rTQJzr9ArvhMzb+8X4bqIZTnJVALviecfokD +yvdbfuvR9x3t8beeBcNee4GKsNp3fmH7L4sWmIaMEFtJ+hj9XULqjrn1hq56bNHvAurCGlzkHRAs +cA5Qn68U0bfawN0fUvGM/KAFqA9L1wITZ4FCCBxVL9BLXuLsCu7/+SSKh/KtQJ1Y5Rb8ZNaTqZYK +yKNdihEv7AHTY5jq5bNvkb0BMQ96ciBTQ0/6g/J5Nrklb2qMYQRBBPwWoHLIJd4Cs8SA+PfTwj6y +9qtapr+tNWSJBNOAI8iFAW9AvHaKeOcThYtsMq7Q15pYDDrAtOBiwyDKjnVnKNOcxEnnR/UwNGB1 +3U26wFRh9S4ls8PneAfpy86P22WY4TbldSWPzwqmj/WNxMAdrozy1jJGiPx8svzXdKrnMmH9Q67/ +lh8X6WYt79T30b9iHFp55neB6Wbt40aKe/3VKH2r1PJCntv4GbMEW6/4Kap6Q0xYe+e1e1ZkJ0tw +cyUZNlG72+61gpnDQvoYGyguBsQJYxfz5meKke4jLWCmsbz0Bl/Z3ywxrK4GAqur8L83Qb+XnHXl +GhoaGhoaGhoaauJ/z6sVgaPaB24AAAAASUVORK5CYII= +" + id="image234" + x="2.9323115" + y="161.16402" + style="stroke-width:2.33685" /><text + xml:space="preserve" + style="font-size:5.64444px;line-height:70%;font-family:AbcCursiveDottedLined;-inkscape-font-specification:AbcCursiveDottedLined;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-rule:evenodd;stroke-width:2.8448;stop-color:#000000" + x="38.344975" + y="188.41483" + id="url"><tspan + sodipodi:role="line" + id="tspan398" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#ffffff;stroke-width:2.8448" + x="38.344975" + y="188.41483">Initializing...</tspan></text><text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:9.87778px;line-height:70%;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-dasharray:none;stop-color:#000000" + x="2.535063" + y="10.061478" + id="news"><tspan + sodipodi:role="line" + id="tspan452" + style="font-size:9.87778px;fill:#ffffff;stroke:#000000;stroke-width:0.264583;stroke-dasharray:none" + x="2.535063" + y="10.061478">Initializing...</tspan></text><text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.05556px;line-height:70%;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.264583;stroke-dasharray:none;stop-color:#000000" + x="38.2995" + y="180.71815" + id="bottom"><tspan + sodipodi:role="line" + id="tspan452-6" + style="font-size:7.05556px;fill:#ffffff;stroke:none;stroke-width:0.264583;stroke-dasharray:none" + x="38.2995" + y="180.71815">Initializing...</tspan></text><rect + style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.264583;stroke-dasharray:none;stop-color:#000000" + id="rect1531" + width="309.6998" + height="174.09039" + x="33.69672" + y="-2.1396465" /></g></svg> diff --git a/roles/obs/tasks/main.yml b/roles/obs/tasks/main.yml index 36e14c3..bf856ec 100644 --- a/roles/obs/tasks/main.yml +++ b/roles/obs/tasks/main.yml @@ -1,5 +1,5 @@ - name: Load icecast vars - tags: wip + tags: obs-profile, wip include_vars: file: ../../stream/defaults/main.yml - name: Add repo @@ -53,5 +53,13 @@ regexp: '(AllowUsers(?!.*\b{{ emacsconf_id }}-{{ item.id }}\b).*)$' replace: \1 {{ emacsconf_id }}-{{ item.id }} loop: "{{ emacsconf_tracks }}" - - +- name: Allow sudo from {{ emacsconf_user }} to the stream users + tags: wip + become: true + become_user: root + copy: + content: | + {% for item in emacsconf_tracks %} + {{ emacsconf_user }} ALL=({{ emacsconf_id }}-{{ item.id }}) NOPASSWD: ALL + {% endfor %} + dest: /etc/sudoers.d/50_emacsconf diff --git a/roles/obs/tasks/obs-setup.yml b/roles/obs/tasks/obs-setup.yml index 1da678a..59170f1 100644 --- a/roles/obs/tasks/obs-setup.yml +++ b/roles/obs/tasks/obs-setup.yml @@ -6,13 +6,12 @@ state: directory - name: Create profile directories file: - path: "~{{ emacsconf_user }}/.config/obs-studio/basic/{{ item.name }}" + path: "~{{ emacsconf_user }}/.config/obs-studio/basic/profiles/{{ item.name }}" state: directory owner: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" mode: 0775 - name: Install OBS global profile - tags: wip template: src: global.ini owner: "{{ emacsconf_user }}" @@ -23,7 +22,7 @@ src: profile.ini owner: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" - dest: "~{{ emacsconf_user }}/.config/obs-studio/basic/{{ item.name }}/basic.ini" + dest: "~{{ emacsconf_user }}/.config/obs-studio/basic/profiles/{{ item.name }}/basic.ini" mode: 0664 - name: Install OBS scenes tags: obs-scene @@ -32,17 +31,33 @@ owner: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" dest: "~{{ emacsconf_user }}/.config/obs-studio/basic/scenes/{{ item.id }}.json" +- name: Copy overlay template for video + tags: obs-scene + copy: + src: overlay.svg + dest: "~{{ emacsconf_user }}/video.svg" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" +- name: Copy overlay template for video + tags: obs-scene + copy: + src: overlay.svg + dest: "~{{ emacsconf_user }}/other.svg" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" - name: Create text files for OBS sources copy: content: "Initializing..." owner: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" - dest: "~{{ emacsconf_user }}/{{ item }}.txt" + dest: "~{{ emacsconf_user }}/{{ filename }}.txt" mode: 0664 loop: - news - url - bottom + loop_control: + loop_var: filename - name: Create OBS scripts for tracks template: src: obs-track @@ -50,4 +65,11 @@ owner: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" mode: 0775 - +- name: Fix permissions + become: true + become_user: root + file: + path: "~{{ emacsconf_user }}" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" + recurse: true diff --git a/roles/obs/tasks/track.yml b/roles/obs/tasks/track.yml index 93b789d..a0acf16 100644 --- a/roles/obs/tasks/track.yml +++ b/roles/obs/tasks/track.yml @@ -1,5 +1,5 @@ - name: Set vars - tags: wip, obs-scene, mpv + tags: obs-scene, obs-profile, mpv, wip set_fact: old_emacsconf_user: "{{ emacsconf_user }}" old_emacsconf_home: "{{ emacsconf_home }}" @@ -10,7 +10,7 @@ - name: Set up user include: user.yml - name: Set up user-related things - become: "{{ emacsconf_user }}" + become_user: "{{ emacsconf_user }}" block: - name: Set up track bins for addition to paths file: @@ -27,6 +27,7 @@ group: "{{ emacsconf_group }}" state: directory - name: Add MPV profile + tags: mpv-conf template: src: mpv.conf dest: "~{{ emacsconf_user }}/.config/mpv/mpv.conf" @@ -40,8 +41,12 @@ group: "{{ emacsconf_group }}" mode: 0775 - name: Set up OBS profiles and scenes - tags: obs-profile - include: obs-setup.yml + tags: obs-profile, obs-scene + include_tasks: + file: obs-setup.yml + apply: + tags: + - obs-profile - name: Add FFMPEG script for streaming template: src: stream-desktop-with-ffmpeg.sh @@ -75,7 +80,7 @@ owner: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" - name: Reset vars - tags: wip, obs-scene, mpv + tags: obs-scene, obs-profile, mpv, wip set_fact: old_emacsconf_user: "{{ emacsconf_user }}" old_emacsconf_home: "{{ emacsconf_home }}" diff --git a/roles/obs/tasks/user.yml b/roles/obs/tasks/user.yml index a941d7f..caaf2d9 100644 --- a/roles/obs/tasks/user.yml +++ b/roles/obs/tasks/user.yml @@ -2,7 +2,6 @@ group: name: "{{ emacsconf_group }}" - name: Add user - tags: wip user: name: "{{ emacsconf_user }}" group: "{{ emacsconf_group }}" @@ -15,10 +14,10 @@ state: directory mode: 700 - name: Set up SSH key access + tags: wip template: src: authorized_keys dest: "/home/{{ emacsconf_user }}/.ssh/authorized_keys" - force: no - name: Change ownership of SSH directory file: path: "/home/{{ emacsconf_user }}/.ssh" diff --git a/roles/obs/templates/mpv.conf b/roles/obs/templates/mpv.conf index 9aff3ee..b1bd1ca 100644 --- a/roles/obs/templates/mpv.conf +++ b/roles/obs/templates/mpv.conf @@ -15,7 +15,7 @@ sub-use-margins=yes sub-scale-by-window=yes sub-pos=103 sub-margin-x=120 -sub-margin-y=40 +sub-margin-y=20 sub-align-x=left # Style sub-font="{{ emacsconf_font }}" diff --git a/roles/obs/templates/scenes.json b/roles/obs/templates/scenes.json index 4e6e4bd..a92f29e 100644 --- a/roles/obs/templates/scenes.json +++ b/roles/obs/templates/scenes.json @@ -1,6 +1,6 @@ { - "current_program_scene": "All track audio and screen", - "current_scene": "All track audio and screen", + "current_program_scene": "Video audio and screen", + "current_scene": "Video audio and screen", "current_transition": "Fade", "groups": [ { @@ -30,8 +30,8 @@ "push-to-talk-delay": 0, "settings": { "custom_size": true, - "cx": 103, - "cy": 507, + "cx": 494, + "cy": 715, "id_counter": 0, "items": [ { @@ -56,11 +56,11 @@ "locked": false, "name": "News", "pos": { - "x": 22, - "y": 390 + "x": 12, + "y": 0 }, "private_settings": {}, - "rot": -90, + "rot": 0, "scale": { "x": 1, "y": 1 @@ -93,11 +93,11 @@ "locked": false, "name": "URL", "pos": { - "x": 55, - "y": 389 + "x": 114, + "y": 691 }, "private_settings": {}, - "rot": -90, + "rot": 0, "scale": { "x": 1, "y": 1 @@ -131,7 +131,7 @@ "name": "Logo", "pos": { "x": 0, - "y": 404 + "y": 603 }, "private_settings": {}, "rot": 0, @@ -236,6 +236,8 @@ "push-to-talk": false, "push-to-talk-delay": 0, "settings": { + "color2": 4294967295, + "drop_shadow": false, "font": { "face": "Sans Serif", "flags": 0, @@ -243,6 +245,7 @@ "style": "" }, "from_file": true, + "outline": true, "text_file": "/home/{{ emacsconf_id }}-{{ item.id }}/news.txt" }, "sync": 0, @@ -271,7 +274,7 @@ "font": { "face": "Sans Serif", "flags": 0, - "size": 24, + "size": 20, "style": "" }, "from_file": true, @@ -346,14 +349,14 @@ "OBSBasic.SelectScene": [], "libobs.hide_scene_item.Bottom": [], "libobs.hide_scene_item.Screen Capture (XSHM)": [], - "libobs.hide_scene_item.dev-qa": [], - "libobs.hide_scene_item.dev-vid": [], "libobs.hide_scene_item.emacsconf base": [], + "libobs.hide_scene_item.gen-qa": [], + "libobs.hide_scene_item.gen-vid": [], "libobs.show_scene_item.Bottom": [], "libobs.show_scene_item.Screen Capture (XSHM)": [], - "libobs.show_scene_item.dev-qa": [], - "libobs.show_scene_item.dev-vid": [], - "libobs.show_scene_item.emacsconf base": [] + "libobs.show_scene_item.emacsconf base": [], + "libobs.show_scene_item.gen-qa": [], + "libobs.show_scene_item.gen-vid": [] }, "id": "scene", "mixers": 0, @@ -503,11 +506,11 @@ "locked": false, "name": "News", "pos": { - "x": 31, - "y": 595 + "x": 21, + "y": 6 }, "private_settings": {}, - "rot": -90, + "rot": 0, "scale": { "x": 1, "y": 1 @@ -540,11 +543,11 @@ "locked": false, "name": "URL", "pos": { - "x": 64, - "y": 594 + "x": 123, + "y": 697 }, "private_settings": {}, - "rot": -90, + "rot": 0, "scale": { "x": 1, "y": 1 @@ -615,7 +618,7 @@ "name": "{{ emacsconf_id }} base", "pos": { "x": 9, - "y": 205 + "y": 6 }, "private_settings": {}, "rot": 0, @@ -734,11 +737,11 @@ "hotkeys": { "OBSBasic.SelectScene": [], "libobs.hide_scene_item.Screen Capture (XSHM)": [], - "libobs.hide_scene_item.dev-vid": [], "libobs.hide_scene_item.emacsconf base": [], + "libobs.hide_scene_item.gen-vid": [], "libobs.show_scene_item.Screen Capture (XSHM)": [], - "libobs.show_scene_item.dev-vid": [], - "libobs.show_scene_item.emacsconf base": [] + "libobs.show_scene_item.emacsconf base": [], + "libobs.show_scene_item.gen-vid": [] }, "id": "scene", "mixers": 0, @@ -851,11 +854,11 @@ "locked": false, "name": "News", "pos": { - "x": 31, - "y": 580 + "x": 12, + "y": 0 }, "private_settings": {}, - "rot": -90, + "rot": 0, "scale": { "x": 1, "y": 1 @@ -888,11 +891,11 @@ "locked": false, "name": "URL", "pos": { - "x": 64, - "y": 579 + "x": 114, + "y": 691 }, "private_settings": {}, - "rot": -90, + "rot": 0, "scale": { "x": 1, "y": 1 @@ -925,8 +928,8 @@ "locked": false, "name": "Logo", "pos": { - "x": 9, - "y": 594 + "x": 0, + "y": 603 }, "private_settings": {}, "rot": 0, @@ -962,8 +965,8 @@ "locked": false, "name": "{{ emacsconf_id }} base", "pos": { - "x": 9, - "y": 190 + "x": 0, + "y": 0 }, "private_settings": {}, "rot": 0, diff --git a/roles/publish/defaults/main.yml b/roles/publish/defaults/main.yml index 4de7445..6416e25 100644 --- a/roles/publish/defaults/main.yml +++ b/roles/publish/defaults/main.yml @@ -1,9 +1,5 @@ emacsconf_org_file: "{{ emacsconf_private_dir }}/conf.org" emacsconf_publishing_phase: schedule -emacsconf_el_dir: ~{{ emacsconf_user }}/emacsconf-el -emacsconf_edit_wiki_dir: ~{{ emacsconf_user }}/emacsconf-wiki -emacsconf_private_dir: ~{{ emacsconf_user }}/emacsconf-2022-private -emacs_config_dir: ~{{ emacsconf_user }}/.emacs.d emacs_version: 28.2 emacs_build_parent: /usr/src/emacs emacs_build_dir: "{{ emacs_build_parent }}/emacs-{{ emacs_version }}" diff --git a/roles/publish/tasks/main.yml b/roles/publish/tasks/main.yml index 28566a5..ddee975 100644 --- a/roles/publish/tasks/main.yml +++ b/roles/publish/tasks/main.yml @@ -1,66 +1,106 @@ --- -- name: Set up SSH directory - ansible.builtin.file: - path: "/home/{{ emacsconf_user }}/.ssh" - state: directory - mode: '0700' +- name: Set up user + include_role: + name: user - name: Install SSH key for EmacsConf wiki ansible.builtin.get_url: url: https://emacsconf.org/id_rsa_anon_git_emacsconf dest: "/home/{{ emacsconf_user }}/.ssh/id_rsa_anon_git_emacsconf" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" mode: '0600' -- name: Install the SSH key for orga - template: - src: id_rsa - dest: "/home/{{ emacsconf_user }}/.ssh/id_rsa" +- name: Check SSH config for line + blockinfile: + path: /home/{{ emacsconf_user }}/.ssh/config mode: '0600' -- name: Change ownership of SSH directory - file: - path: "/home/{{ emacsconf_user }}/.ssh" - recurse: true owner: "{{ emacsconf_user }}" - + group: "{{ emacsconf_group }}" + create: yes + block: | + Host anon-emacsconf + Hostname git.emacsconf.org + Port 22 + User anon + IdentityFile ~/.ssh/id_rsa_anon_git_emacsconf +- name: Install the SSH key for orga + copy: + src: "{{ item }}" + dest: "/home/{{ emacsconf_user }}/.ssh" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" + mode: '0600' + loop: + - id_rsa + - id_rsa.pub +- name: Check if local Emacs already exists + stat: + path: "/usr/local/bin/emacs" + register: emacs - name: Set up Emacs + become: yes include: emacs.yml + when: not emacs.stat.exists +- name: Configure git + template: + src: git-config + dest: "~{{ emacsconf_user }}/.gitconfig" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" - name: Ensure configuration directory exists file: path: "{{ emacs_config_dir }}" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" state: directory - name: Set up Emacs configuration for non-interactive publishing template: src: emacsconf-config.el dest: "{{ emacs_config_dir }}/emacsconf-config.el" - + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" - name: Check if Emacs base configuration already exists - become: true lineinfile: dest: "{{ emacs_config_dir }}/init.el" state: present regexp: "emacsconf-config" - line: "(load-file \"emacsconf-config.el\")" + line: "(load-file \"{{ emacs_config_dir }}/emacsconf-config.el\")" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" create: yes +- name: Set up compile-media + become_user: "{{ emacsconf_user }}" + git: + repo: https://github.com/sachac/compile-media.git + dest: "~{{ emacsconf_user }}/compile-media" - name: Set up or update repositories - become: false + become_user: "{{ emacsconf_user }}" tags: publish block: - name: Check out wiki repository ansible.builtin.git: - repo: git://git.emacsconf.org/emacsconf-wiki + repo: git@anon-emacsconf:/emacsconf-wiki dest: "{{ emacsconf_edit_wiki_dir }}" + key_file: "~{{ emacsconf_user }}/.ssh/id_rsa_anon_git_emacsconf" register: wiki_clone failed_when: - wiki_clone.failed - not 'Local modifications exist in repository' in wiki_clone.msg - not 'Failed to checkout branch' in wiki_clone.msg + - name: Configure git to use + shell: git config core.sshCommand "ssh -i ~{{ emacsconf_user }}/.ssh/id_rsa_anon_git_emacsconf -F /dev/null" + args: + chdir: "{{ emacsconf_edit_wiki_dir }}" - name: Check out emacsconf-el ansible.builtin.git: repo: git@git.emacsconf.org:pub/emacsconf-el + key_file: "~{{ emacsconf_user }}/.ssh/id_rsa" dest: "{{ emacsconf_el_dir }}" register: elisp failed_when: - elisp.failed - not 'Local modifications exist in repository' in elisp.msg - not 'Failed to checkout branch' in elisp.msg + debugger: on_failed - name: Check out emacsconf-2022-private ansible.builtin.git: repo: git@git.emacsconf.org:emacsconf-2022-private @@ -75,20 +115,23 @@ path: "{{ item }}" owner: "{{ emacsconf_user }}" recurse: true - with_items: - - "{{ emacsconf_config_dir }}" + loop: + - "{{ emacs_config_dir }}" - "{{ emacsconf_el_dir }}" - "{{ emacsconf_private_dir }}" - "{{ emacsconf_edit_wiki_dir }}" - name: Publish - tags: publish + tags: publish, publish-only block: - name: Publish the schedule - command: emacs -l "{{ emacsconf_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-generate-main-schedule)' - when: (force_publish|d(false)) or ((private.changed or elisp.changed) and slug is not defined) + command: emacs -l "{{ emacs_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-publish-schedule)' + when: ((publish|d("")) == "schedule") or (((private is defined and private.changed) or (elisp is defined and elisp.changed)) and slug is not defined) - name: Update a specific talk's nav page tags: publish-talk - command: emacs -l "{{ emacsconf_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-with-talk-heading "{{ slug }}" (emacsconf-update-talk))' + command: emacs -l "{{ emacs_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-with-talk-heading "{{ slug }}" (emacsconf-update-talk))' when: slug is defined - name: Commit the wiki and push shell: cd {{ emacsconf_edit_wiki_dir }}; git commit -m 'Update from ansible' -a; git push + - name: Publish the backstage index + command: emacs -l "{{ emacs_config_dir }}/emacsconf-config.el" --batch --exec '(emacsconf-publish-backstage-index)' + when: (publish|d("")) == "backstage" diff --git a/roles/publish/templates/emacsconf-config.el b/roles/publish/templates/emacsconf-config.el index cfb554e..e9e4fcc 100644 --- a/roles/publish/templates/emacsconf-config.el +++ b/roles/publish/templates/emacsconf-config.el @@ -1,9 +1,6 @@ ;; {{ ansible_managed }} (add-to-list 'load-path "{{ emacsconf_el_dir }}") -(require 'emacsconf) -(require 'emacsconf-publish) -(require 'emacsconf-schedule) (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (shell . t))) (setq emacsconf-year "{{ emacsconf_year }}") (setq emacsconf-org-file "{{ emacsconf_org_file }}") @@ -19,3 +16,28 @@ {% endif %} (setq emacsconf-pad-api-key "{{ etherpad_api_key }}") (setq emacsconf-publishing-phase '{{ emacsconf_publishing_phase }}) +(setq emacsconf-backstage-password "{{ emacsconf_backstage_password }}") + +(setq emacsconf-backstage-dir "/ssh:orga@media.emacsconf.org:/var/www/media.emacsconf.org/2022/backstage") +(setq emacsconf-upload-dir "/ssh:orga@media.emacsconf.org:/srv/upload") +{% if ansible_host == "res.emacsconf.org" %} +(setq emacsconf-res-dir (format "/ssh:orga@res.emacsconf.org:/data/emacsconf/%s" emacsconf-year)) +(setq emacsconf-cache-dir "/data/{{ emacsconf_id }}/cache") +(setq emacsconf-stream-host "localhost") +{% else %} +(setq emacsconf-res-dir "/data/{{ emacsconf_id }}/{{ emacsconf_year}}") +{% endif %} +(add-to-list 'load-path "~/compile-media") +(require 'compile-media) +(require 'emacsconf) +(require 'emacsconf-publish) +(require 'emacsconf-schedule) +(require 'emacsconf-erc) +(require 'emacsconf-stream) + +(setq emacsconf-tracks + '((:name "General" :color "peachpuff" :id "gen" :channel "emacsconf-gen" + :tramp "/ssh:gen:") + (:name "Development" :color "skyblue" :id "dev" :channel "emacsconf-dev" + :tramp "/ssh:dev:"))) + diff --git a/roles/publish/templates/git-config b/roles/publish/templates/git-config new file mode 100644 index 0000000..e03358d --- /dev/null +++ b/roles/publish/templates/git-config @@ -0,0 +1,8 @@ +# {{ ansible_managed }} +# This is Git's per-user configuration file. +[user] +# Please adapt and uncomment the following lines: + name = {{ emacsconf_name }} + email = {{ emacsconf_email }} +[push] + default = simple diff --git a/roles/user/tasks/main.yml b/roles/user/tasks/main.yml new file mode 100644 index 0000000..11c0a74 --- /dev/null +++ b/roles/user/tasks/main.yml @@ -0,0 +1,31 @@ +- name: Add group + become: yes + group: + name: "{{ emacsconf_group }}" +- name: Add user + become: yes + user: + name: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" + shell: /bin/bash +# password: "{{ emacsconf_unix_password }}" +- name: Create SSH folder + file: + path: "/home/{{ emacsconf_user }}/.ssh" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" + state: directory + mode: 0700 +- name: Set up SSH key access + template: + src: authorized_keys + dest: "/home/{{ emacsconf_user }}/.ssh/authorized_keys" + owner: "{{ emacsconf_user }}" + group: "{{ emacsconf_group }}" + force: no +- name: Change ownership of SSH directory + file: + path: "/home/{{ emacsconf_user }}/.ssh" + recurse: true + owner: "{{ emacsconf_user }}" + mode: "u+rwX,g-rwx,o-rwx" |