Repository: yt-dlp/yt-dlp
Pull request #5173 (2022-10-07 22:18:37)

[extractor/viu] Added support for second subtitle and updated testing url
**IMPORTANT**: PRs without the template will be CLOSED

### Description of your *pull request* and other information

Some shows will contain more than one subtitle as shown below, this commit added support for extracting it.
Testing URLs are also not available anymore, they were updated accordingly.

URL: `https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3`
```
      {
         "is_default":1,
         "name":"繁體中文",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/4279146490/799524ec920a46b1960dc18729d2a98261bcb8e3",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/4279146490/799524ec920a46b1960dc18729d2a98261bcb8e3",
         "product_subtitle_id":"240856",
         "product_subtitle_language_id":"1",
         "second_subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/2156290699/c04164fcd48fc654733a877382f018f1cb381d89",
         "second_subtitle_position":1,
         "code":"zh-Hant"
      }
```

URL: `https://www.viu.com/ott/hk/zh-hk/vod/444666/%E6%88%91%E7%9A%84%E5%AE%A4%E5%8F%8B%E6%98%AF%E4%B9%9D%E5%B0%BE%E7%8B%90`
```
      {
         "is_default":1,
         "name":"繁體中文",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/3058849036/65e41c405b9405739271c1badaa303868602ed3a",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/3058849036/65e41c405b9405739271c1badaa303868602ed3a",
         "product_subtitle_id":"253068",
         "product_subtitle_language_id":"1",
         "second_subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/674676911/744f1fed7f536677b76e7e695f7ff03a7c7ad330",
         "second_subtitle_position":0,
         "code":"zh-Hant"
      },
```

Command running on latest version
Command: `./yt-dlp -vU --list-subs --no-playlist https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3`

```
[debug] Command-line config: ['-vU', '--list-subs', '--no-playlist', 'https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.10.04 [4e0511f] (win32_exe)
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22623-SP0
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] exe versions: ffmpeg 2022-03-07-git-e645a1ddb9-essentials_build-www.gyan.dev (setts), ffprobe 2022-03-07-git-e645a1ddb9-essentials_build-www.gyan.dev
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.09.24, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
[debug] Proxy map: {}
[debug] Loaded 1690 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.10.04, Current version: 2022.10.04
yt-dlp is up to date (2022.10.04)
[debug] [viu:ott] Extracting URL: https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3
[viu:ott] 430078: Downloading video info
[viu:ott] Downloading just the video 430078 because of --no-playlist
[viu:ott] 430078: Getting bearer token
[viu:ott] 430078: Downloading stream info
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] Available subtitles for 430078:
Language Formats
English  srt
Indo     srt
ภาษาไทย  srt
繁體中文     srt
```

Command run on PR version
Command: `python -m yt_dlp -vU --list-subs --no-playlist https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3`

```
[debug] Command-line config: ['-vU', '--list-subs', '--no-playlist', 'https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8 
[debug] yt-dlp version 2022.10.04 [4e0511f27] (source) 
[debug] Lazy loading extractors is disabled
[debug] Plugins: ['SamplePluginIE', 'SamplePluginPP']
[debug] Git HEAD: 35b27db60 
[debug] Python 3.9.13 (CPython 64bit) - Windows-10-10.0.22623-SP0  
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs 
[debug] exe versions: ffmpeg 2022-03-07-git-e645a1ddb9-essentials_build-www.gyan.dev (setts), ffprobe 2022-03-07-git-e645a1ddb9-essentials_build-www.gyan.dev
[debug] Optional libraries: certifi-2021.10.08, sqlite3-2.6.0
[debug] Proxy map: {}
[debug] Loaded 1692 extractors 
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.10.04, Current version: 2022.10.04 
yt-dlp is up to date (2022.10.04)
[debug] [viu:ott] Extracting URL: https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3 
[viu:ott] 430078: Downloading video info
[viu:ott] Downloading just the video 430078 because of --no-playlist 
[viu:ott] 430078: Getting bearer token
[viu:ott] 430078: Downloading stream info 
[debug] Formats sorted by: hasvid, ie_pref, lang, quality, res, fps, hdr:12(7), vcodec:vp9.2(10), channels, acodec, filesize, fs_approx, tbr, vbr, abr, asr, proto, vext, aext, hasaud, source, id
[info] Available subtitles for 430078:
Language   Formats
English    srt
Indo       srt
ภาษาไทย    srt
繁體中文       srt
繁體中文Second srt
```

second_subtitle_url can be seen in the json below under the subtitle section

