Repository: ytdl-org/youtube-dl

Issue:
Netease broken again
Tests in master for extractor will fail.
Looks like they switched download server again.



Discussion:
Note, if you switch to m2 and switch to using some chinese DNS server it works just fine.
Support for that could be implemented (for downloads using youtube-dl) using pydns/dnspython. But I doubt that youtube-dl would want such dependency.

Users could also set their resolvers to use one of chinese public DNS servers or even just pu2 m2.music.126.net into their hosts file.

In any case - current m5.music.126.net server hardcoded into youtube-dl doesn't work

Which URL are you trying to download?

@yan12125 
eg the one from the tests http://music.163.com/#/song?id=32102397  will get you http://m5.music.126.net/Hj8j3B9wn2TetQue7VJ-iQ==/2901611187169450.mp3  and end up with 404.
So far I've been getting 404 for all links

When I click play in http://music.163.com/#/song?id=32102397, it says that I have to pay for this song to listen to it:

```
唱片公司要求，当前资源须付费使用
```

Which browser are you using? Are you inside Mainland China?

I haven't even tried to open it in browser. I just tried to download it using youtube-dl . Its one of the test urls inside extractor/neteasemusic.py 
No, I'm not in China

If a video/song can't be played in browsers, there's nothing much youtube-dl can do. Test cases may become invalid as time goes by. It's possible this song is once free but later requires payment.

Sure, but the sond could be downloaded from "http://m2.music.126.net/zUA4iqTZyZFncNkFNUwAjw==/5991238859813420.mp3" 
Do you have an example of url that works with youtube-dl?

Have you changed your DNS settings? m2.music.126.net is resolved to 1.1.1.1, which is apparently not a NetEase IP.

```
$ curl -v http://m2.music.126.net/Hj8j3B9wn2TetQue7VJ-iQ\=\=/2901611187169450.mp3
*   Trying 1.1.1.1...
* connect to 1.1.1.1 port 80 failed: Connection refused
* Failed to connect to m2.music.126.net port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to m2.music.126.net port 80: Connection refused
```


Resolving patch:
diff --git a/youtube_dl/extractor/neteasemusic.py b/youtube_dl/extractor/neteasemusic.py
--- a/youtube_dl/extractor/neteasemusic.py
+++ b/youtube_dl/extractor/neteasemusic.py
@@ -32,23 +32,32 @@ def _encrypt(cls, dfsid):
         result = b64encode(m.digest()).decode('ascii')
         return result.replace('/', '_').replace('+', '-')
 
-    @classmethod
-    def extract_formats(cls, info):
+    def extract_formats(self, info):
         formats = []
-        for song_format in cls._FORMATS:
+        for song_format in self._FORMATS:
             details = info.get(song_format)
             if not details:
                 continue
-            formats.append({
-                'url': 'http://m5.music.126.net/%s/%s.%s' %
-                       (cls._encrypt(details['dfsId']), details['dfsId'],
-                        details['extension']),
-                'ext': details.get('extension'),
-                'abr': details.get('bitrate', 0) / 1000,
-                'format_id': song_format,
-                'filesize': details.get('size'),
-                'asr': details.get('sr')
-            })
+            song_file_path = '/%s/%s.%s' % (
+                self._encrypt(details['dfsId']), details['dfsId'], details['extension'])
+
+            # 203.130.59.9, 124.40.233.182, 115.231.74.139, etc is a reverse proxy-like feature
+            # from NetEase's CDN provider that can be used if m5.music.126.net does not
+            # work, especially for users outside of Mainland China
+            # via: https://github.com/JixunMoe/unblock-163/issues/3#issuecomment-163115880
+            for host in ('http://m5.music.126.net', 'http://115.231.74.139/m1.music.126.net',
+                         'http://124.40.233.182/m1.music.126.net', 'http://203.130.59.9/m1.music.126.net'):
+                song_url = host + song_file_path
+                if self._is_valid_url(song_url, info['id'], 'song'):
+                    formats.append({
+                        'url': song_url,
+                        'ext': details.get('extension'),
+                        'abr': details.get('bitrate', 0) / 1000,
+                        'format_id': song_format,
+                        'filesize': details.get('size'),
+                        'asr': details.get('sr')
+                    })
+                    break
         return formats
 
     @classmethod


Test patch: