Ver código fonte

[back] RYM parser and deluge utils.

Innocenty Enikeew 7 anos atrás
pai
commit
cc6b4feae1
1 arquivos alterados com 213 adições e 0 exclusões
  1. 213 0
      back/rym.lisp

+ 213 - 0
back/rym.lisp

@@ -0,0 +1,213 @@
+(in-package :cl-user)
+(defpackage chad-music.rym
+  (:use :cl #:alexandria #:chad-music.utils)
+  (:export #:custom-chart
+           #:load-all))
+(in-package :chad-music.rym)
+
+(defvar *cookies* nil "rateyourmusic.com valid cookies")
+(defparameter +rym-charts-url+ "https://rateyourmusic.com/customchart")
+(defparameter +ua+ "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0")
+
+(defun custom-chart (&key
+                       (chart-type :top)
+                       (countries)
+                       (genre-include t)
+                       (genres)
+                       (include :both)
+                       (include-child-genres t)
+                       (origin-countries nil)
+                       (page 1)
+                       (type :album)
+                       (year :alltime))
+  (let* ((parameters
+          `(("chart_type" . ,(dekeyify chart-type))
+            ("countries" . ,(or countries ""))
+            ("genre_include" . ,(if genre-include "1" "0"))
+            ("genres" . ,(format nil "~{~A~^,~}" genres))
+            ("include" . ,(dekeyify include))
+            ("include_child_genres" . ,(if include-child-genres "1" "0"))
+            ("include_child_genres_chk" . ,(if include-child-genres "1" ""))
+            ("origin_countries" . ,(format nil "~{~A~^,~}" origin-countries))
+            ("page" . ,(write-to-string page))
+            ("type" . ,(dekeyify type))
+            ("year" . ,(etypecase year
+                                  (integer (write-to-string year))
+                                  (symbol (dekeyify year))
+                                  (string year)))))
+         (dom (xml-request +rym-charts-url+
+                           :parameters parameters
+                           :cookie-jar *cookies*
+                           :user-agent +ua+)))
+    (loop for tr across (clss:select "#content table tr" dom)
+       when (select-text tr ".artist")
+       collect
+         (list :chart-year year
+               :chart-pos (parse-integer (select-text tr ".ooookiig"))
+               :artist (select-text tr ".artist")
+               :album (select-text tr ".album")
+               :year (let ((text (select-text tr ".chart_year")))
+                       (when text
+                         (parse-integer (subseq text 1 (1- (length text))) :junk-allowed t)))
+               :genres (map 'list #'select-text (clss:select ".genre" tr))
+               :stats  (map 'list #'select-text (clss:select ".chart_stats b" tr))))))
+
+(defvar *dull-genres* '("television music" "video game music" "film score" "film soundtrack"))
+
+(defun scrape-charts (filename &key (max-year 2017) (min-year 1950) (max-page 3) (min-page 1))
+  (with-output-to-file (s filename :if-exists :append)
+    (loop for page from min-page upto max-page
+       do (loop for year from max-year downto min-year
+             do (progn
+                  (loop for item in (custom-chart :genre-include nil
+                                                  :genres *dull-genres*
+                                                  :year year
+                                                  :page page)
+                     do (print item s))
+                  (print (cons year page))
+                  (sleep 5))))))
+
+(defun load-all (filename &optional if-does-not-exist)
+  (with-input-from-file (s filename :if-does-not-exist if-does-not-exist)
+    (loop for value = (read s nil)
+       while value
+       collect value)))
+
+(defun range (field min &optional (max 3000))
+  (lambda (c)
+    (let ((value (getf c field)))
+      (<= min value max))))
+
+(defun getter (field)
+  (lambda (c)
+    (getf c field)))
+
+(defstruct ordered artist albums genres score)
+(defun ordered-print (instance &optional stream)
+  (with-slots (artist albums genres score) instance
+    (format stream "Artist: ~A~%Genres: ~{~A~^, ~}~%Albums: ~{~A~^, ~}~%Score: ~A~%~%"
+            artist genres albums score)))
+(defun order-artists (charts)
+  (let ((ht (make-hash-table :test 'equal)))
+    (loop for c in charts
+       for artist = (getf c :artist)
+       do (multiple-value-bind (item foundp)
+              (gethash artist ht)
+            (unless foundp
+              (setf item (make-ordered :artist artist :score 0)
+                    (gethash artist ht) item))
+            (pushnew (getf c :album) (ordered-albums item) :test 'equal)
+            (dolist (genre (getf c :genres))
+              (pushnew genre (ordered-genres item) :test 'equal))
+            (incf (ordered-score item)
+                  (ceiling (- 121 (getf c :chart-pos)) 12))))
+    (sort (hash-table-values ht) #'> :key 'ordered-score)))
+
+(defun set-cookie (name value path domain)
+  (cl-cookie:merge-cookies
+   *cookies*
+   (list
+    (cl-cookie:make-cookie :name name
+                           :value value
+                           :path path
+                           :domain domain))))
+
+(defun infohash-magnet (info-hash)
+  (concatenate 'string "magnet:?xt=urn:btih:" info-hash))
+
+(defvar *deluge-api* "http://10.8.77.1:8112/json")
+(defvar *deluge-password* "deluge")
+(defvar *deluge-request-id* 1)
+(defun deluge-request (method &optional params)
+  (let* ((content (trivial-utf-8:string-to-utf-8-bytes
+                  (jojo:to-json `(:|id| ,(incf *deluge-request-id*)
+                                   :|method| ,method
+                                   :|params| ,params))))
+         (response (json-request *deluge-api* :method :post
+                                 :content content
+                                 :cookie-jar *cookies*))
+         (error (getf response :|error|)))
+    (when error (error (getf error :|message|)))
+    (getf response :|result|)))
+(defun deluge-auth ()
+  (deluge-request "auth.login" (list *deluge-password*)))
+(defun deluge-add-info-hash (info-hashes)
+  (unless (listp info-hashes)
+    (setf info-hashes (list info-hashes)))
+  (let ((torrents (loop for ih in info-hashes
+                     collect (list :|path| (infohash-magnet ih)
+                                   :|options| '(:|add_paused| :false)))))
+    (deluge-request "web.add_torrents" (list torrents))))
+
+(defun deluge-get-torrents ()
+  (deluge-request "core.get_session_state"))
+
+(defparameter +deluge-default-status-fields+ '("files" "file_priorities" "save_path"))
+(defun deluge-get-torrent-files (info-hash)
+  (let* ((status (deluge-request "core.get_torrent_status" (list info-hash +deluge-default-status-fields+)))
+         (save-path (getf status :|save_path|)))
+    (loop for file in (getf status :|files|)
+       for prio in (getf status :|file_priorities|)
+       do (setf (getf file :|prio|) prio
+                (getf file :|path|) (format nil "~A/~A" save-path (getf file :|path|)))
+       collect file)))
+
+(defun deluge-save-rm-list (filename)
+  (let ((torrents (deluge-get-torrents)))
+    (with-output-to-file (s filename)
+      (dolist (torrent torrents)
+        (let* ((status (deluge-request "core.get_torrent_status" (list torrent +deluge-default-status-fields+)))
+               (save-path (getf status :|save_path|)))
+          (loop
+             for file in (getf status :|files|)
+             for prio in (getf status :|file_priorities|)
+             when (zerop prio)
+             do (format s "~A/~A~%" save-path (getf file :|path|))))))))
+
+(defparameter +rutracker-search+ "https://rutracker.org/forum/tracker.php")
+(defun search-artist (artist &optional (suffix "дискография"))
+  (http-request +rutracker-search+ :parameters `(("nm" . ,(format nil "~A~@[ ~A~]" artist suffix)))))
+
+;;; rutracker sqlite dump
+(defvar *rt-db* nil)
+(defun rt-connect (path)
+  (setf *rt-db* (sqlite:connect path)))
+(defun rt-select (sql &rest parameters)
+  (apply #'sqlite:execute-to-list *rt-db* sql parameters))
+
+(defun rt-artist (artist)
+  (rt-select "select title, hash_info from torrent where title like ? and title like '%искография%' and not title like '%lossless%' order by size_b desc limit 30" (format nil "%~A%" artist)))
+
+(defun save-rt (filename artists)
+  (with-output-to-file (s filename :if-exists :append :if-does-not-exist :create)
+    (loop for artist in artists
+       do (print artist)
+       do (loop for result in (rt-artist artist)
+             do (print (cons artist result) s)))))
+
+(defun select-torrent (torrents)
+  (format t "   Select torrent:~%~{~A) ~A~%~}0) Skip~%s) Stop~%~%Enter: "
+          (loop for i from 1 for tor in torrents append (list i (car tor))))
+  (let ((answer (read)))
+    (typecase answer
+      (integer (if (zerop answer) nil
+                   (or (nth (1- answer) torrents)
+                       (select-torrent torrents))))
+      (symbol :stop))))
+
+(defun rt-find-artists (charts-filename found-filename)
+  (let ((found (load-all found-filename :create))
+        (ordered (order-artists (load-all charts-filename))))
+    (with-output-to-file (s found-filename :if-exists :append :if-does-not-exist :create)
+      (dolist (item ordered)
+        (let ((artist (ordered-artist item)))
+          (unless (member artist found :test 'equal :key 'car)
+            (ordered-print item t)
+            (let ((torrents (rt-artist artist)))
+              (if torrents
+                  (let ((torrent (select-torrent torrents)))
+                    (when (eq torrent :stop)
+                      (return))
+                    (when torrent (deluge-add-info-hash (cdr torrent)))
+                    (print (cons artist torrent) s))
+                  (print (cons artist nil) s)))))))))