Raw output of video_data for url `https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3`
```json
{
   "series_id":"22766",
   "product_id":"430078",
   "number":"1",
   "synopsis":"大韓民國的1%",
   "description":"新一季第一集有兩位特別嘉賓—宋恩伊和尹燦榮。恩伊是在錫其中一位好朋友，她和在錫表現出完美的默契；燦榮則展示了他意想不到的魅力。本集，第六感團隊與韓國首1%有關的人見面。",
   "schedule_start_time":"1647532815",
   "schedule_end_time":"1742831955",
   "skip_intro_start_time":0,
   "skip_intro_end_time":15,
   "free_time":1648742415,
   "premium_time":0,
   "is_free_premium_time":0,
   "duration_start":0,
   "cover_image_url":"https://d2anahhhmp1ffz.cloudfront.net/1313295781/d2b14f48d008ef2f3a9200c98d8e9b63967b9cc2",
   "ccs_product_id":"f4976fbfb196d21288ff3db519bab49a",
   "allow_download":"1",
   "share_url":"https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3",
   "subtitle":[
      {
         "is_default":0,
         "name":"English",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/925751760/ba938f69a7b858b8c709d7cc946cfe0084ca675b",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/925751760/ba938f69a7b858b8c709d7cc946cfe0084ca675b",
         "product_subtitle_id":"240262",
         "product_subtitle_language_id":"3",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"en"
      },
      {
         "is_default":0,
         "name":"Indo",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/3255922512/f399e750351924c0aa91557231731e533c302ef8",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/3255922512/f399e750351924c0aa91557231731e533c302ef8",
         "product_subtitle_id":"240277",
         "product_subtitle_language_id":"7",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"id"
      },
      {
         "is_default":0,
         "name":"ภาษาไทย",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/4237993129/b69f134865c5f72aceabe32f47b546f20e2b1d22",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/4237993129/b69f134865c5f72aceabe32f47b546f20e2b1d22",
         "product_subtitle_id":"240435",
         "product_subtitle_language_id":"8",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"th"
      },
      {
         "is_default":1,
         "name":"繁體中文",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/4279146490/799524ec920a46b1960dc18729d2a98261bcb8e3",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/4279146490/799524ec920a46b1960dc18729d2a98261bcb8e3",
         "product_subtitle_id":"240856",
         "product_subtitle_language_id":"1",
         "second_subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/2156290699/c04164fcd48fc654733a877382f018f1cb381d89",
         "second_subtitle_position":1,
         "code":"zh-Hant"
      }
   ],
   "focus":[
      
   ],
   "ad":[
      {
         "position":0,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007011DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007012DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"0"
      },
      {
         "position":1,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007021DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007021_bDP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"2883"
      },
      {
         "position":2,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007022DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007022_bDP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"4613"
      },
      {
         "position":3,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007023DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F430078%2F&cust_params=ott_cate%3Dkoreanvariety%26ott_series_id%3D22766",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"5934"
      }
   ],
   "is_movie":0,
   "is_parental_lock_limited":"0",
   "allow_play_big_screen":"1",
   "play_big_screen_start_time":"0",
   "play_big_screen_end_time":"0",
   "time_duration":"6614",
   "campaign_name":"",
   "user_level":0,
   "seo_title":"",
   "seo_description":"",
   "has_content_window":"1",
   "classification":"",
   "censorship_ads_mp4_url":"",
   "client_logo":"None",
   "keyword":"Sixth Sense 3, 식스센스 3, 第六感 3, 第六感 第三季, 推理CJ, 最新上架, 韓國綜藝, 劉在錫, Jessi, 吳娜拉, 李美珠, 李相燁, 全昭旻, Yoo Jae Suk, Oh Na Ra, Lee Sang Yeob, Lee Mi Joo",
   "offline_time":"1742831955",
   "poster_logo_url":"",
   "source_flag":"0",
   "allow_tv":"1",
   "released_product_total":14,
   "landscape_episode_image_url":"https://d2anahhhmp1ffz.cloudfront.net/1313295781/d2b14f48d008ef2f3a9200c98d8e9b63967b9cc2",
   "allow_chromecast_play_big_screen":1,
   "allow_airplay_play_big_screen":1,
   "product_tag":[
      
   ]
}
```

