wuclan 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/LICENSE.textile +20 -0
  2. data/README.textile +28 -0
  3. data/examples/analyze/strong_links/gen_multi_edge.rb +103 -0
  4. data/examples/analyze/strong_links/main.rb +51 -0
  5. data/examples/analyze/word_count/dump_schema.rb +13 -0
  6. data/examples/analyze/word_count/freq_user.rb +31 -0
  7. data/examples/analyze/word_count/freq_whole_corpus.rb +27 -0
  8. data/examples/analyze/word_count/word_count.pig +43 -0
  9. data/examples/analyze/word_count/word_count.rb +34 -0
  10. data/examples/lastfm/scrape/load_lastfm.rb +31 -0
  11. data/examples/lastfm/scrape/scrape_lastfm.rb +47 -0
  12. data/examples/lastfm/scrape/seed.tsv +147 -0
  13. data/examples/twitter/old/load_twitter_search_jobs.rb +157 -0
  14. data/examples/twitter/old/scrape_twitter_api.rb +104 -0
  15. data/examples/twitter/old/scrape_twitter_search.rb +57 -0
  16. data/examples/twitter/old/scrape_twitter_trending.rb +73 -0
  17. data/examples/twitter/parse/parse_twitter_requests.rb +81 -0
  18. data/examples/twitter/parse/parse_twitter_search_requests.rb +28 -0
  19. data/examples/twitter/scrape_twitter_api/scrape_twitter_api.rb +61 -0
  20. data/examples/twitter/scrape_twitter_api/seed.tsv +4 -0
  21. data/examples/twitter/scrape_twitter_api/start_cache_twitter.sh +2 -0
  22. data/examples/twitter/scrape_twitter_api/support/make_request_stats.rb +291 -0
  23. data/examples/twitter/scrape_twitter_api/support/make_requests_by_id_and_date_1.rb +98 -0
  24. data/examples/twitter/scrape_twitter_api/support/make_requests_by_id_and_date_2.pig +4 -0
  25. data/examples/twitter/scrape_twitter_api/support/twitter_search_jobs.tsv +6 -0
  26. data/examples/twitter/scrape_twitter_api/support/twitter_trending_seed.tsv +725 -0
  27. data/examples/twitter/scrape_twitter_hosebird/edamame-killall +4 -0
  28. data/examples/twitter/scrape_twitter_hosebird/foo.rb +19 -0
  29. data/examples/twitter/scrape_twitter_hosebird/ps_emulation.rb +111 -0
  30. data/examples/twitter/scrape_twitter_hosebird/scrape_twitter_hosebird.rb +110 -0
  31. data/examples/twitter/scrape_twitter_hosebird/test_spewer.rb +20 -0
  32. data/examples/twitter/scrape_twitter_hosebird/twitter_hosebird_god.yaml +10 -0
  33. data/examples/twitter/scrape_twitter_search/dump_twitter_search_jobs.rb +38 -0
  34. data/examples/twitter/scrape_twitter_search/load_twitter_search_jobs.rb +63 -0
  35. data/examples/twitter/scrape_twitter_search/scrape_twitter_search.rb +44 -0
  36. data/examples/twitter/scrape_twitter_search/twitter_search_daemons.god +25 -0
  37. data/lib/old/twitter_api.rb +88 -0
  38. data/lib/wuclan/delicious/delicious_html_request.rb +31 -0
  39. data/lib/wuclan/delicious/delicious_models.rb +26 -0
  40. data/lib/wuclan/delicious/delicious_request.rb +65 -0
  41. data/lib/wuclan/friendfeed/scrape/friendfeed_search_request.rb +60 -0
  42. data/lib/wuclan/friendster.rb +7 -0
  43. data/lib/wuclan/lastfm/model/base.rb +49 -0
  44. data/lib/wuclan/lastfm/model/sample_responses.txt +16 -0
  45. data/lib/wuclan/lastfm/scrape/base.rb +195 -0
  46. data/lib/wuclan/lastfm/scrape/concrete.rb +143 -0
  47. data/lib/wuclan/lastfm/scrape/lastfm_job.rb +12 -0
  48. data/lib/wuclan/lastfm/scrape/lastfm_request_stream.rb +17 -0
  49. data/lib/wuclan/lastfm/scrape/recursive_requests.rb +154 -0
  50. data/lib/wuclan/lastfm/scrape.rb +12 -0
  51. data/lib/wuclan/lastfm.rb +7 -0
  52. data/lib/wuclan/metrics/user_graph_metrics.rb +99 -0
  53. data/lib/wuclan/metrics/user_metrics.rb +443 -0
  54. data/lib/wuclan/metrics/user_metrics_basic.rb +277 -0
  55. data/lib/wuclan/metrics/user_scraping_metrics.rb +64 -0
  56. data/lib/wuclan/metrics.rb +0 -0
  57. data/lib/wuclan/myspace.rb +21 -0
  58. data/lib/wuclan/open_social/model/base.rb +0 -0
  59. data/lib/wuclan/open_social/scrape/base.rb +111 -0
  60. data/lib/wuclan/open_social/scrape_request.rb +6 -0
  61. data/lib/wuclan/open_social.rb +0 -0
  62. data/lib/wuclan/rdf_output/relationship_rdf.rb +47 -0
  63. data/lib/wuclan/rdf_output/text_element_rdf.rb +64 -0
  64. data/lib/wuclan/rdf_output/tweet_rdf.rb +10 -0
  65. data/lib/wuclan/rdf_output/twitter_rdf.rb +84 -0
  66. data/lib/wuclan/rdf_output/twitter_user_rdf.rb +12 -0
  67. data/lib/wuclan/shorturl/shorturl_request.rb +271 -0
  68. data/lib/wuclan/twitter/api_response_examples.textile +300 -0
  69. data/lib/wuclan/twitter/model/base.rb +72 -0
  70. data/lib/wuclan/twitter/model/multi_edge.rb +31 -0
  71. data/lib/wuclan/twitter/model/relationship.rb +176 -0
  72. data/lib/wuclan/twitter/model/text_element/extract_info_tests.rb +83 -0
  73. data/lib/wuclan/twitter/model/text_element/grok_tweets.rb +96 -0
  74. data/lib/wuclan/twitter/model/text_element/more_regexes.rb +370 -0
  75. data/lib/wuclan/twitter/model/text_element.rb +38 -0
  76. data/lib/wuclan/twitter/model/tweet/tokenize.rb +38 -0
  77. data/lib/wuclan/twitter/model/tweet/tweet_regexes.rb +202 -0
  78. data/lib/wuclan/twitter/model/tweet/tweet_token.rb +79 -0
  79. data/lib/wuclan/twitter/model/tweet.rb +74 -0
  80. data/lib/wuclan/twitter/model/twitter_user/style/color_to_hsv.rb +57 -0
  81. data/lib/wuclan/twitter/model/twitter_user.rb +145 -0
  82. data/lib/wuclan/twitter/model.rb +21 -0
  83. data/lib/wuclan/twitter/parse/ff_ids_parser.rb +27 -0
  84. data/lib/wuclan/twitter/parse/friends_followers_parser.rb +52 -0
  85. data/lib/wuclan/twitter/parse/generic_json_parser.rb +26 -0
  86. data/lib/wuclan/twitter/parse/json_tweet.rb +63 -0
  87. data/lib/wuclan/twitter/parse/json_twitter_user.rb +122 -0
  88. data/lib/wuclan/twitter/parse/public_timeline_parser.rb +54 -0
  89. data/lib/wuclan/twitter/parse/twitter_search_parse.rb +60 -0
  90. data/lib/wuclan/twitter/parse/user_parser.rb +30 -0
  91. data/lib/wuclan/twitter/scrape/base.rb +97 -0
  92. data/lib/wuclan/twitter/scrape/old_skool_request_classes.rb +40 -0
  93. data/lib/wuclan/twitter/scrape/twitter_fake_fetcher.rb +31 -0
  94. data/lib/wuclan/twitter/scrape/twitter_ff_ids_request.rb +75 -0
  95. data/lib/wuclan/twitter/scrape/twitter_followers_request.rb +135 -0
  96. data/lib/wuclan/twitter/scrape/twitter_json_response.rb +124 -0
  97. data/lib/wuclan/twitter/scrape/twitter_request_stream.rb +44 -0
  98. data/lib/wuclan/twitter/scrape/twitter_search_fake_fetcher.rb +44 -0
  99. data/lib/wuclan/twitter/scrape/twitter_search_flat_stream.rb +30 -0
  100. data/lib/wuclan/twitter/scrape/twitter_search_job.rb +25 -0
  101. data/lib/wuclan/twitter/scrape/twitter_search_request.rb +70 -0
  102. data/lib/wuclan/twitter/scrape/twitter_search_request_stream.rb +19 -0
  103. data/lib/wuclan/twitter/scrape/twitter_timeline_request.rb +72 -0
  104. data/lib/wuclan/twitter/scrape/twitter_user_request.rb +64 -0
  105. data/lib/wuclan/twitter/scrape.rb +27 -0
  106. data/lib/wuclan/twitter.rb +7 -0
  107. data/lib/wuclan.rb +1 -0
  108. data/spec/spec_helper.rb +9 -0
  109. data/spec/wuclan_spec.rb +7 -0
  110. data/wuclan.gemspec +184 -0
  111. metadata +219 -0
