mirror of
				https://github.com/searxng/searxng.git
				synced 2025-11-03 19:17:07 -05:00 
			
		
		
		
	
						commit
						98aa70cd41
					
				
							
								
								
									
										60
									
								
								searx/engines/spotify.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								searx/engines/spotify.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					## Spotify (Music)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @website     https://spotify.com
 | 
				
			||||||
 | 
					# @provide-api yes (https://developer.spotify.com/web-api/search-item/)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @using-api   yes
 | 
				
			||||||
 | 
					# @results     JSON
 | 
				
			||||||
 | 
					# @stable      yes
 | 
				
			||||||
 | 
					# @parse       url, title, content, embedded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from json import loads
 | 
				
			||||||
 | 
					from urllib import urlencode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# engine dependent config
 | 
				
			||||||
 | 
					categories = ['music']
 | 
				
			||||||
 | 
					paging = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# search-url
 | 
				
			||||||
 | 
					url = 'https://api.spotify.com/'
 | 
				
			||||||
 | 
					search_url = url + 'v1/search?{query}&type=track&offset={offset}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					embedded_url = '<iframe data-src="https://embed.spotify.com/?uri=spotify:track:{audioid}"\
 | 
				
			||||||
 | 
					     width="300" height="80" frameborder="0" allowtransparency="true"></iframe>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# do search-request
 | 
				
			||||||
 | 
					def request(query, params):
 | 
				
			||||||
 | 
					    offset = (params['pageno'] - 1) * 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    params['url'] = search_url.format(query=urlencode({'q': query}),
 | 
				
			||||||
 | 
					                                      offset=offset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# get response from search-request
 | 
				
			||||||
 | 
					def response(resp):
 | 
				
			||||||
 | 
					    results = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    search_res = loads(resp.text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # parse results
 | 
				
			||||||
 | 
					    for result in search_res.get('tracks', {}).get('items', {}):
 | 
				
			||||||
 | 
					        if result['type'] == 'track':
 | 
				
			||||||
 | 
					            title = result['name']
 | 
				
			||||||
 | 
					            url = result['external_urls']['spotify']
 | 
				
			||||||
 | 
					            content = result['artists'][0]['name'] +\
 | 
				
			||||||
 | 
					                " • " +\
 | 
				
			||||||
 | 
					                result['album']['name'] +\
 | 
				
			||||||
 | 
					                " • " + result['name']
 | 
				
			||||||
 | 
					            embedded = embedded_url.format(audioid=result['id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # append result
 | 
				
			||||||
 | 
					            results.append({'url': url,
 | 
				
			||||||
 | 
					                            'title': title,
 | 
				
			||||||
 | 
					                            'embedded': embedded,
 | 
				
			||||||
 | 
					                            'content': content})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # return results
 | 
				
			||||||
 | 
					    return results
 | 
				
			||||||
@ -84,12 +84,6 @@ engines:
 | 
				
			|||||||
#    shortcut : fa
 | 
					#    shortcut : fa
 | 
				
			||||||
#    api_key : 'apikey' # required!
 | 
					#    api_key : 'apikey' # required!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# down - website is under criminal investigation by the UK
 | 
					 | 
				
			||||||
#  - name : filecrop
 | 
					 | 
				
			||||||
#    engine : filecrop
 | 
					 | 
				
			||||||
#    categories : files
 | 
					 | 
				
			||||||
#    shortcut : fc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name : 500px
 | 
					  - name : 500px
 | 
				
			||||||
    engine : www500px
 | 
					    engine : www500px
 | 
				
			||||||
    shortcut : px
 | 
					    shortcut : px
 | 
				
			||||||
@ -109,11 +103,6 @@ engines:
 | 
				
			|||||||
# Or you can use the html non-stable engine, activated by default
 | 
					# Or you can use the html non-stable engine, activated by default
 | 
				
			||||||
    engine : flickr_noapi
 | 
					    engine : flickr_noapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name : general-file
 | 
					 | 
				
			||||||
    engine : generalfile
 | 
					 | 
				
			||||||
    shortcut : gf
 | 
					 | 
				
			||||||
    disabled : True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name : gigablast
 | 
					  - name : gigablast
 | 
				
			||||||
    engine : gigablast
 | 
					    engine : gigablast
 | 
				
			||||||
    shortcut : gb
 | 
					    shortcut : gb
 | 
				
			||||||
@ -201,6 +190,10 @@ engines:
 | 
				
			|||||||
    shortcut : scc
 | 
					    shortcut : scc
 | 
				
			||||||
    disabled : True
 | 
					    disabled : True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name : spotify
 | 
				
			||||||
 | 
					    engine : spotify
 | 
				
			||||||
 | 
					    shortcut : stf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name : subtitleseeker
 | 
					  - name : subtitleseeker
 | 
				
			||||||
    engine : subtitleseeker
 | 
					    engine : subtitleseeker
 | 
				
			||||||
    shortcut : ss
 | 
					    shortcut : ss
 | 
				
			||||||
 | 
				
			|||||||
@ -12,9 +12,14 @@ class TestBlekkoImagesEngine(SearxTestCase):
 | 
				
			|||||||
        dicto['pageno'] = 0
 | 
					        dicto['pageno'] = 0
 | 
				
			||||||
        dicto['safesearch'] = 1
 | 
					        dicto['safesearch'] = 1
 | 
				
			||||||
        params = blekko_images.request(query, dicto)
 | 
					        params = blekko_images.request(query, dicto)
 | 
				
			||||||
        self.assertTrue('url' in params)
 | 
					        self.assertIn('url', params)
 | 
				
			||||||
        self.assertTrue(query in params['url'])
 | 
					        self.assertIn(query, params['url'])
 | 
				
			||||||
        self.assertTrue('blekko.com' in params['url'])
 | 
					        self.assertIn('blekko.com', params['url'])
 | 
				
			||||||
 | 
					        self.assertIn('page', params['url'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dicto['pageno'] = 1
 | 
				
			||||||
 | 
					        params = blekko_images.request(query, dicto)
 | 
				
			||||||
 | 
					        self.assertNotIn('page', params['url'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_response(self):
 | 
					    def test_response(self):
 | 
				
			||||||
        self.assertRaises(AttributeError, blekko_images.response, None)
 | 
					        self.assertRaises(AttributeError, blekko_images.response, None)
 | 
				
			||||||
 | 
				
			|||||||
@ -11,9 +11,14 @@ class TestGoogleImagesEngine(SearxTestCase):
 | 
				
			|||||||
        dicto = defaultdict(dict)
 | 
					        dicto = defaultdict(dict)
 | 
				
			||||||
        dicto['pageno'] = 1
 | 
					        dicto['pageno'] = 1
 | 
				
			||||||
        params = google_images.request(query, dicto)
 | 
					        params = google_images.request(query, dicto)
 | 
				
			||||||
        self.assertTrue('url' in params)
 | 
					        self.assertIn('url', params)
 | 
				
			||||||
        self.assertTrue(query in params['url'])
 | 
					        self.assertIn(query, params['url'])
 | 
				
			||||||
        self.assertTrue('googleapis.com' in params['url'])
 | 
					        self.assertIn('googleapis.com', params['url'])
 | 
				
			||||||
 | 
					        self.assertIn('safe=on', params['url'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dicto['safesearch'] = 0
 | 
				
			||||||
 | 
					        params = google_images.request(query, dicto)
 | 
				
			||||||
 | 
					        self.assertIn('safe=off', params['url'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_response(self):
 | 
					    def test_response(self):
 | 
				
			||||||
        self.assertRaises(AttributeError, google_images.response, None)
 | 
					        self.assertRaises(AttributeError, google_images.response, None)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										124
									
								
								searx/tests/engines/test_spotify.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								searx/tests/engines/test_spotify.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					from collections import defaultdict
 | 
				
			||||||
 | 
					import mock
 | 
				
			||||||
 | 
					from searx.engines import spotify
 | 
				
			||||||
 | 
					from searx.testing import SearxTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestSpotifyEngine(SearxTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_request(self):
 | 
				
			||||||
 | 
					        query = 'test_query'
 | 
				
			||||||
 | 
					        dicto = defaultdict(dict)
 | 
				
			||||||
 | 
					        dicto['pageno'] = 0
 | 
				
			||||||
 | 
					        params = spotify.request(query, dicto)
 | 
				
			||||||
 | 
					        self.assertIn('url', params)
 | 
				
			||||||
 | 
					        self.assertIn(query, params['url'])
 | 
				
			||||||
 | 
					        self.assertIn('spotify.com', params['url'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_response(self):
 | 
				
			||||||
 | 
					        self.assertRaises(AttributeError, spotify.response, None)
 | 
				
			||||||
 | 
					        self.assertRaises(AttributeError, spotify.response, [])
 | 
				
			||||||
 | 
					        self.assertRaises(AttributeError, spotify.response, '')
 | 
				
			||||||
 | 
					        self.assertRaises(AttributeError, spotify.response, '[]')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = mock.Mock(text='{}')
 | 
				
			||||||
 | 
					        self.assertEqual(spotify.response(response), [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = mock.Mock(text='{"data": []}')
 | 
				
			||||||
 | 
					        self.assertEqual(spotify.response(response), [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        json = """
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "tracks": {
 | 
				
			||||||
 | 
					            "href": "https://api.spotify.com/v1/search?query=nosfell&offset=0&limit=20&type=track",
 | 
				
			||||||
 | 
					            "items": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "album": {
 | 
				
			||||||
 | 
					                  "album_type": "album",
 | 
				
			||||||
 | 
					                  "external_urls": {
 | 
				
			||||||
 | 
					                    "spotify": "https://open.spotify.com/album/5c9ap1PBkSGLxT3J73toxA"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "href": "https://api.spotify.com/v1/albums/5c9ap1PBkSGLxT3J73toxA",
 | 
				
			||||||
 | 
					                  "id": "5c9ap1PBkSGLxT3J73toxA",
 | 
				
			||||||
 | 
					                  "name": "Album Title",
 | 
				
			||||||
 | 
					                  "type": "album",
 | 
				
			||||||
 | 
					                  "uri": "spotify:album:5c9ap1PBkSGLxT3J73toxA"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "artists": [
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    "external_urls": {
 | 
				
			||||||
 | 
					                      "spotify": "https://open.spotify.com/artist/0bMc6b75FfZEpQHG1jifKu"
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    "href": "https://api.spotify.com/v1/artists/0bMc6b75FfZEpQHG1jifKu",
 | 
				
			||||||
 | 
					                    "id": "0bMc6b75FfZEpQHG1jifKu",
 | 
				
			||||||
 | 
					                    "name": "Artist Name",
 | 
				
			||||||
 | 
					                    "type": "artist",
 | 
				
			||||||
 | 
					                    "uri": "spotify:artist:0bMc6b75FfZEpQHG1jifKu"
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                "disc_number": 1,
 | 
				
			||||||
 | 
					                "duration_ms": 202386,
 | 
				
			||||||
 | 
					                "explicit": false,
 | 
				
			||||||
 | 
					                "external_ids": {
 | 
				
			||||||
 | 
					                  "isrc": "FRV640600067"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "external_urls": {
 | 
				
			||||||
 | 
					                  "spotify": "https://open.spotify.com/track/2GzvFiedqW8hgqUpWcASZa"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "href": "https://api.spotify.com/v1/tracks/2GzvFiedqW8hgqUpWcASZa",
 | 
				
			||||||
 | 
					                "id": "1000",
 | 
				
			||||||
 | 
					                "is_playable": true,
 | 
				
			||||||
 | 
					                "name": "Title of track",
 | 
				
			||||||
 | 
					                "popularity": 6,
 | 
				
			||||||
 | 
					                "preview_url": "https://p.scdn.co/mp3-preview/7b8ecda580965a066b768c2647f877e43f7b1a0a",
 | 
				
			||||||
 | 
					                "track_number": 3,
 | 
				
			||||||
 | 
					                "type": "track",
 | 
				
			||||||
 | 
					                "uri": "spotify:track:2GzvFiedqW8hgqUpWcASZa"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "limit": 20,
 | 
				
			||||||
 | 
					            "next": "https://api.spotify.com/v1/search?query=nosfell&offset=20&limit=20&type=track",
 | 
				
			||||||
 | 
					            "offset": 0,
 | 
				
			||||||
 | 
					            "previous": null,
 | 
				
			||||||
 | 
					            "total": 107
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = mock.Mock(text=json)
 | 
				
			||||||
 | 
					        results = spotify.response(response)
 | 
				
			||||||
 | 
					        self.assertEqual(type(results), list)
 | 
				
			||||||
 | 
					        self.assertEqual(len(results), 1)
 | 
				
			||||||
 | 
					        self.assertEqual(results[0]['title'], 'Title of track')
 | 
				
			||||||
 | 
					        self.assertEqual(results[0]['url'], 'https://open.spotify.com/track/2GzvFiedqW8hgqUpWcASZa')
 | 
				
			||||||
 | 
					        self.assertEqual(results[0]['content'], 'Artist Name • Album Title • Title of track')
 | 
				
			||||||
 | 
					        self.assertIn('1000', results[0]['embedded'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        json = """
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "tracks": {
 | 
				
			||||||
 | 
					            "href": "https://api.spotify.com/v1/search?query=nosfell&offset=0&limit=20&type=track",
 | 
				
			||||||
 | 
					            "items": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                "href": "https://api.spotify.com/v1/tracks/2GzvFiedqW8hgqUpWcASZa",
 | 
				
			||||||
 | 
					                "id": "1000",
 | 
				
			||||||
 | 
					                "is_playable": true,
 | 
				
			||||||
 | 
					                "name": "Title of track",
 | 
				
			||||||
 | 
					                "popularity": 6,
 | 
				
			||||||
 | 
					                "preview_url": "https://p.scdn.co/mp3-preview/7b8ecda580965a066b768c2647f877e43f7b1a0a",
 | 
				
			||||||
 | 
					                "track_number": 3,
 | 
				
			||||||
 | 
					                "type": "album",
 | 
				
			||||||
 | 
					                "uri": "spotify:track:2GzvFiedqW8hgqUpWcASZa"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "limit": 20,
 | 
				
			||||||
 | 
					            "next": "https://api.spotify.com/v1/search?query=nosfell&offset=20&limit=20&type=track",
 | 
				
			||||||
 | 
					            "offset": 0,
 | 
				
			||||||
 | 
					            "previous": null,
 | 
				
			||||||
 | 
					            "total": 107
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = mock.Mock(text=json)
 | 
				
			||||||
 | 
					        results = spotify.response(response)
 | 
				
			||||||
 | 
					        self.assertEqual(type(results), list)
 | 
				
			||||||
 | 
					        self.assertEqual(len(results), 0)
 | 
				
			||||||
@ -75,12 +75,6 @@ class TestYahooEngine(SearxTestCase):
 | 
				
			|||||||
    <li>
 | 
					    <li>
 | 
				
			||||||
        <div class="dd algo lst Sr">
 | 
					        <div class="dd algo lst Sr">
 | 
				
			||||||
            <div class="compTitle">
 | 
					            <div class="compTitle">
 | 
				
			||||||
                <h3 class="title"><a class=" td-u" href="http://r.search.yahoo.com/_ylt=AwrBT7zgEudUW.wAe2ZXNyoA;
 | 
					 | 
				
			||||||
                     _ylu=X3oDMTBybGY3bmpvBGNvbG8DYmYxBHBvcwMyBHZ0aWQDBHNlYwNzcg--/RV=2\/RE=1424458593/RO=10
 | 
					 | 
				
			||||||
                     /RU=https%3a%2f%2fthis.is.the.second.url%2f/RK=0/RS=jIctjj_cBH1Efj88GCgHKp3__Qk-"
 | 
					 | 
				
			||||||
                     target="_blank" data-bid="54e712e136926">
 | 
					 | 
				
			||||||
                     This is the second <b><b>title</b></b></a>
 | 
					 | 
				
			||||||
                </h3>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="compText aAbs">
 | 
					            <div class="compText aAbs">
 | 
				
			||||||
                <p class="lh-18">This is the second content</p>
 | 
					                <p class="lh-18">This is the second content</p>
 | 
				
			||||||
@ -102,16 +96,12 @@ class TestYahooEngine(SearxTestCase):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        response = mock.Mock(text=html)
 | 
					        response = mock.Mock(text=html)
 | 
				
			||||||
        results = yahoo.response(response)
 | 
					        results = yahoo.response(response)
 | 
				
			||||||
        print results
 | 
					 | 
				
			||||||
        self.assertEqual(type(results), list)
 | 
					        self.assertEqual(type(results), list)
 | 
				
			||||||
        self.assertEqual(len(results), 3)
 | 
					        self.assertEqual(len(results), 2)
 | 
				
			||||||
        self.assertEqual(results[0]['title'], 'This is the title')
 | 
					        self.assertEqual(results[0]['title'], 'This is the title')
 | 
				
			||||||
        self.assertEqual(results[0]['url'], 'https://this.is.the.url/')
 | 
					        self.assertEqual(results[0]['url'], 'https://this.is.the.url/')
 | 
				
			||||||
        self.assertEqual(results[0]['content'], 'This is the content')
 | 
					        self.assertEqual(results[0]['content'], 'This is the content')
 | 
				
			||||||
        self.assertEqual(results[1]['title'], 'This is the second title')
 | 
					        self.assertEqual(results[1]['suggestion'], 'This is the suggestion')
 | 
				
			||||||
        self.assertEqual(results[1]['url'], 'https://this.is.the.second.url/')
 | 
					 | 
				
			||||||
        self.assertEqual(results[1]['content'], 'This is the second content')
 | 
					 | 
				
			||||||
        self.assertEqual(results[2]['suggestion'], 'This is the suggestion')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        html = """
 | 
					        html = """
 | 
				
			||||||
<ol class="reg mb-15 searchCenterMiddle">
 | 
					<ol class="reg mb-15 searchCenterMiddle">
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,7 @@ from searx.tests.engines.test_piratebay import *  # noqa
 | 
				
			|||||||
from searx.tests.engines.test_searchcode_code import *  # noqa
 | 
					from searx.tests.engines.test_searchcode_code import *  # noqa
 | 
				
			||||||
from searx.tests.engines.test_searchcode_doc import *  # noqa
 | 
					from searx.tests.engines.test_searchcode_doc import *  # noqa
 | 
				
			||||||
from searx.tests.engines.test_soundcloud import *  # noqa
 | 
					from searx.tests.engines.test_soundcloud import *  # noqa
 | 
				
			||||||
 | 
					from searx.tests.engines.test_spotify import *  # noqa
 | 
				
			||||||
from searx.tests.engines.test_stackoverflow import *  # noqa
 | 
					from searx.tests.engines.test_stackoverflow import *  # noqa
 | 
				
			||||||
from searx.tests.engines.test_startpage import *  # noqa
 | 
					from searx.tests.engines.test_startpage import *  # noqa
 | 
				
			||||||
from searx.tests.engines.test_subtitleseeker import *  # noqa
 | 
					from searx.tests.engines.test_subtitleseeker import *  # noqa
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user