Raw output of video_data for url `https://www.viu.com/ott/hk/zh-hk/vod/444666/%E6%88%91%E7%9A%84%E5%AE%A4%E5%8F%8B%E6%98%AF%E4%B9%9D%E5%B0%BE%E7%8B%90`
```json
{
   "series_id":"23807",
   "product_id":"444666",
   "number":"1",
   "synopsis":"同居",
   "description":"申又如，生於高麗顯宗13年，是畢業於成均館的九尾狐男人。李 丹，21世紀女大學生，典型的現代人類。兩個看似完全沒有交集的「種族」，因狐狸內丹而產生糾纏，開始了相差977年的……不，一整個世紀的驚心動魄的同居生活……",
   "schedule_start_time":"1657857624",
   "schedule_end_time":"1752508764",
   "skip_intro_start_time":0,
   "skip_intro_end_time":30,
   "free_time":0,
   "premium_time":0,
   "is_free_premium_time":0,
   "duration_start":0,
   "cover_image_url":"https://d2anahhhmp1ffz.cloudfront.net/2987913268/3d6204b53267f1a17699e131b5e7673e8dfad1ed",
   "ccs_product_id":"3cd4671a4c110ac693f11f933afb1887",
   "allow_download":"1",
   "share_url":"https://www.viu.com/ott/hk/zh-hk/vod/444666/%E6%88%91%E7%9A%84%E5%AE%A4%E5%8F%8B%E6%98%AF%E4%B9%9D%E5%B0%BE%E7%8B%90",
   "subtitle":[
      {
         "is_default":0,
         "name":"English",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/1400088194/8c7dc4228930683b66fda48f7125ad14ad5f6ac3",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/1400088194/8c7dc4228930683b66fda48f7125ad14ad5f6ac3",
         "product_subtitle_id":"240524",
         "product_subtitle_language_id":"3",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"en"
      },
      {
         "is_default":0,
         "name":"ภาษาไทย",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/380887317/6dca5aa82e86d2c16831d1555154894a0d151dcf",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/380887317/6dca5aa82e86d2c16831d1555154894a0d151dcf",
         "product_subtitle_id":"244020",
         "product_subtitle_language_id":"8",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"th"
      },
      {
         "is_default":1,
         "name":"繁體中文",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/3058849036/65e41c405b9405739271c1badaa303868602ed3a",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/3058849036/65e41c405b9405739271c1badaa303868602ed3a",
         "product_subtitle_id":"253068",
         "product_subtitle_language_id":"1",
         "second_subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/674676911/744f1fed7f536677b76e7e695f7ff03a7c7ad330",
         "second_subtitle_position":0,
         "code":"zh-Hant"
      },
      {
         "is_default":0,
         "name":"Malay",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/1012457802/9dddd0b1aa721c1324a1b9f1262d9b1408c38446",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/1012457802/9dddd0b1aa721c1324a1b9f1262d9b1408c38446",
         "product_subtitle_id":"255964",
         "product_subtitle_language_id":"6",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"ms"
      },
      {
         "is_default":0,
         "name":"မြန်မာဘာသာ",
         "url":"https://d2anahhhmp1ffz.cloudfront.net/4097893180//ff2ba84462eedbae739cc461af0c209deeda221d",
         "subtitle_url":"https://d2anahhhmp1ffz.cloudfront.net/4097893180/ff2ba84462eedbae739cc461af0c209deeda221d",
         "product_subtitle_id":"255980",
         "product_subtitle_language_id":"10",
         "second_subtitle_url":"",
         "second_subtitle_position":1,
         "code":"my"
      }
   ],
   "focus":[
      
   ],
   "ad":[
      {
         "position":0,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007011DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007012DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007013DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"0"
      },
      {
         "position":1,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007021DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007021_bDP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"970"
      },
      {
         "position":2,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007022DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            },
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007022_bDP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"2051"
      },
      {
         "position":3,
         "code_list":[
            {
               "ad_stuff":"https://pubads.g.doubleclick.net/gampad/ads?sz=1280x720&iu=/94348418/OTTHK100007023DP&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=https%3A%2F%2Fwww.viu.com%2Fott%2Fhk%2Fzh-hk%2Fvod%2F444666%2F&cust_params=ott_cate%3Dkoreandrama%26ott_series_id%3D23807",
               "ad_track":"None",
               "ad_is_user_pay":"0",
               "ima_force":"1"
            }
         ],
         "start_time":"3185"
      }
   ],
   "is_movie":0,
   "is_parental_lock_limited":"0",
   "allow_play_big_screen":"1",
   "play_big_screen_start_time":"0",
   "play_big_screen_end_time":"0",
   "time_duration":"3932",
   "campaign_name":"",
   "user_level":0,
   "seo_title":"",
   "seo_description":"",
   "has_content_window":"1",
   "classification":"",
   "censorship_ads_mp4_url":"",
   "client_logo":"None",
   "keyword":"我的室友是九尾狐, My Roommate is a Gumiho, 간 떨어 지는 동거, 韓劇, 愛情, 喜劇, 奇幻, 愛奇藝, tvn, 南成宇, 崔寶林, 白善宇, 張基龍, 李惠利, 姜漢娜, 金度完, 裴仁爀, Jang Ki Yong, Lee Hye Ri, Kang Han Na, Kim Do Wan, Bae In Hyuk",
   "offline_time":"1752508764",
   "poster_logo_url":"",
   "source_flag":"0",
   "allow_tv":"1",
   "released_product_total":16,
   "landscape_episode_image_url":"https://d2anahhhmp1ffz.cloudfront.net/2987913268/3d6204b53267f1a17699e131b5e7673e8dfad1ed",
   "allow_chromecast_play_big_screen":1,
   "allow_airplay_play_big_screen":1,
   "product_tag":[
      
   ]
}
```