@@ -0,0 +1,60 @@
1
+ # module Wuclan
2
+ # module Twitter
3
+ # module Scrape
4
+ # #
5
+ # # ScrapeRequest for the twitter Search API.
6
+ # #
7
+ # # Examines the parsed contents to describe result
8
+ # #
9
+ # class TwitterSearchRequest < Monkeyshines::ScrapeRequest
10
+ # include Monkeyshines::RawJsonContents
11
+ # # Extract the actual search items returned
12
+ # def items
13
+ # parsed_contents['results'] if parsed_contents
14
+ # end
15
+ # # Checks that the response parses and has the right data structure.
16
+ # # if healthy? is true things should generally work
17
+ # def healthy?
18
+ # items && items.is_a?(Array)
19
+ # end
20
+ # # Number of items returned in this request
21
+ # def num_items()
22
+ # items ? items.length : 0
23
+ # end
24
+ # # Span of IDs. Assumes the response has the ids in sort order oldest to newest
25
+ # # (which the twitter API provides)
26
+ # def span
27
+ # [items.last['id'], items.first['id']] rescue nil
28
+ # end
29
+ # # Span of created_at times covered by this request.
30
+ # # Useful for rate estimation.
31
+ # def timespan
32
+ # [Time.parse(items.last['created_at']), Time.parse(items.first['created_at'])] rescue nil
33
+ # end
34
+ # end
35
+ # end
36
+ # end
37
+ # end
38
+ # end
39
+
40
+
41
+
42
+ # class FriendFeedSearchFetcher
43
+ # def search_url query, page, max_id
44
+ # start = (page-1)*100
45
+ # %Q{http://friendfeed.com/api/feed/search?q=#{query}&service=twitter&start=#{start}&num=100}
46
+ # end
47
+ # def max_results_per_page
48
+ # 100
49
+ # end
50
+ # def max_page
51
+ # 4
52
+ # end
53
+ # def terminate? response, page
54
+ # terminate = super || page >= max_page
55
+ # end
56
+ # def get_results response
57
+ # response['entries'] if response
58
+ # end
59
+ # end
60
+
@@ -0,0 +1,7 @@
1
+ require 'monkeyshines/scrape_request/raw_json_contents'
2
+ module Wuclan
3
+ module Friendster
4
+ autoload :Scrape, 'wuclan/friendster/scrape.rb'
5
+ autoload :Parse, 'wuclan/friendster/parse.rb'
6
+ end
7
+ end
@@ -0,0 +1,49 @@
1
+ require 'monkeyshines/scrape_request/raw_json_contents'
2
+ module Wuclan
3
+ module Lastfm
4
+
5
+ class LastfmUser
6
+ end
7
+
8
+ class LastfmShout
9
+ end
10
+
11
+ class LastfmArtist
12
+ end
13
+
14
+ class LastfmAlbum
15
+ end
16
+
17
+ class LastfmTrack
18
+ end
19
+
20
+ class LastfmGroup
21
+ end
22
+
23
+ class LastfmTag
24
+ end
25
+
26
+ class LastfmEvent
27
+ end
28
+
29
+ class LastfmVenue
30
+ end
31
+
32
+ # Interactions
33
+
34
+ class AShoutsB
35
+ end
36
+
37
+ # Relationships
38
+
39
+ class LastfmTagging < Struct.new(:user_id, :taggable_type, :taggable_id)
40
+ end
41
+
42
+ class LastfmListen
43
+ end
44
+
45
+ class LastfmAttending
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,16 @@
1
+ // lastfm_artist_info_request Linkin%20Park 1 http://ws.audioscrobbler.com/2.0/?method=Artist.getInfo&artist=Linkin%20Park&limit=100&page=1&api_key=12717a7e1a0ec8a053026b813fd59a7c&format=json 20090817020535 200 OK
2
+ { "artist": {
3
+ "name": "Linkin Park",
4
+ "mbid": "f59c5520-5f46-4d2c-b2c4-822eabf53419",
5
+ "stats": {"listeners":"1473188","playcount":"93021323"},
6
+ "url": "http:\/\/www.last.fm\/music\/Linkin+Park",
7
+ "tags": {"tag":[{"name":"rock","url":"http:\/\/www.last.fm\/tag\/rock"},{"name":"nu metal","url":"http:\/\/www.last.fm\/tag\/nu%20metal"},{"name":"alternative","url":"http:\/\/www.last.fm\/tag\/alternative"},{"name":"alternative rock","url":"http:\/\/www.last.fm\/tag\/alternative%20rock"},{"name":"metal","url":"http:\/\/www.last.fm\/tag\/metal"}]},
8
+ "bio": {"published":"Thu, 2 Jul 2009 21:19:19 +0000","summary":"Linkin Park is a band from Agoura Hills, California. The band consists of Joe &quot;<a href=\"http:\/\/ws.audioscrobbler.com\/music\/Mr.+Hahn\" class=\"bbcode_artist\">Mr. Hahn<\/a>&quot; Hahn, <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Mike+Shinoda\" class=\"bbcode_artist\">Mike Shinoda<\/a>, Dave Farrell (a.k.a. Phoenix), <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Chester+Bennington\" class=\"bbcode_artist\">Chester Bennington<\/a>, <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Brad+Delson\" class=\"bbcode_artist\">Brad Delson<\/a> and <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Rob+Bourdon\" class=\"bbcode_artist\">Rob Bourdon<\/a>. Released under the band name &quot;Hybrid Theory&quot;, the band's first album, <a title=\"Linkin Park - Hybrid Theory EP\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Hybrid+Theory+EP\" class=\"bbcode_album\">Hybrid Theory EP<\/a> was released in 1995. Only 1000 copies of this very rare original EP were made. The band's first full studio album, <a title=\"Linkin Park - Hybrid Theory\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Hybrid+Theory\" class=\"bbcode_album\">Hybrid Theory<\/a>, released in 2001, had a huge success, selling 42 million copies worldwide as of October 2009.","content":"<strong>Linkin Park<\/strong> is a band from Agoura Hills, California. The band consists of Joe &quot;<a href=\"http:\/\/ws.audioscrobbler.com\/music\/Mr.+Hahn\" class=\"bbcode_artist\">Mr. Hahn<\/a>&quot; Hahn, <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Mike+Shinoda\" class=\"bbcode_artist\">Mike Shinoda<\/a>, Dave Farrell (a.k.a. Phoenix), <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Chester+Bennington\" class=\"bbcode_artist\">Chester Bennington<\/a>, <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Brad+Delson\" class=\"bbcode_artist\">Brad Delson<\/a> and <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Rob+Bourdon\" class=\"bbcode_artist\">Rob Bourdon<\/a>.\n \n Released under the band name &quot;Hybrid Theory&quot;, the band's first album, <a title=\"Linkin Park - Hybrid Theory EP\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Hybrid+Theory+EP\" class=\"bbcode_album\">Hybrid Theory EP<\/a> was released in 1995. Only 1000 copies of this very rare original EP were made.\n \n The band's first full studio album, <a title=\"Linkin Park - Hybrid Theory\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Hybrid+Theory\" class=\"bbcode_album\">Hybrid Theory<\/a>, released in 2001, had a huge success, selling 42 million copies worldwide as of October 2009. Among its singles were <a title=\"Linkin Park &ndash; One Step Closer\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/One+Step+Closer\" class=\"bbcode_track\">One Step Closer<\/a>, <a title=\"Linkin Park &ndash; Crawling\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/Crawling\" class=\"bbcode_track\">Crawling<\/a>, and the band's highest-selling single, <a title=\"Linkin Park &ndash; In the End\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/In+the+End\" class=\"bbcode_track\">In the End<\/a>. In 2001, the band released &quot;Frat party at the pancake festival&quot;. A dvd with live stuff, extra footage of the band and the music video's from Hybrid Theory. A remix album, <a title=\"Linkin Park - Reanimation\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Reanimation\" class=\"bbcode_album\">Reanimation<\/a> was released in 2002, and was based around Hybrid Theory songs.\n \n The band's second studio album, <a title=\"Linkin Park - Meteora\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Meteora\" class=\"bbcode_album\">Meteora<\/a> (released 2003), was not so successful as &quot;Hybrid Theory&quot;. Still, it sold 10 million copies worldwide, spawned the worldwide hit <a title=\"Linkin Park &ndash; Breaking the Habit\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/Breaking+the+Habit\" class=\"bbcode_track\">Breaking the Habit<\/a>, and was the first Linkin Park album that was not fully rap driven.\n \n A limited edition live album and DVD came next: <a title=\"Linkin Park - Live in Texas\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Live+in+Texas\" class=\"bbcode_album\">Live in Texas<\/a>. It was an opportunity for fans who have not been to the band's gigs to see their performance live. \n \n Linkin Park and <a href=\"http:\/\/ws.audioscrobbler.com\/music\/Jay-Z\" class=\"bbcode_artist\">Jay-Z<\/a> then released <a title=\"Jay-Z and Linkin Park - Collision Course\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Jay-Z+and+Linkin+Park\/Collision+Course\" class=\"bbcode_album\">Collision Course<\/a> in which previously-released songs from the two artists were mixed together. Some time after this was a major label attempt to combat the success of illegal <a href=\"http:\/\/ws.audioscrobbler.com\/tag\/mashup\" class=\"bbcode_tag\" rel=\"tag\">mashup<\/a> albums popular in 2004 and 2005, most of which also used Jay-Z's <a title=\"Jay-Z - Black Album\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Jay-Z\/Black+Album\" class=\"bbcode_album\">Black Album<\/a> a cappellas. The album introduced fans of both artists to each other and marked the beginning of Jay-Z's return from retirement.\n \n The band's newest album is <a title=\"Linkin Park - Minutes To Midnight\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/Minutes+To+Midnight\" class=\"bbcode_album\">Minutes To Midnight<\/a>, often abbreviated MTM. The album was co-produced by Mike Shinoda and legendary Grammy wining producer Rick Rubin (Slayer, Red Hot Chili Peppers, Johnny Cash) The release date, originally somewhere between July and September of 2006, was pushed to May of 2007. The band cites their reasoning as having fun experimenting with new styles and sounds in the studio, saying it was their most fun recording session to date. The new album was finally released on iTunes on May 11th 2007 and in stores worldwide three days later. The sounds are new, more hip-hop based beats and rock song dynamics. It is 12 tracks long, including the band's 6th consecutive #1 hit, <a title=\"Linkin Park &ndash; What I've Done\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/What+I%27ve+Done\" class=\"bbcode_track\">What I've Done<\/a>. The video for the song was directed by Joseph Hahn.\n \n Four more singles from Minutes to Midnight were released: \n <a title=\"Linkin Park &ndash; Bleed It Out\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/Bleed+It+Out\" class=\"bbcode_track\">Bleed It Out<\/a> (released August 2007), <a title=\"Linkin Park &ndash; Shadow of the Day\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/Shadow+of+the+Day\" class=\"bbcode_track\">Shadow of the Day<\/a> (released October 2007), <a title=\"Linkin Park &ndash; Given Up\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/Given+Up\" class=\"bbcode_track\">Given Up<\/a> (released March 2008), and the final single, <a title=\"Linkin Park &ndash; Leave Out All the Rest\" href=\"http:\/\/ws.audioscrobbler.com\/music\/Linkin+Park\/_\/Leave+Out+All+the+Rest\" class=\"bbcode_track\">Leave Out All the Rest<\/a> (released June 2008).\n \n The band began touring again in late April in 2007 with a show in Berlin, and then continuing through Europe. 2007 also marks the return of the Projekt Revolution tour, which is always headlined by Linkin Park.\n \n On 29 June 2008, the band played their biggest Projekt Revolution show to date at the Milton Keynes Bowl, the venue Green Day's 2005 live DVD was recorded at (a 60,000+ capacity venue). The PR UK lineup included Jay-Z who again performed some &quot;Collision Course&quot; tracks with Linkin Park. Other bands included Pendulum, N*E*R*D, Enter Shikari, The Bravery and Innerpartysystem.\n \n The band's second live CD\/DVD, Road To Revolution: Live At Milton Keynes, was released November 24th 2008. It included at least one song of each Linkin Park album including Collision Course with Jay-Z and featured excerpts from Fort Minor's The Rising Tied in Points Of Authority. "}}}
9
+ "image": [{"#text":"http:\/\/userserve-ak.last.fm\/serve\/34\/21871115.jpg","size":"small"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/64\/21871115.jpg","size":"medium"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/126\/21871115.jpg","size":"large"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/252\/21871115.jpg","size":"extralarge"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/_\/21871115\/Linkin+Park.jpg","size":"mega"}],"streamable":"1",
10
+ "similar": {
11
+ "artist":[
12
+ {"name":"Fort Minor","url":"http:\/\/www.last.fm\/music\/Fort+Minor","image":[{"#text":"http:\/\/userserve-ak.last.fm\/serve\/34\/108914.jpg","size":"small"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/64\/108914.jpg>","size":"medium"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/126\/108914.jpg","size":"large"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/252\/108914.jpg","size":"extralarge"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/500\/108914\/Fort+Minor.jpg","size":"mega"}]},
13
+ {"name":"Jay-Z and Linkin Park","url":"http:\/\/www.last.fm\/music\/Jay-Z+and+Linkin+Park","image":[{"#text":"http:\/\/userserve-ak.last.fm\/serve\/34\/16170933.jpg","size":"small"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/64\/16170933.jpg>","size":"medium"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/126\/16170933.jpg","size":"large"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/252\/16170933.jpg","size":"extralarge"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/500\/16170933\/JayZ+and+Linkin+Park+LP++JAYZ.jpg","size":"mega"}]},
14
+ {"name":"Chester Bennington","url":"http:\/\/www.last.fm\/music\/Chester+Bennington","image":[{"#text":"http:\/\/userserve-ak.last.fm\/serve\/34\/33909.jpg","size":"small"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/64\/33909.jpg>","size":"medium"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/126\/33909.jpg","size":"large"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/252\/33909.jpg","size":"extralarge"},{"#text":"","size":"mega"}]},
15
+ {"name":"Xero","url":"http:\/\/www.last.fm\/music\/Xero","image":[{"#text":"http:\/\/userserve-ak.last.fm\/serve\/34\/8582695.jpg","size":"small"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/64\/8582695.jpg>","size":"medium"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/126\/8582695.jpg","size":"large"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/252\/8582695.jpg","size":"extralarge"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/500\/8582695\/Xero.jpg","size":"mega"}]},
16
+ {"name":"Papa Roach","url":"http:\/\/www.last.fm\/music\/Papa+Roach", "image":[{"#text":"http:\/\/userserve-ak.last.fm\/serve\/34\/23013493.jpg","size":"small"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/64\/23013493.jpg>","size":"medium"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/126\/23013493.jpg","size":"large"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/252\/23013493.jpg","size":"extralarge"},{"#text":"http:\/\/userserve-ak.last.fm\/serve\/500\/23013493\/Papa+Roach+lifeline_presspic.jpg","size":"mega"}]}]},
@@ -0,0 +1,195 @@
1
+ require 'monkeyshines/scrape_request/raw_json_contents'
2
+ module Wuclan
3
+ module Lastfm
4
+ module Scrape
5
+
6
+ #
7
+ # Base class for Lastfm API requests
8
+ #
9
+ #
10
+ # For parsing, note the different form for empty responses.
11
+ #
12
+ class Base < TypedStruct.new(
13
+ [:identifier, Integer],
14
+ [:page, Integer],
15
+ [:moreinfo, String],
16
+ [:url, String],
17
+ [:scraped_at, Bignum],
18
+ [:response_code, Integer],
19
+ [:response_message, String],
20
+ [:contents, String]
21
+ )
22
+ # Basic ScrapeRequest functionality
23
+ include Monkeyshines::ScrapeRequestCore
24
+ # Contents are JSON
25
+ include Monkeyshines::RawJsonContents
26
+ # API Authentication
27
+ cattr_accessor :api_key
28
+ self.api_key = Monkeyshines::CONFIG[:lastfm_api][:api_key] rescue nil
29
+
30
+ #
31
+ # call with fields in order:
32
+ #
33
+ # identifier page moreinfo url scraped_at response_code response_message contents
34
+ #
35
+ # you only have to fill in what you know -- in fact, it's typical to
36
+ # pass in only the identifier
37
+ def initialize *args
38
+ super *args
39
+ self.page = (page.to_i < 1 ? 1 : page.to_i)
40
+ end
41
+
42
+ # ===========================================================================
43
+ #
44
+ # API
45
+ #
46
+
47
+ # Slug for the URL to let lastfm know what resource is requested
48
+ class_inheritable_accessor :api_method
49
+ # Responses are a JSON hash with one
50
+ class_inheritable_accessor :main_fieldname
51
+ class_inheritable_accessor :response_type
52
+
53
+ #
54
+ # Generate request URL from other attributes
55
+ #
56
+ def make_url
57
+ [ "http://ws.audioscrobbler.com/2.0/?method=#{api_method}#{identifier}",
58
+ "&limit=#{max_items}&api_key=#{self.class.api_key}&format=json",
59
+ "&page=#{page}" ].join("")
60
+ end
61
+
62
+ #
63
+ # Call with a hash of identifier attr-val pairs (including an optional page)
64
+ #
65
+ # Ex.
66
+ # LastfmAlbumInfoRequest.from_identifier_hash(
67
+ # :album => 'Rum+Sodomy+%26+the+Lash',
68
+ # :artist => 'The+Pogues',
69
+ # :mbid => 'ba1f6641-9085-36a0-8962-65ad6e48afd1')
70
+ # Calls
71
+ # LastfmAlbumInfoRequest.new(
72
+ # 'album=Rum+Sodomy+%26+the+Lash&artist=The+Pogues&mbid=ba1f6641-9085-36a0-8962-65ad6e48afd1')
73
+ #
74
+ # FIXME -- this is stupid in taking a special 'id' and a just-as-necessary hash
75
+ #
76
+ def self.from_identifier_hash name, hsh={}
77
+ name = url_encode(name)
78
+ page = hsh.delete(:page)
79
+ rest = self.class.make_url_query(hsh)
80
+ new("#{name}&#{rest}", page)
81
+ end
82
+
83
+ # ===========================================================================
84
+ #
85
+ # Parsing
86
+ #
87
+
88
+ # Healthy if it's well-formed and not an error
89
+ def healthy?
90
+ super && ( (contents !~ %r{^\{"error":}) )
91
+ end
92
+
93
+ #
94
+ # Last.fm responses typically have the form
95
+ #
96
+ # { "requested_objs": {
97
+ # "requested_type": [ { ... obj ... }, { ... obj ... } ... ],
98
+ # "@attr":{ "key": "val" }
99
+ # }
100
+ # }
101
+ #
102
+ # e.g.
103
+ #
104
+ # {"similarartists":{
105
+ # "artist": [
106
+ # ...
107
+ # {"name":"Pillar","mbid":"..","match":"5.66",..."streamable":"1"}],
108
+ # "@attr":{"artist":"Dead by Sunrise"}
109
+ # }}
110
+ #
111
+ def main_result
112
+ return @main_result if @main_result
113
+ return unless healthy?
114
+ @main_result = parsed_contents[main_fieldname] rescue {}
115
+ end
116
+
117
+ #
118
+ # Information about the request itself.
119
+ #
120
+ # (see doc for result_attrs)
121
+ def result_attrs
122
+ main_result["@attr"] rescue {}
123
+ end
124
+
125
+ # Extract the actual search items returned
126
+ def items
127
+ # [obj_or_array].flatten makes this always be an array (lastfm gives
128
+ # the single object when it's not an array)
129
+ [main_result[response_type]].flatten.compact rescue []
130
+ end
131
+
132
+ #
133
+ # Pagination and Rescheduling
134
+ #
135
+ # Pagination
136
+ include Monkeyshines::ScrapeRequestCore::Paginated
137
+ include Monkeyshines::ScrapeRequestCore::Paginating
138
+ include Monkeyshines::ScrapeRequestCore::PaginatedWithLimit
139
+ # include Monkeyshines::ScrapeRequestCore::PaginatedWithRate
140
+
141
+ # API max items per response
142
+ self.max_items = 50
143
+ # API max pages
144
+ self.hard_request_limit = 15
145
+ # Items to get each re-visit. If there are up to 50 items per page,
146
+ # target_items_per_job of 1000 will try to reschedule so that its return visit
147
+ # makes about twenty page requests.
148
+ # #self.target_items_per_job = 150
149
+
150
+ # creates the paginated request
151
+ def request_for_page page, pageinfo=nil
152
+ (page.to_i > 1) ? self.class.new(identifier, page) : self
153
+ end
154
+
155
+ #
156
+ # Max pages for this resource
157
+ #
158
+ def max_pages
159
+ return 1 if (result_attrs.blank? || result_attrs['totalPages'].blank?)
160
+ attrs['totalPages'].to_i.clamp(1,self.hard_request_limit)
161
+ end
162
+
163
+ #
164
+ # For stream-type objects
165
+ #
166
+
167
+ # Span of IDs. Assumes the response has the ids in sort order oldest to newest
168
+ # (which the twitter API provides)
169
+ def span
170
+ [items.last['id'], items.first['id']] rescue nil
171
+ end
172
+ # Span of created_at times covered by this request.
173
+ # Useful for rate estimation.
174
+ def timespan
175
+ [Time.parse(items.last['created_at']).utc, Time.parse(items.first['created_at']).utc] rescue nil
176
+ end
177
+
178
+ class_inheritable_accessor :requestables
179
+ self.requestables = []
180
+ def recursive_requests &block
181
+ return unless healthy?
182
+ #
183
+ # requestables
184
+ #
185
+ requestables.each do |klass|
186
+ req = klass.new(identifier)
187
+ req.generation = generation.to_i + 1
188
+ yield req
189
+ end
190
+ end
191
+ end
192
+
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,143 @@
1
+ module Wuclan
2
+ module Lastfm
3
+ module Scrape
4
+
5
+ #
6
+ # Defines the concrete terms of the API
7
+ #
8
+
9
+ class LastfmAlbumInfoRequest < Base ; self.api_method = 'album.getinfo&album=' ; end
10
+ class LastfmAlbumTagsRequest < Base ; self.api_method = 'album.gettags&album=' ; end
11
+ class LastfmArtistEventsRequest < Base ; self.api_method = 'artist.getevents&artist=' ; end
12
+ class LastfmArtistImagesRequest < Base ; self.api_method = 'artist.getimages&artist=' ; end
13
+ class LastfmArtistInfoRequest < Base ; self.api_method = 'artist.getinfo&artist=' ; end
14
+ class LastfmArtistPodcastRequest < Base ; self.api_method = 'artist.getpodcast&artist=' ; end
15
+ class LastfmArtistShoutsRequest < Base ; self.api_method = 'artist.getshouts&artist=' ; end
16
+ class LastfmArtistSimilarRequest < Base ; self.api_method = 'artist.getsimilar&artist=' ; end
17
+ class LastfmArtistTagsRequest < Base ; self.api_method = 'artist.gettags&artist=' ; end
18
+ class LastfmArtistTopAlbumsRequest < Base ; self.api_method = 'artist.gettopalbums&artist=' ; end
19
+ class LastfmArtistTopFansRequest < Base ; self.api_method = 'artist.gettopfans&artist=' ; end
20
+ class LastfmArtistTopTagsRequest < Base ; self.api_method = 'artist.gettoptags&artist=' ; end
21
+ class LastfmArtistTopTracksRequest < Base ; self.api_method = 'artist.gettoptracks&artist=' ; end
22
+ class LastfmEventAttendeesRequest < Base ; self.api_method = 'event.getattendees&event=' ; end
23
+ class LastfmEventInfoRequest < Base ; self.api_method = 'event.getinfo&event=' ; end
24
+ class LastfmEventShoutsRequest < Base ; self.api_method = 'event.getshouts&event=' ; end
25
+ class LastfmGeoEventsRequest < Base ; self.api_method = 'geo.getevents&geo=' ; end
26
+ class LastfmGeoTopArtistsRequest < Base ; self.api_method = 'geo.gettopartists&geo=' ; end
27
+ class LastfmGeoTopTracksRequest < Base ; self.api_method = 'geo.gettoptracks&geo=' ; end
28
+ class LastfmGroupMembersRequest < Base ; self.api_method = 'group.getmembers&group=' ; end
29
+ class LastfmGroupWeeklyAlbumChartRequest < Base ; self.api_method = 'group.getweeklyalbumchart&group=' ; end
30
+ class LastfmGroupWeeklyArtistChartRequest < Base ; self.api_method = 'group.getweeklyartistchart&group=' ; end
31
+ class LastfmGroupWeeklyChartListRequest < Base ; self.api_method = 'group.getweeklychartlist&group=' ; end
32
+ class LastfmGroupWeeklyTrackChartRequest < Base ; self.api_method = 'group.getweeklytrackchart&group=' ; end
33
+ class LastfmPlaylistfetchRequest < Base ; self.api_method = 'playlist.fetch&playlist=' ; end
34
+ class LastfmTagSimilarRequest < Base ; self.api_method = 'tag.getsimilar&tag=' ; end
35
+ class LastfmTagTopAlbumsRequest < Base ; self.api_method = 'tag.gettopalbums&tag=' ; end
36
+ class LastfmTagTopArtistsRequest < Base ; self.api_method = 'tag.gettopartists&tag=' ; end
37
+ class LastfmTagTopTagsRequest < Base ; self.api_method = 'tag.gettoptags&tag=' ; end
38
+ class LastfmTagTopTracksRequest < Base ; self.api_method = 'tag.gettoptracks&tag=' ; end
39
+ class LastfmTagWeeklyArtistChartRequest < Base ; self.api_method = 'tag.getweeklyartistchart&tag=' ; end
40
+ class LastfmTagWeeklyChartListRequest < Base ; self.api_method = 'tag.getweeklychartlist&tag=' ; end
41
+ class LastfmTasteometercompareRequest < Base ; self.api_method = 'tasteometer.compare&tasteometer=' ; end
42
+ class LastfmTrackInfoRequest < Base ; self.api_method = 'track.getinfo&track=' ; end
43
+ class LastfmTrackSimilarRequest < Base ; self.api_method = 'track.getsimilar&track=' ; end
44
+ class LastfmTrackTagsRequest < Base ; self.api_method = 'track.gettags&track=' ; end
45
+ class LastfmTrackTopFansRequest < Base ; self.api_method = 'track.gettopfans&track=' ; end
46
+ class LastfmTrackTopTagsRequest < Base ; self.api_method = 'track.gettoptags&track=' ; end
47
+ class LastfmUserEventsRequest < Base ; self.api_method = 'user.getevents&user=' ; end
48
+ class LastfmUserFriendsRequest < Base ; self.api_method = 'user.getfriends&recenttracks=1&user=' ; end
49
+ class LastfmUserInfoRequest < Base ; self.api_method = 'user.getinfo&user=' ; end
50
+ class LastfmUserLovedTracksRequest < Base ; self.api_method = 'user.getlovedtracks&user=' ; end
51
+ class LastfmUserNeighboursRequest < Base ; self.api_method = 'user.getneighbours&user=' ; end
52
+ class LastfmUserPastEventsRequest < Base ; self.api_method = 'user.getpastevents&user=' ; end
53
+ class LastfmUserPlaylistsRequest < Base ; self.api_method = 'user.getplaylists&user=' ; end
54
+ class LastfmUserRecentStationsRequest < Base ; self.api_method = 'user.getrecentstations&user=' ; end
55
+ class LastfmUserRecentTracksRequest < Base ; self.api_method = 'user.getrecenttracks&user=' ; end
56
+ class LastfmUserRecommendedArtistsRequest < Base ; self.api_method = 'user.getrecommendedartists&user=' ; end
57
+ class LastfmUserRecommendedEventsRequest < Base ; self.api_method = 'user.getrecommendedevents&user=' ; end
58
+ class LastfmUserShoutsRequest < Base ; self.api_method = 'user.getshouts&user=' ; end
59
+ class LastfmUserTopAlbumsRequest < Base ; self.api_method = 'user.gettopalbums&user=' ; end
60
+ class LastfmUserTopArtistsRequest < Base ; self.api_method = 'user.gettopartists&user=' ; end
61
+ class LastfmUserTopTagsRequest < Base ; self.api_method = 'user.gettoptags&user=' ; end
62
+ class LastfmUserTopTracksRequest < Base ; self.api_method = 'user.gettoptracks&user=' ; end
63
+ class LastfmUserWeeklyAlbumChartRequest < Base ; self.api_method = 'user.getweeklyalbumchart&user=' ; end
64
+ class LastfmUserWeeklyArtistChartRequest < Base ; self.api_method = 'user.getweeklyartistchart&user=' ; end
65
+ class LastfmUserWeeklyChartListRequest < Base ; self.api_method = 'user.getweeklychartlist&user=' ; end
66
+ class LastfmUserWeeklyTrackChartRequest < Base ; self.api_method = 'user.getweeklytrackchart&user=' ; end
67
+ class LastfmVenueEventsRequest < Base ; self.api_method = 'venue.getevents&venue=' ; end
68
+ class LastfmVenuePastEventsRequest < Base ; self.api_method = 'venue.getpastevents&venue=' ; end
69
+
70
+ # Singular
71
+ class LastfmAlbumInfoRequest < Base ; self.main_fieldname = 'album' ; self.response_type = 'album' ; end
72
+ class LastfmArtistInfoRequest < Base ; self.main_fieldname = 'artist' ; self.response_type = 'artist' ; end
73
+ class LastfmEventInfoRequest < Base ; self.main_fieldname = 'event' ; self.response_type = 'event' ; end
74
+ class LastfmTrackInfoRequest < Base ; self.main_fieldname = 'track' ; self.response_type = 'track' ; end
75
+ class LastfmUserInfoRequest < Base ; self.main_fieldname = 'user' ; self.response_type = 'user' ; end
76
+ # Albums
77
+ class LastfmArtistTopAlbumsRequest < Base ; self.main_fieldname = 'topalbums' ; self.response_type = 'album' ; end
78
+ class LastfmTagTopAlbumsRequest < Base ; self.main_fieldname = 'topalbums' ; self.response_type = 'album' ; end
79
+ class LastfmUserTopAlbumsRequest < Base ; self.main_fieldname = 'topalbums' ; self.response_type = 'album' ; end
80
+ # Tracks
81
+ class LastfmArtistTopTracksRequest < Base ; self.main_fieldname = 'toptracks' ; self.response_type = 'track' ; end
82
+ class LastfmGeoTopTracksRequest < Base ; self.main_fieldname = 'toptracks' ; self.response_type = 'track' ; end
83
+ class LastfmTagTopTracksRequest < Base ; self.main_fieldname = 'toptracks' ; self.response_type = 'track' ; end
84
+ class LastfmUserTopTracksRequest < Base ; self.main_fieldname = 'toptracks' ; self.response_type = 'track' ; end
85
+ class LastfmUserLovedTracksRequest < Base ; self.main_fieldname = 'lovedtracks' ; self.response_type = 'track' ; end
86
+ class LastfmUserRecentTracksRequest < Base ; self.main_fieldname = 'recenttracks' ; self.response_type = 'track' ; end
87
+ class LastfmTrackSimilarRequest < Base ; self.main_fieldname = 'similartracks' ; self.response_type = 'track' ; end
88
+ # Artists
89
+ class LastfmArtistSimilarRequest < Base ; self.main_fieldname = 'artists' ; self.response_type = 'artist' ; end
90
+ class LastfmUserRecommendedArtistsRequest < Base ; self.main_fieldname = 'recommendedartists' ; self.response_type = 'artist' ; end
91
+ class LastfmGeoTopArtistsRequest < Base ; self.main_fieldname = 'topartists' ; self.response_type = 'artist' ; end
92
+ class LastfmTagTopArtistsRequest < Base ; self.main_fieldname = 'topartists' ; self.response_type = 'artist' ; end
93
+ class LastfmUserTopArtistsRequest < Base ; self.main_fieldname = 'topartists' ; self.response_type = 'artist' ; end
94
+ # Events
95
+ class LastfmArtistEventsRequest < Base ; self.main_fieldname = 'events' ; self.response_type = 'event' ; end
96
+ class LastfmGeoEventsRequest < Base ; self.main_fieldname = 'events' ; self.response_type = 'event' ; end
97
+ class LastfmUserEventsRequest < Base ; self.main_fieldname = 'events' ; self.response_type = 'event' ; end
98
+ class LastfmVenueEventsRequest < Base ; self.main_fieldname = 'events' ; self.response_type = 'event' ; end
99
+ class LastfmUserPastEventsRequest < Base ; self.main_fieldname = 'pastevents' ; self.response_type = 'event' ; end
100
+ class LastfmVenuePastEventsRequest < Base ; self.main_fieldname = 'pastevents' ; self.response_type = 'event' ; end
101
+ class LastfmUserRecommendedEventsRequest < Base ; self.main_fieldname = 'recommendedevents' ; self.response_type = 'event' ; end
102
+ # Users
103
+ class LastfmEventAttendeesRequest < Base ; self.main_fieldname = 'attendees' ; self.response_type = 'user' ; end
104
+ class LastfmUserFriendsRequest < Base ; self.main_fieldname = 'friends' ; self.response_type = 'user' ; end
105
+ class LastfmArtistTopFansRequest < Base ; self.main_fieldname = 'topfans' ; self.response_type = 'user' ; end
106
+ class LastfmTrackTopFansRequest < Base ; self.main_fieldname = 'topfans' ; self.response_type = 'user' ; end
107
+ class LastfmGroupMembersRequest < Base ; self.main_fieldname = 'members' ; self.response_type = 'user' ; end
108
+ class LastfmUserNeighboursRequest < Base ; self.main_fieldname = 'neighbours' ; self.response_type = 'user' ; end
109
+ # Shouts
110
+ class LastfmArtistShoutsRequest < Base ; self.main_fieldname = 'shouts' ; self.response_type = 'shout' ; end
111
+ class LastfmEventShoutsRequest < Base ; self.main_fieldname = 'shouts' ; self.response_type = 'shout' ; end
112
+ class LastfmUserShoutsRequest < Base ; self.main_fieldname = 'shouts' ; self.response_type = 'shout' ; end
113
+ # Tags
114
+ class LastfmArtistTagsRequest < Base ; self.main_fieldname = 'similartags' ; self.response_type = 'tag' ; end
115
+ class LastfmTagSimilarRequest < Base ; self.main_fieldname = 'similartags' ; self.response_type = 'tag' ; end
116
+ class LastfmAlbumTagsRequest < Base ; self.main_fieldname = 'tags' ; self.response_type = 'tag' ; end
117
+ class LastfmTrackTagsRequest < Base ; self.main_fieldname = 'tags' ; self.response_type = 'tag' ; end
118
+ class LastfmArtistTopTagsRequest < Base ; self.main_fieldname = 'toptags' ; self.response_type = 'tag' ; end
119
+ class LastfmTagTopTagsRequest < Base ; self.main_fieldname = 'toptags' ; self.response_type = 'tag' ; end
120
+ class LastfmTrackTopTagsRequest < Base ; self.main_fieldname = 'toptags' ; self.response_type = 'tag' ; end
121
+ class LastfmUserTopTagsRequest < Base ; self.main_fieldname = 'toptags' ; self.response_type = 'tag' ; end
122
+ # Charts
123
+ class LastfmGroupWeeklyAlbumChartRequest < Base ; self.main_fieldname = 'weeklyalbumchart' ; self.response_type = 'weeklyalbumchart' ; end
124
+ class LastfmUserWeeklyAlbumChartRequest < Base ; self.main_fieldname = 'weeklyalbumchart' ; self.response_type = 'weeklyalbumchart' ; end
125
+ class LastfmGroupWeeklyArtistChartRequest < Base ; self.main_fieldname = 'weeklyartistchart' ; self.response_type = 'weeklyartistchart' ; end
126
+ class LastfmTagWeeklyArtistChartRequest < Base ; self.main_fieldname = 'weeklyartistchart' ; self.response_type = 'weeklyartistchart' ; end
127
+ class LastfmUserWeeklyArtistChartRequest < Base ; self.main_fieldname = 'weeklyartistchart' ; self.response_type = 'weeklyartistchart' ; end
128
+ class LastfmGroupWeeklyChartListRequest < Base ; self.main_fieldname = 'weeklychartlist' ; self.response_type = 'weeklychartlist' ; end
129
+ class LastfmTagWeeklyChartListRequest < Base ; self.main_fieldname = 'weeklychartlist' ; self.response_type = 'weeklychartlist' ; end
130
+ class LastfmUserWeeklyChartListRequest < Base ; self.main_fieldname = 'weeklychartlist' ; self.response_type = 'weeklychartlist' ; end
131
+ class LastfmGroupWeeklyTrackChartRequest < Base ; self.main_fieldname = 'weeklytrackchart' ; self.response_type = 'weeklytrackchart' ; end
132
+ class LastfmUserWeeklyTrackChartRequest < Base ; self.main_fieldname = 'weeklytrackchart' ; self.response_type = 'weeklytrackchart' ; end
133
+
134
+ # class LastfmArtistImagesRequest < Base ; self.main_fieldname = 'images' ; end
135
+ # class LastfmUserPlaylistsRequest < Base ; self.main_fieldname = 'playlists' ; end
136
+ # class LastfmArtistPodcastRequest < Base ; self.main_fieldname = 'podcast' ; end
137
+ # class LastfmUserRecentStationsRequest < Base ; self.main_fieldname = 'recentstations' ; end
138
+ # class LastfmPlaylistfetchRequest < Base ; self.main_fieldname =''' ; end
139
+ # class LastfmTasteometercompareRequest < Base ; self.main_fieldname ='' ; end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,12 @@
1
+ require 'wuclan/lastfm/scrape/recursive_requests'
2
+
3
+ module Wuclan
4
+ module Lastfm
5
+ module Scrape
6
+
7
+ Base.class_eval do
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ require 'wuclan/lastfm/scrape/base'
2
+ module Wuclan
3
+ module Lastfm
4
+ module Scrape
5
+
6
+ class LastfmRequestStream < Monkeyshines::RequestStream::KlassRequestStream
7
+ include Wuclan::Lastfm::Scrape
8
+
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ # if req.is_a?(LastfmArtistShoutsRequest) then p([req.parsed_contents["shouts"]["shout"].length, req.parsed_contents["shouts"]["@attr"]]) rescue nil
17
+ # end