1
0
v.shebanov 4 rokov pred
rodič
commit
8338871f5b

+ 1 - 1
back/db.lisp

@@ -8,7 +8,7 @@
            #:make-album #:album-id #:album-artist #:album-year #:album-album #:album-original-date #:album-publisher #:album-country
            #:album-genre #:album-type #:album-status #:album-mb-id #:album-track-count #:album-total-duration #:album-cover
            #:track #:no #:title #:part-of-set #:bit-rate #:is-vbr #:duration #:path
-           #:make-track #:track-album #:track-artist #:track-no #:track-title #:track-part-of-set
+           #:make-track #:track-id #:track-album #:track-artist #:track-no #:track-title #:track-part-of-set
            #:track-bit-rate #:track-is-vbr #:track-duration #:track-path
            #:entry #:track #:added #:modified #:present
            #:make-entry #:entry-track #:entry-added #:entry-modified #:entry-present

+ 28 - 7
back/server.lisp

@@ -64,6 +64,7 @@
   (let ((album-artist (album-artist (track-album track)))
         (track-artist (track-artist track)))
     (with-object
+      (maybe-key-value "id" (track-id track))
       (maybe-key-value "artist" track-artist)
       (maybe-key-value "album_artist" (unless (equal track-artist album-artist) album-artist))
       (maybe-key-value "album" (album-album (track-album track)))
@@ -71,7 +72,6 @@
       (maybe-key-value "no" (clear-track-no (track-no track)))
       (maybe-key-value "title" (track-title track))
       (maybe-key-value "bit_rate" (track-bit-rate track))
-      (maybe-key-value "vbr" (if (track-is-vbr track) :true :false))
       (maybe-key-value "duration" (track-duration track))
       (maybe-key-value "url" (get-url (track-path track)))
       (maybe-key-value "cover" (get-url (album-cover (track-album track)))))))
@@ -261,13 +261,15 @@
                 :content (trivial-utf-8:string-to-utf-8-bytes (to-json params))))
 
 (defvar *bot-auth-chat-id* nil "Authentication chat id")
+(defun tg-get-chat-member (user-id &optional (chat-id *bot-auth-chat-id*))
+  (getf (telegram-request "getChatMember"
+                          `(:|chat_id| ,chat-id :|user_id| ,user-id))
+        :|result|))
+
 (defparameter +authorized-statuses+ '("creator" "administrator" "member"))
 (defun authorize-user (info)
   (ignore-errors
-   (let* ((response (telegram-request "getChatMember"
-                                      `(:|chat_id| ,*bot-auth-chat-id*
-                                         :|user_id| ,(getf info :|id|))))
-          (chat-member (getf response :|result|))
+   (let* ((chat-member (tg-get-chat-member (getf info :|id|)))
           (status (getf chat-member :|status|)))
      (member status +authorized-statuses+ :test #'equal))))
 
@@ -294,11 +296,29 @@
             +401+))
     (error (e) (log:error e) +400+)))
 