### Before submitting a *pull request* make sure you have:
- [X] At least skimmed through [contributing guidelines](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions) including [yt-dlp coding conventions](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#yt-dlp-coding-conventions)
- [X] [Searched](https://github.com/yt-dlp/yt-dlp/search?q=is%3Apr&type=Issues) the bugtracker for similar pull requests
- [X] Checked the code with [flake8](https://pypi.python.org/pypi/flake8) and [ran relevant tests](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions)

### In order to be accepted and merged into yt-dlp each piece of code must be in public domain or released under [Unlicense](http://unlicense.org/). Check one of the following options:
- [X] I am the original author of this code and I am willing to release it under [Unlicense](http://unlicense.org/)

### What is the purpose of your *pull request*?
- [X] Fix or improvement to an extractor (Make sure to add/update tests)


pukkandan: Could u explain to me how the "second subtitle" different from the normal one?

Patch:
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
index 2b603f4f2..75cb3fcab 100644
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -1181,6 +1181,7 @@
 from .noovo import NoovoIE
 from .normalboots import NormalbootsIE
 from .nosvideo import NosVideoIE
+from .nosnl import NOSNLArticleIE
 from .nova import (
     NovaEmbedIE,
     NovaIE,
@@ -1565,6 +1566,7 @@
 from .sapo import SapoIE
 from .savefrom import SaveFromIE
 from .sbs import SBSIE
+from .screen9 import Screen9IE
 from .screencast import ScreencastIE
 from .screencastomatic import ScreencastOMaticIE
 from .scrippsnetworks import (
diff --git a/yt_dlp/extractor/nosnl.py b/yt_dlp/extractor/nosnl.py
new file mode 100644
index 000000000..eba94c416
--- /dev/null
+++ b/yt_dlp/extractor/nosnl.py
@@ -0,0 +1,95 @@
+from .common import InfoExtractor
+from ..utils import parse_duration, parse_iso8601, traverse_obj
+
+
+class NOSNLArticleIE(InfoExtractor):
+    _VALID_URL = r'https?://nos\.nl/((?!video)(\w+/)?\w+/)\d+-(?P<display_id>[\w-]+)'
+    _TESTS = [
+        {
+            # only 1 video
+            'url': 'https://nos.nl/nieuwsuur/artikel/2440353-verzakking-door-droogte-dreigt-tot-een-miljoen-kwetsbare-huizen',
+            'info_dict': {
+                'id': '2440340',
+                'ext': 'mp4',
+                'description': 'md5:5f83185d902ac97af3af4bed7ece3db5',
+                'title': '\'We hebben een huis vol met scheuren\'',
+                'duration': 95.0,
+                'thumbnail': 'https://cdn.nos.nl/image/2022/08/12/887149/3840x2160a.jpg',
+            }
+        }, {
+            # more than 1 video
+            'url': 'https://nos.nl/artikel/2440409-vannacht-sliepen-weer-enkele-honderden-asielzoekers-in-ter-apel-buiten',
+            'info_dict': {
+                'id': '2440409',
+                'title': 'Vannacht sliepen weer enkele honderden asielzoekers in Ter Apel buiten',
+                'description': 'Er werd wel geprobeerd om kwetsbare migranten onderdak te bieden, zegt het COA.',
+                'tags': ['aanmeldcentrum', 'Centraal Orgaan opvang asielzoekers', 'COA', 'asielzoekers', 'Ter Apel'],
+                'modified_timestamp': 1660452773,
+                'modified_date': '20220814',
+                'upload_date': '20220813',
+                'thumbnail': 'https://cdn.nos.nl/image/2022/07/18/880346/1024x576a.jpg',
+                'timestamp': 1660401384,
+            },
+            'playlist_count': 2,
+        }, {
+            # audio + video
+            'url': 'https://nos.nl/artikel/2440789-wekdienst-16-8-groningse-acties-tien-jaar-na-zware-aardbeving-femke-bol-in-actie-op-ek-atletiek',
+            'info_dict': {
+                'id': '2440789',
+                'title': 'Wekdienst 16/8: Groningse acties tien jaar na zware aardbeving • Femke Bol in actie op EK atletiek ',
+                'description': 'Nieuws, weer, verkeer: met dit overzicht begin je geïnformeerd aan de dag.',
+                'tags': ['wekdienst'],
+                'modified_date': '20220816',
+                'modified_timestamp': 1660625449,
+                'timestamp': 1660625449,
+                'upload_date': '20220816',
+                'thumbnail': 'https://cdn.nos.nl/image/2022/08/16/888178/1024x576a.jpg',
+            },
+            'playlist_count': 2,
+        }
+    ]
+
+    def _entries(self, nextjs_json, display_id):
+        for item in nextjs_json['items']:
+            if item.get('type') == 'video':
+                formats, subtitle = self._extract_m3u8_formats_and_subtitles(
+                    traverse_obj(item, ('source', 'url')), display_id, ext='mp4')
+                yield {
+                    'id': str(item['id']),
+                    'title': item.get('title'),
+                    'description': item.get('description'),
+                    'formats': formats,
+                    'subtitles': subtitle,
+                    'duration': parse_duration(item.get('duration')),
+                    'thumbnails': [{
+                        'url': traverse_obj(image, ('url', ...), get_all=False),
+                        'width': image.get('width'),
+                        'height': image.get('height')
+                    } for image in traverse_obj(item, ('imagesByRatio', ...))[0]],
+                }
+
+            elif item.get('type') == 'audio':
+                yield {
+                    'id': str(item['id']),
+                    'title': item.get('title'),
+                    'url': traverse_obj(item, ('media', 'src')),
+                    'ext': 'mp3',
+                }
+
+    def _real_extract(self, url):
+        display_id = self._match_valid_url(url).group('display_id')
+        webpage = self._download_webpage(url, display_id)
+
+        nextjs_json = self._search_nextjs_data(webpage, display_id)['props']['pageProps']['data']
+        return {
+            '_type': 'playlist',
+            'entries': self._entries(nextjs_json, display_id),
+            'id': str(nextjs_json['id']),
+            'title': nextjs_json.get('title') or self._html_search_meta(['title', 'og:title', 'twitter:title'], webpage),
+            'description': (nextjs_json.get('description')
+                            or self._html_search_meta(['description', 'twitter:description', 'og:description'], webpage)),
+            'tags': nextjs_json.get('keywords'),
+            'modified_timestamp': parse_iso8601(nextjs_json.get('modifiedAt')),
+            'thumbnail': nextjs_json.get('shareImageSrc') or self._html_search_meta(['og:image', 'twitter:image'], webpage),
+            'timestamp': parse_iso8601(nextjs_json.get('publishedAt'))
+        }
diff --git a/yt_dlp/extractor/screen9.py b/yt_dlp/extractor/screen9.py
new file mode 100644
index 000000000..eae652af7
--- /dev/null
+++ b/yt_dlp/extractor/screen9.py
@@ -0,0 +1,63 @@
+from .common import InfoExtractor
+from ..utils import traverse_obj
+
+
+class Screen9IE(InfoExtractor):
+    _VALID_URL = r'https?://(?:\w+\.screen9\.(?:tv|com)|play\.su\.se)/(?:embed|media)/(?P<id>[^?#/]+)'
+    _TESTS = [
+        {
+            'url': 'https://api.screen9.com/embed/8kTNEjvoXGM33dmWwF0uDA',
+            'md5': 'd60d23f8980583b930724b01fa6ddb41',
+            'info_dict': {
+                'id': '8kTNEjvoXGM33dmWwF0uDA',
+                'ext': 'mp4',
+                'title': 'Östersjön i förändrat klimat',
+                'thumbnail': r're:^https?://.+\.jpg',
+            },
+        },
+        {
+            'url': 'https://folkhogskolekanalen.screen9.tv/media/gy35PKLHe-5K29RYHga2bw/ett-starkare-samhalle-en-snabbguide-om-sveriges-folkhogskolor',
+            'md5': 'c9389806e78573ea34fc48b6f94465dc',
+            'info_dict': {
+                'id': 'gy35PKLHe-5K29RYHga2bw',
+                'ext': 'mp4',
+                'title': 'Ett starkare samhälle - en snabbguide om Sveriges folkhögskolor',
+                'thumbnail': r're:^https?://.+\.jpg',
+            },
+        },
+        {
+            'url': 'https://play.su.se/media/H1YA0EYNCxiesrSU1kaRBQ/baltic-breakfast',
+            'md5': '2b817647c3058002526269deff4c0683',
+            'info_dict': {
+                'id': 'H1YA0EYNCxiesrSU1kaRBQ',
+                'ext': 'mp4',
+                'title': 'Baltic Breakfast',
+                'thumbnail': r're:^https?://.+\.jpg',
+            },
+        },
+    ]
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+        webpage = self._download_webpage(f'https://api.screen9.com/embed/{video_id}', video_id)
+        config = self._search_json(r'var\s+config\s*=', webpage, 'config', video_id)
+        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
+            traverse_obj(config, ('src', lambda _, v: v['type'] == 'application/x-mpegURL', 'src'), get_all=False),
+            video_id, ext='mp4')
+        formats.append({
+            'url': traverse_obj(config, ('src', lambda _, v: v['type'] == 'video/mp4', 'src'), get_all=False),
+            'format': 'mp4',
+        })
+
+        self._sort_formats(formats)
+        return {
+            'id': video_id,
+            'title': traverse_obj(
+                config,
+                ('plugins', (('title', 'title'), ('googleAnalytics', 'title'), ('share', 'mediaTitle'))),
+                get_all=False),
+            'description': traverse_obj(config, ('plugins', 'title', 'description')),
+            'thumbnail': traverse_obj(config, ('poster')),
+            'formats': formats,
+            'subtitles': subtitles,
+        }
diff --git a/yt_dlp/extractor/viu.py b/yt_dlp/extractor/viu.py
index 63b6fd3a1..d27091c94 100644
--- a/yt_dlp/extractor/viu.py
+++ b/yt_dlp/extractor/viu.py
@@ -164,12 +164,17 @@ class ViuOTTIE(InfoExtractor):
         },
         'skip': 'Geo-restricted to Singapore',
     }, {
-        'url': 'http://www.viu.com/ott/hk/zh-hk/vod/7123/%E5%A4%A7%E4%BA%BA%E5%A5%B3%E5%AD%90',
+        'url': 'https://www.viu.com/ott/hk/zh-hk/vod/430078/%E7%AC%AC%E5%85%AD%E6%84%9F-3',
         'info_dict': {
-            'id': '7123',
+            'id': '430078',
             'ext': 'mp4',
-            'title': '這就是我的生活之道',
-            'description': 'md5:4eb0d8b08cf04fcdc6bbbeb16043434f',
+            'title': '大韓民國的1%',
+            'description': 'md5:74d6db47ddd9ddb9c89a05739103ccdb',
+            'episode_number': 1,
+            'duration': 6614,
+            'episode': '大韓民國的1%',
+            'series': '第六感 3',
+            'thumbnail': 'https://d2anahhhmp1ffz.cloudfront.net/1313295781/d2b14f48d008ef2f3a9200c98d8e9b63967b9cc2',
         },
         'params': {
             'skip_download': 'm3u8 download',
@@ -177,11 +182,12 @@ class ViuOTTIE(InfoExtractor):
         },
         'skip': 'Geo-restricted to Hong Kong',
     }, {
-        'url': 'https://www.viu.com/ott/hk/zh-hk/vod/68776/%E6%99%82%E5%B0%9A%E5%AA%BD%E5%92%AA',
-        'playlist_count': 12,
+        'url': 'https://www.viu.com/ott/hk/zh-hk/vod/444666/%E6%88%91%E7%9A%84%E5%AE%A4%E5%8F%8B%E6%98%AF%E4%B9%9D%E5%B0%BE%E7%8B%90',
+        'playlist_count': 16,
         'info_dict': {
-            'id': '3916',
-            'title': '時尚媽咪',
+            'id': '23807',
+            'title': '我的室友是九尾狐',
+            'description': 'md5:b42c95f2b4a316cdd6ae14ca695f33b9',
         },
         'params': {
             'skip_download': 'm3u8 download',
@@ -363,13 +369,19 @@ def download_playback():
 
         subtitles = {}
         for sub in video_data.get('subtitle') or []:
-            sub_url = sub.get('url')
-            if not sub_url:
-                continue
-            subtitles.setdefault(sub.get('name'), []).append({
-                'url': sub_url,
-                'ext': 'srt',
-            })
+            lang = sub.get('name') or 'und'
+            if sub.get('url'):
+                subtitles.setdefault(lang, []).append({
+                    'url': sub['url'],
+                    'ext': 'srt',
+                    'name': f'Spoken text for {lang}',
+                })
+            if sub.get('second_subtitle_url'):
+                subtitles.setdefault(f'{lang}_ost', []).append({
+                    'url': sub['second_subtitle_url'],
+                    'ext': 'srt',
+                    'name': f'On-screen text for {lang}',
+                })
 
         title = strip_or_none(video_data.get('synopsis'))
         return {
diff --git a/yt_dlp/extractor/vk.py b/yt_dlp/extractor/vk.py
index 69f518b69..0c856e2b0 100644
--- a/yt_dlp/extractor/vk.py
+++ b/yt_dlp/extractor/vk.py
@@ -536,7 +536,7 @@ def _real_extract(self, url):
 class VKUserVideosIE(VKBaseIE):
     IE_NAME = 'vk:uservideos'
     IE_DESC = "VK - User's Videos"
-    _VALID_URL = r'https?://(?:(?:m|new)\.)?vk\.com/video/@(?P<id>[^?$#/&]+)(?!\?.*\bz=video)(?:[/?#&](?:.*?\bsection=(?P<section>\w+))?|$)'
+    _VALID_URL = r'https?://(?:(?:m|new)\.)?vk\.com/video/(?:playlist/)?(?P<id>[^?$#/&]+)(?!\?.*\bz=video)(?:[/?#&](?:.*?\bsection=(?P<section>\w+))?|$)'
     _TEMPLATE_URL = 'https://vk.com/videos'
     _TESTS = [{
         'url': 'https://vk.com/video/@mobidevices',
@@ -550,6 +550,13 @@ class VKUserVideosIE(VKBaseIE):
             'id': '-17892518_uploaded',
         },
         'playlist_mincount': 182,
+    }, {
+        'url': 'https://vk.com/video/playlist/-174476437_2',
+        'info_dict': {
+            'id': '-174476437_2',
+            'title': 'Анонсы'
+        },
+        'playlist_mincount': 108,
     }]
     _VIDEO = collections.namedtuple('Video', ['owner_id', 'id'])
 
@@ -584,11 +591,19 @@ def _entries(self, page_id, section):
     def _real_extract(self, url):
         u_id, section = self._match_valid_url(url).groups()
         webpage = self._download_webpage(url, u_id)
-        page_id = self._search_regex(r'data-owner-id\s?=\s?"([^"]+)"', webpage, 'page_id')
+
+        if u_id.startswith('@'):
+            page_id = self._search_regex(r'data-owner-id\s?=\s?"([^"]+)"', webpage, 'page_id')
+        elif '_' in u_id:
+            page_id, section = u_id.split('_', 1)
+        else:
+            raise ExtractorError('Invalid URL', expected=True)
+
         if not section:
             section = 'all'
 
-        return self.playlist_result(self._entries(page_id, section), '%s_%s' % (page_id, section))
+        playlist_title = clean_html(get_element_by_class('VideoInfoPanel__title', webpage))
+        return self.playlist_result(self._entries(page_id, section), '%s_%s' % (page_id, section), playlist_title)
 
 
 class VKWallPostIE(VKBaseIE):
diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py
index d0be7f19e..cb14908c7 100644
--- a/yt_dlp/utils.py
+++ b/yt_dlp/utils.py
@@ -5294,7 +5294,7 @@ def load_plugins(name, suffix, namespace):
 
 
 def traverse_obj(
-        obj, *paths, default=None, expected_type=None, get_all=True,
+        obj, *paths, default=NO_DEFAULT, expected_type=None, get_all=True,
         casesense=True, is_user_input=False, traverse_string=False):
     """
     Safely traverse nested `dict`s and `Sequence`s
@@ -5304,13 +5304,15 @@ def traverse_obj(
     "value"
 
     Each of the provided `paths` is tested and the first producing a valid result will be returned.
+    The next path will also be tested if the path branched but no results could be found.
+    Supported values for traversal are `Mapping`, `Sequence` and `re.Match`.
     A value of None is treated as the absence of a value.
 
     The paths will be wrapped in `variadic`, so that `'key'` is conveniently the same as `('key', )`.
 
     The keys in the path can be one of:
         - `None`:           Return the current object.
-        - `str`/`int`:      Return `obj[key]`.
+        - `str`/`int`:      Return `obj[key]`. For `re.Match, return `obj.group(key)`.
         - `slice`:          Branch out and return all values in `obj[key]`.
         - `Ellipsis`:       Branch out and return a list of all values.
         - `tuple`/`list`:   Branch out and return a list of all matching values.
@@ -5321,7 +5323,7 @@ def traverse_obj(
         - `dict`            Transform the current object and return a matching dict.
                             Read as: `{key: traverse_obj(obj, path) for key, path in dct.items()}`.
 
-        `tuple`, `list`, and `dict` all support nested paths and branches
+        `tuple`, `list`, and `dict` all support nested paths and branches.
 
     @params paths           Paths which to traverse by.
     @param default          Value to return if the paths do not match.
@@ -5342,6 +5344,7 @@ def traverse_obj(
     @returns                The result of the object traversal.
                             If successful, `get_all=True`, and the path branches at least once,
                             then a list of results is returned instead.
+                            A list is always returned if the last path branches and no `default` is given.
     """
     is_sequence = lambda x: isinstance(x, collections.abc.Sequence) and not isinstance(x, (str, bytes))
     casefold = lambda k: k.casefold() if isinstance(k, str) else k
@@ -5368,6 +5371,8 @@ def apply_key(key, obj):
                 yield from obj.values()
             elif is_sequence(obj):
                 yield from obj
+            elif isinstance(obj, re.Match):
+                yield from obj.groups()
             elif traverse_string:
                 yield from str(obj)
 
@@ -5376,6 +5381,8 @@ def apply_key(key, obj):
                 iter_obj = enumerate(obj)
             elif isinstance(obj, collections.abc.Mapping):
                 iter_obj = obj.items()
+            elif isinstance(obj, re.Match):
+                iter_obj = enumerate((obj.group(), *obj.groups()))
             elif traverse_string:
                 iter_obj = enumerate(str(obj))
             else:
@@ -5385,12 +5392,23 @@ def apply_key(key, obj):
         elif isinstance(key, dict):
             iter_obj = ((k, _traverse_obj(obj, v)) for k, v in key.items())
             yield {k: v if v is not None else default for k, v in iter_obj
-                   if v is not None or default is not None}
+                   if v is not None or default is not NO_DEFAULT}
 
-        elif isinstance(obj, dict):
+        elif isinstance(obj, collections.abc.Mapping):
             yield (obj.get(key) if casesense or (key in obj)
                    else next((v for k, v in obj.items() if casefold(k) == key), None))
 
+        elif isinstance(obj, re.Match):
+            if isinstance(key, int) or casesense:
+                with contextlib.suppress(IndexError):
+                    yield obj.group(key)
+                    return
+
+            if not isinstance(key, str):
+                return
+
+            yield next((v for k, v in obj.groupdict().items() if casefold(k) == key), None)
+
         else:
             if is_user_input:
                 key = (int_or_none(key) if ':' not in key
@@ -5426,18 +5444,22 @@ def apply_path(start_obj, path):
 
         return has_branched, objs
 
-    def _traverse_obj(obj, path):
+    def _traverse_obj(obj, path, use_list=True):
         has_branched, results = apply_path(obj, path)
         results = LazyList(x for x in map(type_test, results) if x is not None)
-        if results:
-            return results.exhaust() if get_all and has_branched else results[0]
 
-    for path in paths:
-        result = _traverse_obj(obj, path)
+        if get_all and has_branched:
+            return results.exhaust() if results or use_list else None
+
+        return results[0] if results else None
+
+    for index, path in enumerate(paths, 1):
+        use_list = default is NO_DEFAULT and index == len(paths)
+        result = _traverse_obj(obj, path, use_list)
         if result is not None:
             return result
 
-    return default
+    return None if default is NO_DEFAULT else default
 
 
 def traverse_dict(dictn, keys, casesense=True):


Test patch:
diff --git a/test/test_utils.py b/test/test_utils.py
index 69313564a..90085a9c0 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -2,6 +2,7 @@
 
 # Allow direct execution
 import os
+import re
 import sys
 import unittest
 
@@ -1890,6 +1891,7 @@ def test_traverse_obj(self):
                 {'index': 2},
                 {'index': 3},
             ),
+            'dict': {},
         }
 
         # Test base functionality
@@ -1926,11 +1928,15 @@ def test_traverse_obj(self):
 
         # Test alternative paths
         self.assertEqual(traverse_obj(_TEST_DATA, 'fail', 'str'), 'str',
-                         msg='multiple `path_list` should be treated as alternative paths')
+                         msg='multiple `paths` should be treated as alternative paths')
         self.assertEqual(traverse_obj(_TEST_DATA, 'str', 100), 'str',
                          msg='alternatives should exit early')
         self.assertEqual(traverse_obj(_TEST_DATA, 'fail', 'fail'), None,
                          msg='alternatives should return `default` if exhausted')
+        self.assertEqual(traverse_obj(_TEST_DATA, (..., 'fail'), 100), 100,
+                         msg='alternatives should track their own branching return')
+        self.assertEqual(traverse_obj(_TEST_DATA, ('dict', ...), ('data', ...)), list(_TEST_DATA['data']),
+                         msg='alternatives on empty objects should search further')
 
         # Test branch and path nesting
         self.assertEqual(traverse_obj(_TEST_DATA, ('urls', (3, 0), 'url')), ['https://www.example.com/0'],
@@ -1963,8 +1969,16 @@ def test_traverse_obj(self):
         self.assertEqual(traverse_obj(_TEST_DATA, {0: ('urls', ((1, ('fail', 'url')), (0, 'url')))}),
                          {0: ['https://www.example.com/1', 'https://www.example.com/0']},
                          msg='tripple nesting in dict path should be treated as branches')
-        self.assertEqual(traverse_obj({}, {0: 1}, default=...), {0: ...},
-                         msg='do not remove `None` values when dict key')
+        self.assertEqual(traverse_obj(_TEST_DATA, {0: 'fail'}), {},
+                         msg='remove `None` values when dict key')
+        self.assertEqual(traverse_obj(_TEST_DATA, {0: 'fail'}, default=...), {0: ...},
+                         msg='do not remove `None` values if `default`')
+        self.assertEqual(traverse_obj(_TEST_DATA, {0: 'dict'}), {0: {}},
+                         msg='do not remove empty values when dict key')
+        self.assertEqual(traverse_obj(_TEST_DATA, {0: 'dict'}, default=...), {0: {}},
+                         msg='do not remove empty values when dict key and a default')
+        self.assertEqual(traverse_obj(_TEST_DATA, {0: ('dict', ...)}), {0: []},
+                         msg='if branch in dict key not successful, return `[]`')
 
         # Testing default parameter behavior
         _DEFAULT_DATA = {'None': None, 'int': 0, 'list': []}
@@ -1981,7 +1995,13 @@ def test_traverse_obj(self):
         self.assertEqual(traverse_obj(_DEFAULT_DATA, ('list', 10)), None,
                          msg='`IndexError` should result in `default`')
         self.assertEqual(traverse_obj(_DEFAULT_DATA, (..., 'fail'), default=1), 1,
-                         msg='if branched but not successfull return `default`, not `[]`')
+                         msg='if branched but not successful return `default` if defined, not `[]`')
+        self.assertEqual(traverse_obj(_DEFAULT_DATA, (..., 'fail'), default=None), None,
+                         msg='if branched but not successful return `default` even if `default` is `None`')
+        self.assertEqual(traverse_obj(_DEFAULT_DATA, (..., 'fail')), [],
+                         msg='if branched but not successful return `[]`, not `default`')
+        self.assertEqual(traverse_obj(_DEFAULT_DATA, ('list', ...)), [],
+                         msg='if branched but object is empty return `[]`, not `default`')
 
         # Testing expected_type behavior
         _EXPECTED_TYPE_DATA = {'str': 'str', 'int': 0}
@@ -2061,6 +2081,25 @@ def test_traverse_obj(self):
         with self.assertRaises(TypeError, msg='too many params should result in error'):
             traverse_obj(_IS_USER_INPUT_DATA, ('range8', ':::'), is_user_input=True)
 
+        # Test re.Match as input obj
+        mobj = re.fullmatch(r'0(12)(?P<group>3)(4)?', '0123')
+        self.assertEqual(traverse_obj(mobj, ...), [x for x in mobj.groups() if x is not None],
+                         msg='`...` on a `re.Match` should give its `groups()`')
+        self.assertEqual(traverse_obj(mobj, lambda k, _: k in (0, 2)), ['0123', '3'],
+                         msg='function on a `re.Match` should give groupno, value starting at 0')
+        self.assertEqual(traverse_obj(mobj, 'group'), '3',
+                         msg='str key on a `re.Match` should give group with that name')
+        self.assertEqual(traverse_obj(mobj, 2), '3',
+                         msg='int key on a `re.Match` should give group with that name')
+        self.assertEqual(traverse_obj(mobj, 'gRoUp', casesense=False), '3',
+                         msg='str key on a `re.Match` should respect casesense')
+        self.assertEqual(traverse_obj(mobj, 'fail'), None,
+                         msg='failing str key on a `re.Match` should return `default`')
+        self.assertEqual(traverse_obj(mobj, 'gRoUpS', casesense=False), None,
+                         msg='failing str key on a `re.Match` should return `default`')
+        self.assertEqual(traverse_obj(mobj, 8), None,
+                         msg='failing int key on a `re.Match` should return `default`')
+
 
 if __name__ == '__main__':
     unittest.main()