-(defun user-info (params)
+(defun self-info (params)
   (declare #.*standard-optimize-settings* (ignorable params))
   (with-user (info)
     (200-json info)))
 
+(defun find-user-info (username)
+  (first
+   (sort
+    (loop for info being the hash-value of (server-token-user *server*)
+          when (equal username (getf info :|username|))
+          collect info)
+    #'< :key (lambda (i) (getf i :|auth_date|)))))
+
+(defun user-info (params)
+  (declare #.*standard-optimize-settings* (ignorable params))
+  (with-user (info)
+    (let ((info (find-user-info (getf params :user))))
+      (if info
+          (progn
+            (dolist (ind '(:|hash| :|id| :|auth_date|)) (remf info ind))
+            (200-json info))
+          +404+))))
+
 (defvar *mapper* (myway:make-mapper))
 (myway:connect *mapper* "/api/cat/:category/size" 'get-category-size)
 (myway:connect *mapper* "/api/cat/:category" 'get-category)
@@ -307,7 +327,8 @@
 (myway:connect *mapper* "/api/stats" 'stats)
 (myway:connect *mapper* "/api/rescan" 'request-rescan :method :POST)
 (myway:connect *mapper* "/api/login" 'login :method :POST)
-(myway:connect *mapper* "/api/user" 'user-info)
+(myway:connect *mapper* "/api/user" 'self-info)
+(myway:connect *mapper* "/api/user/:user" 'user-info)
 
 (defun main (&rest args &key (port 5000) (debug nil) (use-thread t) (serve-files nil) &allow-other-keys)
   ;; Load config file

+ 16 - 0
chad-magnets/background.js

@@ -0,0 +1,16 @@
+browser.browserAction.onClicked.addListener(()=>browser.runtime.openOptionsPage());
+
+async function handleMessage(request) {
+  if (request.magnet) {
+    const opts = await browser.storage.local.get();
+    if (!opts.enabled) return new Promise((r,e) => e(new Error("Extension is not enabled")));
+    return fetch(opts.url, {
+      method: 'POST',
+      body: JSON.stringify({url: request.magnet})
+    }).then(res => res.text()).then(text => {
+      if (text === 'OK') return text;
+      return new Promise((r,e) => e(new Error(text)));
+    });
+  }
+}
+browser.runtime.onMessage.addListener(handleMessage);

+ 56 - 0
chad-magnets/chadify.js

@@ -0,0 +1,56 @@
+function handleChadClick(e, magnet) {
+  e.preventDefault(true);
+  const a = e.target;
+  if (a.processing) return;
+
+  a.processing = true;
+  a.style.borderBottom = 'dotted yellow';
+  a.title = 'Processing';
+
+  browser.runtime.sendMessage({magnet}).then((response) => {
+    a.style.borderBottom = 'dotted green';
+    a.title = 'Added';
+  }, (error) => {
+    console.log(`Error: ${error}`);
+    a.style.borderBottom = 'dotted red';
+    a.title = `${error}`;
+    a.processing = false;
+  });
+}
+
+async function processMagnets(el) {
+  const opts = await browser.storage.local.get("enabled");
+  if (!opts.enabled) return;
+
+  const magnets = el.querySelectorAll("a[href^='magnet:?']")
+  for (let magnet of magnets) {
+    if (magnet.getAttribute('data-chadded')) continue;
+
+    const img = document.createElement('img');
+    img.src = browser.runtime.getURL("logo256.png");
+    img.style = `width: auto; height: 1.2em; vertical-align: text-bottom; padding-right: 0.3em;`;
+    const a = document.createElement('a');
+    a.style = 'text-decoration: none;';
+    a.appendChild(img);
+    a.insertAdjacentText('beforeend', 'Add to Chad Music');
+    a.addEventListener("click", (e) => handleChadClick(e, magnet.href));
+
+    magnet.insertAdjacentElement('afterend', a);
+    magnet.setAttribute('data-chadded', 1);
+  }
+}
+
+// Process page
+processMagnets(document.body);
+
+// Now monitor the DOM for additions and substitute emoji into new nodes.
+// @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver.
+const observer = new MutationObserver((mutations) => {
+  mutations.forEach((mutation) => {
+    if (mutation.addedNodes) mutation.addedNodes.forEach(processMagnets);
+  });
+});
+observer.observe(document.body, {
+  childList: true,
+  subtree: true
+});

BIN
chad-magnets/logo256.png


+ 47 - 0
chad-magnets/manifest.json

@@ -0,0 +1,47 @@
+{
+  "manifest_version": 2,
+  "name": "Chad-music magnets",
+  "version": "1.0",
+
+  "description": "Adds button to torrent magnet links to upload to chad-music",
+
+  "icons": {
+    "256": "logo256.png"
+  },
+
+  "browser_action": {
+    "default_icon": "logo256.png",
+    "default_title": "Chad Music"
+  },
+
+  "options_ui": {
+    "page": "options.html",
+    "browser_style": true,
+    "chrome_style": true
+  },
+
+  "background": {
+    "scripts": ["background.js"]
+  },
+
+  "content_scripts": [
+    {
+      "matches": ["<all_urls>"],
+      "js": ["chadify.js"]
+    }
+  ],
+
+  "web_accessible_resources": [
+    "logo256.png"
+  ],
+
+  "permissions": ["storage",
+                  "https://chatikbot.chad-partners.com/*"],
+
+  "browser_specific_settings": {
+    "gecko": {
+      "id": "music@chad-partners.com",
+      "strict_min_version": "42.0"
+    }
+  }
+}

+ 21 - 0
chad-magnets/options.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+  </head>
+
+  <body>
+    <form>
+      <div class="browser-style">
+        <label for="enabled">Enabled:</label>
+        <input type="checkbox" id="enabled" />
+      </div>
+      <div class="browser-style">
+        <label for="url">Submit url:</label>
+        <input type="text" placeholder="https://chatikbot.chad-partners.com/..." id="url" size="70" />
+      </div>
+      <button class="browser-style" type="submit">Confirm</button>
+    </form>
+    <script src="options.js"></script>
+  </body>
+</html>

+ 23 - 0
chad-magnets/options.js

@@ -0,0 +1,23 @@
+function saveOptions(e) {
+  e.preventDefault();
+  browser.storage.local.set({
+    url: document.querySelector("#url").value,
+    enabled: document.querySelector("#enabled").checked
+  });
+}
+
+function restoreOptions() {
+  function setCurrentChoice(result) {
+    document.querySelector("#enabled").checked = result.enabled;
+    document.querySelector("#url").value = result.url || 'https://';
+  }
+  function onError(error) {
+    console.log(`Error: ${error}`);
+  }
+
+  let getting = browser.storage.local.get();
+  getting.then(setCurrentChoice, onError);
+}
+
+document.addEventListener("DOMContentLoaded", restoreOptions);
+document.querySelector("form").addEventListener("submit", saveOptions);

+ 1 - 1
web/public/index_old.html

@@ -9,7 +9,7 @@
       name="description"
       content="Chad music streaming service"
     />
-    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo256.png" />
     <!--
       manifest.json provides metadata used when your web app is installed on a
       user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

BIN
web/public/logo192.png


BIN
web/public/logo512.png


+ 7 - 0
web/src/components/Album/Album.css

@@ -23,4 +23,11 @@
   padding: 0;
   width: 24px;
   height: 24px;
+  border: 0;
+  border-radius: 4px;
+  cursor: pointer;
+  background: var(--button-background);
+}
+.Album-actions button:hover {
+  background: var(--controls-button-hover-background);
 }

+ 3 - 3
web/src/components/Album/Album.js

@@ -1,5 +1,5 @@
 import './Album.css';
-import logo from 'src/logo.svg';
+import logo from 'src/record.svg';
 import { Link } from 'react-router5'
 import {formatDuration, slugify} from 'src/utils';
 import {getTracks, request} from 'src/Api';
@@ -29,10 +29,10 @@ export default function Album({album, showArtist, showType, dispatch}) {
         <span className="Album-genre">{album.genre}</span>
         <div className="Album-actions">
           <button onClick={(e)=>fetchTracks(album.id, 'ALBUM_PLAY', dispatch)}>
-            <svg viewBox="0 0 24 24"><g><path d="M8 5v14l11-7z"/></g></svg>
+            <svg><use href="#control-play" /></svg>
           </button>
           <button onClick={(e)=>fetchTracks(album.id, 'ALBUM_ENQUEUE', dispatch)}>
-            <svg viewBox="0 0 24 24"><g><path d="M10 6h4v4h4v4h-4v4h-4v-4h-4v-4h4z"/></g></svg>
+            <svg><use href="#control-add" /></svg>
           </button>
         </div>
       </div>

+ 7 - 0
web/src/components/AlbumPage/AlbumPage.css

@@ -3,6 +3,13 @@
   width: 24px;
   height: 24px;
   vertical-align: bottom;
+  border: 0;
+  border-radius: 4px;
+  cursor: pointer;
+  background: var(--button-background);
+}
+.AlbumPage table button:hover {
+  background: var(--controls-button-hover-background);
 }
 
 @media screen and (max-width: 800px) {

+ 10 - 5
web/src/components/AlbumPage/AlbumPage.js

@@ -43,10 +43,10 @@ export default function AlbumPage({dispatch, scrollRef}) {
       </h1>
       <h2>{albumName}</h2>{tracks && (<div className="Album-actions">
           <button onClick={(e)=>dispatch({type:'ALBUM_PLAY', payload: {tracks, pos: 0}})}>
-            <svg viewBox="0 0 24 24"><g><path d="M8 5v14l11-7z"/></g></svg>
+            <svg><use href="#control-play" /></svg>
           </button>
           <button onClick={(e)=>dispatch({type: 'ALBUM_ENQUEUE', payload: {tracks}})}>
-            <svg viewBox="0 0 24 24"><g><path d="M10 6h4v4h4v4h-4v4h-4v-4h-4v-4h4z"/></g></svg>
+            <svg><use href="#control-add" /></svg>
           </button>
       </div>)}
       {album && (<>
@@ -68,9 +68,14 @@ export default function AlbumPage({dispatch, scrollRef}) {
               <td>{track.title}</td>
               <td>{track.bit_rate} kbps</td>
               <td>{formatDuration(track.duration)}</td>
-              <td><button onClick={() => dispatch({type:'ALBUM_PLAY', payload: {tracks, pos: i}})}>
-                  <svg viewBox="0 0 24 24"><g><path d="M8 5v14l11-7z"/></g></svg>
-              </button></td>
+              <td>
+                <button onClick={() => dispatch({type:'ALBUM_PLAY', payload: {tracks, pos: i}})}>
+                  <svg><use href="#control-play" /></svg>
+                </button>
+                <button onClick={(e)=>dispatch({type: 'QUEUE_ADD', payload: track})}>
+                  <svg><use href="#control-add" /></svg>
+                </button>
+              </td>
             </tr>))
           }
       </tbody></table>

+ 6 - 1
web/src/components/App/App.css

@@ -1,3 +1,8 @@
+button svg {
+  height: 100%;
+  width: 100%;
+}
+
 .App {
   --accent: #388E3C;
   --header-background: #000;
@@ -54,7 +59,7 @@
 }
 
 @media screen and (max-width: 800px) {
-  .App {
+  .App[data-layout] {
     grid: "header" auto
           "panels" auto
           "main"   1fr

+ 3 - 1
web/src/components/App/App.js

@@ -4,6 +4,7 @@ import {storeToken, loadToken} from 'src/Api';
 import Login from '../Login';
 import Header from '../Header';
 import Player from '../Player';
+import SvgSymbols from '../SvgSymbols';
 
 function storeSettings(settings) {
   localStorage.setItem('settings', JSON.stringify(settings));
@@ -39,13 +40,14 @@ function App() {
 
   return (
     <div className="App" data-theme={settings.theme} data-layout={settings.layout} data-panel={settings.panel || 'main'}>
+      <SvgSymbols />
+      <Header settings={settings} dispatch={dispatch} />
       <div className="App-panels">
         <input id="panel-main" type="radio" name="panel" value="main" checked={settings.panel === 'main'} onChange={handleRadio(dispatch)} />
         <input id="panel-side" type="radio" name="panel" value="side" checked={settings.panel === 'side'} onChange={handleRadio(dispatch)} />
         <label htmlFor="panel-main">Browse</label>
         <label htmlFor="panel-side">Queue</label>
       </div>
-      <Header settings={settings} dispatch={dispatch} />
       <Player />
     </div>
   );

+ 14 - 6
web/src/components/Controls/Controls.css

@@ -126,30 +126,38 @@
 }
 
 @media screen and (max-width: 800px) {
-  .Controls {
+  .App[data-layout] .Controls {
     grid: "progress progress progress" auto
           "cover    buttons time" auto
           "cover    title artist" auto
           "album    album album" auto
           / auto auto 1fr;
     grid-column-gap: 4px;
+    width: initial;
   }
-  .Controls-buttons {
+  .App[data-layout] .Controls-buttons {
     grid-column-gap: 10px;
   }
-  .Controls progress {
+  .App[data-layout] .Controls progress {
     height: 4px;
   }
-  .Controls-album {
+  .App[data-layout] .Controls-album {
     padding: 0 8px;
   }
-  .Controls-artist {
+  .App[data-layout] .Controls-artist {
     min-width: 25vw;
     padding: 0 8px;
     text-align: right;
   }
-  .Controls-time {
+  .App[data-layout] .Controls-time {
     justify-self: right;
     padding-right: 8px;
   }
+  .App[data-layout] .Controls img {
+    width: 64px;
+    height: 64px;
+  }
+  .App[data-layout] .Controls > div {
+    padding: 0;
+  }
 }

+ 6 - 6
web/src/components/Controls/Controls.js

@@ -1,5 +1,5 @@
 import './Controls.css';
-import logo from 'src/logo.svg';
+import logo from 'src/record.svg';
 import Sound from 'react-sound';
 import {slugify,formatDuration} from 'src/utils';
 
@@ -22,21 +22,21 @@ export default function Controls({queue, error, sound, dispatch}) {
       <div className="Controls-title">{track.no && `${track.no}. `}{track.title}</div>
       <div className="Controls-buttons">
         <button disabled={queue.pos < 1} onClick={()=>dispatch({type: 'CONTROL_PREV'})}>
-          <svg viewBox="0 0 24 24"><g><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"></path></g></svg>
+          <svg><use href="#control-prev" /></svg>
         </button>
         <button disabled={queue.pos < 0}
                 onClick={()=>dispatch({type: (playing ? 'CONTROL_PAUSE' : 'CONTROL_PLAY')})}>
           {playing
-            ? <svg viewBox="0 0 24 24"><g><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></g></svg>
-            : <svg viewBox="0 0 24 24"><g><path d="M8 5v14l11-7z"/></g></svg>
+            ? <svg><use href="#control-pause" /></svg>
+            : <svg><use href="#control-play" /></svg>
           }
         </button>
         <button disabled={sound.playStatus === Sound.status.STOPPED}
                 onClick={()=>dispatch({type: 'CONTROL_STOP'})}>
-          <svg viewBox="0 0 24 24"><g><path d="M6 6v12h12v-12z"/></g></svg>
+          <svg><use href="#control-stop" /></svg>
         </button>
         <button disabled={(queue.pos+1) >= queue.items.length} onClick={()=>dispatch({type: 'CONTROL_NEXT'})}>
-          <svg viewBox="0 0 24 24"><g><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"></path></g></svg>
+          <svg><use href="#control-next" /></svg>
         </button>
       </div>
     </footer>

+ 13 - 0
web/src/components/Player.js

@@ -67,6 +67,19 @@ function playerReducer(state, action) {
   case 'QUEUE_LOAD': {
     const queue = action.payload;
     return {...state, queue, ...playTrack(queue.items[queue.pos], Sound.status.PAUSED)}}
+  case 'QUEUE_DELETE': {
+    const idx = action.payload;
+    const pos = (idx < state.queue.pos ? state.queue.pos-1 : (idx > state.queue.pos ? state.queue.pos : -1));
+    const items = [...state.queue.items];
+    items.splice(idx, 1);
+    const queue = {items, pos};
+    storeQueue(queue);
+    return {...state, queue}}
+  case 'QUEUE_ADD': {
+    const items = state.queue.items.concat(action.payload);
+    const queue = {...state.queue, items};
+    storeQueue(queue);
+    return {...state, queue}}
   case 'SOUND_ERROR':
     return {...state, sound: NO_SOUND, metadata: NO_METADATA, error: action.payload}
   case 'SOUND_PLAYING':

+ 12 - 0
web/src/components/Queue/Queue.css

@@ -8,3 +8,15 @@
 .Queue-active {
   background: var(--accent);
 }
+.Queue button {
+  padding: 0;
+  width: 24px;
+  height: 24px;
+  border: 0;
+  border-radius: 4px;
+  cursor: pointer;
+  background: var(--button-background);
+}
+.Queue button:hover {
+  background: var(--controls-button-hover-background);
+}

+ 8 - 3
web/src/components/Queue/Queue.js

@@ -17,9 +17,14 @@ export default function Queue({queue, dispatch}) {
               <td>{track.no}</td>
               <td>{track.bit_rate} kbps</td>
               <td>{formatDuration(track.duration)}</td>
-              <td><button onClick={() => dispatch({type:'QUEUE_PLAY', payload: i})}>
-                  <svg viewBox="0 0 24 24"><g><path d="M8 5v14l11-7z"/></g></svg>
-              </button></td>
+              <td>
+                <button onClick={() => dispatch({type:'QUEUE_PLAY', payload: i})}>
+                  <svg><use href="#control-play" /></svg>
+                </button>
+                <button onClick={() => dispatch({type:'QUEUE_DELETE', payload: i})}>
+                  <svg><use href="#control-del" /></svg>
+                </button>
+              </td>
             </tr>))}
       </tbody></table>
     </aside>

+ 27 - 0
web/src/components/SvgSymbols.js

@@ -0,0 +1,27 @@
+export default () => (
+  <>
+    <svg style={{display: "none"}}>
+      <symbol id="control-prev" viewBox="0 0 24 24">
+        <path d="M6 6h2v12H6zm3.5 6l8.5 6V6z" />
+      </symbol>
+      <symbol id="control-pause" viewBox="0 0 24 24">
+        <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
+      </symbol>
+      <symbol id="control-play" viewBox="0 0 24 24">
+        <path d="M8 5v14l11-7z" />
+      </symbol>
+      <symbol id="control-stop" viewBox="0 0 24 24">
+        <path d="M6 6v12h12v-12z" />
+      </symbol>
+      <symbol id="control-next" viewBox="0 0 24 24">
+        <path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
+      </symbol>
+      <symbol id="control-add" viewBox="0 0 24 24">
+        <path d="M10 6h4v4h4v4h-4v4h-4v-4h-4v-4h4z" />
+      </symbol>
+      <symbol id="control-del" viewBox="0 0 24 24">
+        <path d="M6 10h12v4h-12z" />
+      </symbol>
+    </svg>
+  </>
+)

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 2
web/src/logo.svg


+ 145 - 0
web/src/record.svg

@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   width="400"
+   height="400"
+   id="svg2"
+   version="1.0">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3273">
+      <stop
+         style="stop-color:#6c6c6c"
+         offset="0"
+         id="stop3275" />
+      <stop
+         style="stop-color:#3e3e3e"
+         offset="1"
+         id="stop3277" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3263">
+      <stop
+         style="stop-color:#f7f7f7"
+         offset="0"
+         id="stop3265" />
+      <stop
+         style="stop-color:#e1e1e1"
+         offset="1"
+         id="stop3267" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3153">
+      <stop
+         style="stop-color:#ffffff"
+         offset="0"
+         id="stop3155" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1"
+         id="stop3157" />
+    </linearGradient>
+    <filter
+       id="filter3315">
+      <feGaussianBlur
+         stdDeviation="3.7"
+         id="feGaussianBlur3317" />
+    </filter>
+    <radialGradient
+       xlink:href="#linearGradient3153"
+       id="radialGradient3328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.8695893,0.8695893,-1.9614862,-1.9614862,681.5104,178.1413)"
+       cx="289.42923"
+       cy="209.66924"
+       fx="289.42923"
+       fy="209.66924"
+       r="92.5" />
+    <radialGradient
+       xlink:href="#linearGradient3263"
+       id="radialGradient3349"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,2.8728591,-1,0,392.84375,-372.50696)"
+       cx="199.28125"
+       cy="192.84375"
+       fx="199.28125"
+       fy="192.84375"
+       r="53.6875" />
+    <radialGradient
+       xlink:href="#linearGradient3273"
+       id="radialGradient3352"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,2.7782148,-0.82387859,0,358.87983,-353.64609)"
+       cx="199.28125"
+       cy="192.84375"
+       fx="199.28125"
+       fy="192.84375"
+       r="68.84375" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <use
+     x="0"
+     y="0"
+     xlink:href="#path3191"
+     id="use31"
+     transform="translate(2.71429,4)"
+     width="100%"
+     height="100%"
+     style="fill-opacity:0.5;filter:url(#filter3315)" />
+  <path
+     id="path3191"
+     d="M 200,15 C 97.88,15 15,97.88 15,200 15,302.12 97.88,385 200,385 302.12,385 385,302.12 385,200 385,97.88 302.12,15 200,15 Z m 0,179.8125 c 2.86645,0 5.18749,2.32105 5.1875,5.1875 0,2.86645 -2.32105,5.18749 -5.1875,5.1875 -2.86645,0 -5.18749,-2.32105 -5.1875,-5.1875 0,-2.86645 2.32105,-5.18749 5.1875,-5.1875 z" />
+  <path
+     id="path3271"
+     d="m 200,132.65625 c -37.15925,0 -67.34375,30.1845 -67.34375,67.34375 0,37.15925 30.1845,67.34375 67.34375,67.34375 37.15925,0 67.34375,-30.1845 67.34375,-67.34375 0,-37.15925 -30.1845,-67.34375 -67.34375,-67.34375 z m 0,62.90625 c 2.45408,0 4.4375,1.98342 4.4375,4.4375 0,2.45408 -1.98342,4.4375 -4.4375,4.4375 -2.45408,0 -4.4375,-1.98342 -4.4375,-4.4375 0,-2.45408 1.98342,-4.4375 4.4375,-4.4375 z"
+     style="fill:#333333" />
+  <path
+     id="path3176"
+     d="M 200,16.78125 C 301.15637,16.78125 383.21875,98.843633 383.21875,200 383.21877,301.15637 301.15637,383.21875 200,383.21875 98.843633,383.21875 16.78125,301.15637 16.78125,200 16.78125,98.843633 98.843633,16.78125 200,16.78125 Z"
+     style="fill:none;stroke:#000000;stroke-width:3.562" />
+  <use
+     height="100%"
+     width="100%"
+     transform="rotate(180,200,200)"
+     id="use3151"
+     xlink:href="#path3193"
+     y="0"
+     x="0" />
+  <path
+     id="path3193"
+     d="m 106.4375,200 c 0,-51.65143 41.91107,-93.59375 93.5625,-93.59375 V 18.5625 C 99.85143,18.5625 18.56249,99.85142 18.5625,200 Z"
+     style="fill:url(#radialGradient3328)" />
+  <path
+     id="path3215"
+     d="M 200,132.6875 C 237.16701,132.6875 267.3125,162.83298 267.3125,200 267.3125,237.16701 237.16701,267.31445 200,267.31445 162.83299,267.31445 132.68555,237.16701 132.68555,200 132.68555,162.83298 162.83299,132.6875 200,132.6875 Z"
+     style="fill:none;stroke:#000000;stroke-width:3" />
+  <path
+     id="path3223"
+     d="m 237.9628,162.03721 c -20.95262,-20.9526 -54.97299,-20.95261 -75.9256,0 -20.9526,20.95262 -20.9526,54.97299 0,75.92559 20.95262,20.9526 54.97299,20.95261 75.9256,0 20.9526,-20.95261 20.9526,-54.97298 0,-75.92559 z m -34.29468,34.29468 c 2.02688,2.02689 2.02688,5.30934 0,7.33623 -2.02689,2.02689 -5.30934,2.02688 -7.33624,0 -2.02688,-2.02688 -2.02688,-5.30934 0,-7.33623 2.02689,-2.02688 5.30934,-2.02688 7.33624,0 z"
+     style="fill:url(#radialGradient3349)" />
+  <path
+     id="path3230"
+     d="m 243.95111,156.0489 c -7.06177,-7.06176 -15.40617,-12.05335 -24.2847,-15.00392 l -1.23744,3.71231 c 8.31532,2.76444 16.12455,7.46249 22.7379,14.07584 8.05626,8.05626 13.25594,17.88433 15.60055,28.24008 l 3.82279,-0.86178 c -2.50205,-11.05558 -8.03761,-21.56103 -16.6391,-30.16253 z"
+     style="opacity:0.7425743;fill:#ffffff" />
+  <path
+     id="path3232"
+     d="m 142.61386,190.25519 -3.88908,-0.66291 c -3.23715,19.17313 2.53847,39.5732 17.32411,54.35884 14.74595,14.74593 35.07597,20.5264 54.20416,17.34621 l -0.64082,-3.86699 c -17.92511,2.98388 -36.96183,-2.44619 -50.7791,-16.26346 -13.85228,-13.85228 -19.25338,-32.94694 -16.21927,-50.91169 z"
+     style="opacity:0.7425743;fill:#ffffff" />
+</svg>

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov