odensc / ttv-ublock

Blocking ads on that certain streaming website
MIT License
895 stars 61 forks source link

We broke again boys #11

Closed Jta26 closed 4 years ago

Jta26 commented 4 years ago

I'd love to contribute to keeping this updated, cause these ads are killing me.

1.1.1 seems to be not working, McDonald's is telling me to not use the ketchup in my fridge again.

Edit from maintainer:

https://github.com/odensc/ttv-ublock/issues/11#issuecomment-731700033

Bucky420 commented 4 years ago

yes it is fully broken

luffydev commented 4 years ago

having ads again since 20 minutes

FutureHits3Dev commented 4 years ago

same here... Started getting ads about 10 minutes ago. I can't stream surf.

julesclear commented 4 years ago

Same here :)

Jta26 commented 4 years ago

Question for @odensc, does this just spoof being an adsense crawler bot by setting the useragent header to Mediapartners-Google? Specifically setting it on the POST to the video-edge ttvnw.net link?

odensc commented 4 years ago

Yes it sets the user-agent on any request to ttvnw.net. I've tried a few other UAs that worked before, but they don't seem to anymore. They may have patched the whole user-agent bypass route, but I'm looking into it more when I have the time

Jta26 commented 4 years ago

Yes it sets the user-agent on any request to ttvnw.net. I've tried a few other UAs that worked before, but they don't seem to anymore. They may have patched the whole user-agent bypass route, but I'm looking into it more when I have the time

Yeah, they probably implemented something like this https://developers.google.com/search/docs/advanced/verifying-googlebot

Edit: they could have also just said screw it and are showing ads to web crawlers, considering that every web crawler user-agent is broken.

revunix commented 4 years ago

This works for me: LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)

Jta26 commented 4 years ago

This works for me: LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)

image

It no work :(

odensc commented 4 years ago

I've seen people post "working" user agents but they don't work for me. I think their account is just in a period where they have no prerolls because they've already watched one recently. You have to test it in incognito mode.

nol166 commented 4 years ago

Yes it sets the user-agent on any request to ttvnw.net. I've tried a few other UAs that worked before, but they don't seem to anymore. They may have patched the whole user-agent bypass route, but I'm looking into it more when I have the time

(compatible; Mediapartners-Google/2.1; +http://www.google.com/bot.html)

^ This mobile version of Mediapartners user-agent works for me, anyone else test?

Seems to work so far

Update Doesn't work in incognito

Jta26 commented 4 years ago

I've seen people post "working" user agents but they don't work for me. I think their account is just in a period where they have no prerolls because they've already watched one recently. You have to test it in incognito mode.

I think this is the case too. Neither the linkedin bot or the mobile google bot are working for me. I'm not letting the ads finish playing, so it gives me another one.

revunix commented 4 years ago

@Jta26 i also have uBlock active. do you?

@codydbgt checked it in incognito mode. LinkedInBot works for me.

PS: reload the plugin

Jta26 commented 4 years ago

@Jta26 i also have uBlock active. do you?

@codydbgt checked it in incognito mode. LinkedInBot works for me.

Yeah, ublock is active, and I'm looking at the request headers and it's in there.

PSSGCSim commented 4 years ago

They could be rolling out the patch gradually

odensc commented 4 years ago

@PSSGCSim Yes this is a possibility too. could be a staged rollout. Previously when people were reporting it as broken last time, it was still working for me until about an hour later.

revunix commented 4 years ago

Fun fact, i use NextDNS on my iPhone with AdBlock Filter and i never get ads with the official Twitch App ... but what user-agent is it in there app?!

odensc commented 4 years ago

@revunix Mobile doesn't seem to have ads as often/at all.

Jta26 commented 4 years ago

@revunix Mobile doesn't seem to have ads as often/at all.

Same thing when running on something like a Chromecast.

luffydev commented 4 years ago

@revunix Mobile doesn't seem to have ads as often/at all.

I use Twitch app on my Samsung and i've ads

luffydev commented 4 years ago

LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com) seem to work for me after extension and twitch reloading

pijcab commented 4 years ago

God dang it, so we ARE gg for now huh...

rmanky commented 4 years ago

Temporary (and very janky) solution that replaces the video with an embed version (that doesn't get prerolls or midrolls?):

Open your Inspector, go to the Console, run the following:

let player = document.querySelector('.video-player')

const [url] = window.location.href.split('/').slice(-1)
player.outerHTML = "<iframe class='video-player' src='https://player.twitch.tv/?channel=" + url + "&parent=www.twitch.tv' data-a-target='video-player' data-a-player-type='site' data-test-selector='video-player__video-layout'></iframe>"
Jta26 commented 4 years ago

Temporary (and very janky) solution that replaces the video with an embed version (that doesn't get prerolls):

Open your Inspector, got to the Console, run the following:

let player = document.querySelector('.video-player')

const [url] = window.location.href.split('/').slice(-1)
player.outerHTML = "<iframe class='video-player' src='https://player.twitch.tv/?channel=" + url + "&parent=www.twitch.tv' data-a-target='video-player' data-a-player-type='site' data-test-selector='video-player__video-layout'></iframe>"
  • Theatre mode doesn't work
  • Switching channels doesn't work (need to refresh the page, and run the script again
  • Midrolls might still come through, haven't watched long enough

This I think is a good temporary solution

edit: We can probably fix some of the issues you've lined up. Like listening for a change in the url bar to reload the iframe with the new stream.

luffydev commented 4 years ago

This works for me: LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)

Yes for the moment modifying header in the extension settings and reloading extension and Twitch working for the moment

revunix commented 4 years ago

This works for me: LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)

meh .. i got ads again.

tested now Googlebot/2.1 (+http://www.google.com/bot.html) and this "works"

@revunix Mobile doesn't seem to have ads as often/at all.

@odensc do you have a good user-agent to test?

justsayingbro commented 4 years ago

I just got out of my cave and discovered theres no ads on the HLS version of twitch (https://twitchls.com), so if you are looking for a good blocker since a week like me, turns out we dont even need one :') GL guys and keep up the good work bc im pretty sure it won't last long before we get ads here too

Jta26 commented 4 years ago

I just got out of my cave and discovered theres no ads on the HLS version of twitch (https://twitchls.com), so if you are looking for a good blocker since a week like me, turns out we dont even need one :') GL guys and keep up the good work bc im pretty sure it won't last long before we get ads here too

I don't like this one cause I like to switch streams constantly, and the twitch UI is good for that.

edit: incognito w/ ublock and the extension has started working with the linkedin user-agent.

rmanky commented 4 years ago

@Jta26

edit: We can probably fix some of the issues you've lined up. Like listening for a change in the url bar to reload the iframe with the new stream.

I tried some initial stuff, couldn't get anything working. Had a weird situation where the audio would play, even though the player was gone from its div. Probably didn't try hard enough, though.

frshhh commented 4 years ago

The linkedin user agent seems to work for me. Using it in incognito, I've been clicking around multiple streams for the past 5-10 minutes and haven't seen an ad (yet), definitely no pre-rolls.

Make sure you set TTV ad-block's "Run in Private Windows" option to allow (it's set to don't allow by default)

edit: was just watching a stream where the streamer announced he was doing an ad break... no ads for me.

nol166 commented 4 years ago

I've seen people post "working" user agents but they don't work for me. I think their account is just in a period where they have no prerolls because they've already watched one recently. You have to test it in incognito mode.

Testing the LinkedIn header with an incognito (with extension enabled) doesn't seem to work even after reloading the extension.

Edit: Firefox user

dlfsjdfjodifjsodifjsdo commented 4 years ago

Twitch devs are here, reading all comments, code is open source, and will easily fix it next time too. Prove me wrong

sapphire-bt commented 4 years ago

This comment in another topic mentioned intercepting the GET requests to the M3U8 manifests rather than playing cat and mouse with the user agent string. I've never heard of Streamlink but it looks promising - does anyone know how long it's been around and how long their bypass has been working?

Jta26 commented 4 years ago

Twitch devs are here, reading all comments, code is open source, and will easily fix it next time too. Prove me wrong

This is a given. Adblocking is like the captcha problem with extra steps. Devs fix, then someone finds the workaround, repeat.

odensc commented 4 years ago

@sapphire-bt Their approach just removes the ad segments, which causes a black screen during ads AFAIK, doesn't actually skip them.

@Jta26 Exactly, the way I see it, it's much more beneficial to release it to a wide audience because nothing's gonna keep Twitch from getting the code if they wanted - it'll just hinder actual users. If I cared I'd have just kept it to myself from the start.

svengeance commented 4 years ago

I find it slightly hilarious that some pissant manager is authorizing several thousands of dollars of employee time and resources to force what is now an extremely minor percent of twitch viewers to watch ads. There's no way it's a profitable use of time, and we will eventually win out unless they find the magic bullet of ads.

nicolas-martin commented 4 years ago

@sapphire-bt @rmanky @odensc

This comment in another topic mentioned intercepting the GET requests to the M3U8 manifests rather than playing cat and mouse with the user agent string. I've never heard of Streamlink but it looks promising - does anyone know how long it's been around and how long their bypass has been working?

I actually managed to reliably modify the m3u8 playlist. I think the next step is creating an addon that does this

let player = document.querySelector('.video-player')

const [url] = window.location.href.split('/').slice(-1)
player.outerHTML = "<iframe class='video-player' src='https://player.twitch.tv/?channel=" + url + "&parent=www.twitch.tv' data-a-target='video-player' data-a-player-type='site' data-test-selector='video-player__video-layout'></iframe>"

but uses the .m3u8 as a source which should be doable. Twitch devs are in here, we should take this conversation elsewhere.

svengeance commented 4 years ago

If you setup a discord server somewhere I'd like to help the war effort. I was going to investigate twitchls's source code and see if there's anything there that gives a hint on what we could do.

On Wed, Nov 18, 2020, 7:33 PM Nicolas Martin notifications@github.com wrote:

This comment https://github.com/odensc/ttv-ublock/issues/8#issuecomment-728692054 in another topic mentioned intercepting the GET requests to the M3U8 manifests rather than playing cat and mouse with the user agent string. I've never heard of Streamlink but it looks promising - does anyone know how long it's been around and how long their bypass has been working?

I actually managed to reliably modify the m3u8 playlist. I think the next step is creating an addon that does this

let player = document.querySelector('.video-player') const [url] = window.location.href.split('/').slice(-1)player.outerHTML = ""

but uses the .m3u8 as a source which should be doable. Twitch devs are in here, we should take this conversation elsewhere.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/odensc/ttv-ublock/issues/11#issuecomment-730045338, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABUHXFMPTKY77CMR3NT56O3SQRRW3ANCNFSM4T2RWGZA .

sapphire-bt commented 4 years ago

@sapphire-bt @rmanky @odensc

This comment in another topic mentioned intercepting the GET requests to the M3U8 manifests rather than playing cat and mouse with the user agent string. I've never heard of Streamlink but it looks promising - does anyone know how long it's been around and how long their bypass has been working?

I actually managed to reliably modify the m3u8 playlist. I think the next step is creating an addon that does this

let player = document.querySelector('.video-player')

const [url] = window.location.href.split('/').slice(-1)
player.outerHTML = "<iframe class='video-player' src='https://player.twitch.tv/?channel=" + url + "&parent=www.twitch.tv' data-a-target='video-player' data-a-player-type='site' data-test-selector='video-player__video-layout'></iframe>"

but uses the .m3u8 as a source which should be doable. Twitch devs are in here, we should take this conversation elsewhere.

Nice one - so is that effectively refreshing the iframe rather than "blocking" ads?

I don't think there's any point in moving the conversation elsewhere. If it's to be released as an add-on then the source code will be visible plain as day to anyone who wants to see it.

nicolas-martin commented 4 years ago

There's no docs for the usher.ttvnw.net api I'm guessing it's not meant to be used publicly. That's why I was hesitant in sharing it, but you're right. If it's an extension it would have been visible anyway.

USER="$1"
OAUTH="$2"

RESP="`curl "https://api.twitch.tv/api/channels/$USER/access_token?adblock=true&need_https=true&platform=web&player_type=site&oauth_token" -H "Client-ID: $OAUTH" --compressed`"
TOKEN="`echo $RESP | jq '.token' | sed 's/\\\\"/"/g' | sed 's/\(^.\)//g' | sed 's/\(.$\)//g'`"
SIG="`echo $RESP | jq '.sig' | sed 's/\(^.\)//g' | sed 's/\(.$\)//g'`"

curl http://usher.ttvnw.net/api/channel/hls/$USER.m3u8\?allow_source\=true\&allow_audio_only\=true\&supported_codecs\=avc1\&sig\=$SIG\&token\=%7B%22adblock%22%3Afalse%2C%22authorization%22%3A%7B%22forbidden%22%3Afalse%2C%22reason%22%3A%22%22%7D%2C%22blackout_enabled%22%3Afalse%2C%22channel%22%3A%22jaeyun%22%2C%22channel_id%22%3A164378078%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%2C%22view_until%22%3A1924905600%7D%2C%22ci_gb%22%3Afalse%2C%22geoblock_reason%22%3A%22%22%2C%22device_id%22%3A%22a615b93a6c8acb58%22%2C%22expires%22%3A1605748569%2C%22extended_history_allowed%22%3Afalse%2C%22game%22%3A%22%22%2C%22hide_ads%22%3Afalse%2C%22https_required%22%3Afalse%2C%22mature%22%3Afalse%2C%22partner%22%3Afalse%2C%22platform%22%3A%22android%22%2C%22player_type%22%3A%22mobile_player%22%2C%22private%22%3A%7B%22allowed_to_view%22%3Atrue%7D%2C%22privileged%22%3Afalse%2C%22role%22%3A%22%22%2C%22server_ads%22%3Afalse%2C%22show_ads%22%3Atrue%2C%22subscriber%22%3Afalse%2C%22turbo%22%3Afalse%2C%22user_id%22%3A21251497%2C%22user_ip%22%3A%2299.99.99.99%22%2C%22version%22%3A2%7D
Response without ad tags ``` #EXTM3U #EXT-X-TWITCH-INFO:NODE="video-edge-c2a19c.yto01",MANIFEST-NODE-TYPE="weaver_cluster",MANIFEST-NODE="video-weaver.yto01",SUPPRESS="false",SERVER-TIME="1605744376.18",TRANSCODESTACK="2017TranscodeX264_V2",USER-IP="99.99.999.999",SERVING-ID="9051fbeb13da4d87aeb5789dc80ecba5",CLUSTER="yto01",ABS="false",VIDEO-SESSION-ID="1835370236164458871",BROADCAST-ID="40550636094",STREAM-TIME="25234.178816",B="false",USER-COUNTRY="US",MANIFEST-CLUSTER="yto01",ORIGIN="sjc02",C="aHR0cHM6Ly92aWRlby1lZGdlLTU2NmM1ZS5wZHgwMS5hYnMuaGxzLnR0dm53Lm5ldC92MS9zZWdtZW50L0N0M1Vvek5CX0NHRUJnT3kzc2FhUlh2X0dwbXF2TGlqUzBldlhTNFpRaUFtY1diV3lSNzBXSzdja1VjVUFvbGhVRHpRbEhGckozYTZ6STM0MEltWVIzQWVfWXZFTmxXWVNjV1djRXZ0NElicGdGLVJtOUN2QmZyM2w1ZHBlUjdTZ25Vc1JNTDZteWd2djZEbnpSR19lUUI4WjVpMmt4QnpIMFZ1MWQzR2RxdVR3NlVIRkMyREdoRm5oUW0zWGg3dkVWcWdnYXJOWEVzbUY5OHR4RjZoZkFiMVdBVVNZUHRFd0h4blRFQUQzNGNUNjEyTTQwc0EtMHBTRnZMbWFLSHh5b3JrajlpMVVIQ05ZRlNUR0xyMFZFRmw0MHNIbC1tQW1TWXlRdDdvTHUxbjZLT2FJaEQteEVRQ255dWMzcWQ5azU2XzktUnZUSEpzblFqcW5WVFpodTFpU1ZBM0NsSTgzQlNUakxZNGo3UDU3TGxjSnJ5OGRyUUJYNVhQUndSRUhpNWx0SllyVll3bjhmLWJVWDlnemtjRFYwNEN4RlBTU01WMHJlZnp3WXNXWHNMaEpjUmlfTU5Qck5QcDdXRDQ3cm9qVl91TzU4Y0VxeU1JSXVxMjVOUHpzRk5jQVBUeE5ndW5PT3pVUWx5MEJTbEZ2b3dwV3lpa3NiZEZBc2ZvUzAwM3ZGdFZUaXMyM0haR2lsU01oQWlOUjRCbFliOC13d0ZpYTlDby1aQnJDLTBZLW1hVTVnUDd3cW5adjdlbUItQ2ZTNnN5dENLMmI5MFNGblNnTFVfSjc0OEZsd0VwVnVLem1TRXJTWDIyV0tpM19iZlFEaFFBN1AwMHpycktIYzBzTVQtUFZVc2ZDelptMlcyVk01ZVNoMGRrZFNFRVJSRE9la1lUanE4OVBGMFVZa0hsd3N6cTRZNTRiVktCN0pmVy1QQ2FwdVBTUXdlR18wZDdFUFZHVWVkb05oN0VqOGNqRHpsN3VEbVREU1ExTnRFdktodndFdFcwbjhJekxFLnRz",D="false" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="chunked",NAME="1080p60 (source)",AUTOSELECT=YES,DEFAULT=YES #EXT-X-STREAM-INF:BANDWIDTH=8997365,RESOLUTION=1920x1080,CODECS="avc1.64002A,mp4a.40.2",VIDEO="chunked" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsUEORbuChUVYnerpAYgmwCMgztkZm5NQTzJ5dPBfsnx091LbcCgjaQiNtTwtrwASrUfxM-RPopuk7m1nRgidlZ4pcMg8xSRNwxecOHyYWBqx2GG7GJcjDuwIwNIQi24rG2ratfNy56GisKnaPtg5rEV5FF00eEnO1FmhARIUSza_Ec29qFWJxUUIUzH16eKEBZEQkIZh_iC5iJC6wPYCrvWvIX2_M3kzeFg45hbcMDxqN_Wb3B0L7uujkWvXfaGmflbHv5Oeko564sylyKQiu12be5hXptapVtEBiEbPXiA_vWRQ0E8O6j9xEloBKEjl2M1M6DKTOxYJq96pRQF03cPDlWLn1tKlDzWgx6U-RsFWhHdIY8ahkVpJj0vutruCpZ4V-wSwKwbFR4aD9FxwwUX-p1T4Ych55Ov0a37cWCWFexIbYnTdNgzLsmqJrOwPJUiI11Wu5Ko9rVpavIqFfBWHHwVaFKSQ038SvEyFX_IR760TPXi_GJWIEd58Jgl1-kdgjxsQduKK9fpelCm4IJZLgHVkMyoPPZv0bP5s0n2b_43pGFItTZL3c_v_XyFvWEBgGiaXKmFaWuggRoQSpL7D7iILAPM0r1gX-kIS2I1oAGJRw2Pw6CHSZV04MkQxyws-fk3Jj0StQWamqOnVedcGaMyOpWTbFaTOi-C0LaODNK9Qp_2DCUkYSFLqS_h_NPXEdVzq3LFe6-Q27FY_bnE7xs7hajVMsnUr_gbvh-sEBCP-zQFKfwtYUf9EGWUS1GOYhnB1y4SECnq-Yx3CTJFLjZwyinRqd4aDDSeEm5MnL--dtkKgQ.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="720p60",NAME="720p60",AUTOSELECT=YES,DEFAULT=YES #EXT-X-STREAM-INF:BANDWIDTH=3417779,RESOLUTION=1280x720,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="720p60" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsME7vvWC5ESEHKOMt3lqkBdZj859SjUS4dxDGjnaOTbvIp9_eZpVBTYBRwnAzfdQvW02Dp-CqvZR0ff99oKqQZdvr53QuTgkbbCDIkooGm4yOKtsitZCPUbPHgDFU4wM8OWvhou7KrExA1Ya-317Rb3uJf3FZfr_lPzeorymxl-LX8GWlZCwgAuVZcBSxQubK_mwoXtT6G9eob_yV27pRz5LpFKQtsoftaiuO4U6h7JfY7GUFRT7wn5htn8QXLANgam_p-SOkynOpPAwgOxCRUdYhs-QaT_K6kCNXmWK6-mZqAPeL0KhLtphAHcpK-o3dZ9XT0xZ9j6ZxbstWluC61TG7MbXTdlOVC5KV4Qy98aW7YDnd0x5Er9qC1nLLSXl4fPrBa4qGXTis6A1TkGDxqsm7xJSjEW1BBqOOVXX0NSa7ptOP5qeGoknl5V5FOPy7okk1H-eD1Ge2gnOEDe9FzuaygzI5g3-jG9xuaIzHKzEE_2N53EeN0OqYqLoU4Eh6hZ7duMT46oL6R8PVU4NP98pnXqKBZf65fBhWMJA-dEwmNOtzXIegjq9ZJ5uWflKGO2LLSfVv5oNnYp4zLgD8cq2slwSPct-Vdrg5sKoAdfXY8Oiz5AzIy5oEaN_3C0X8tgHIkTtGkmx7OFaz6n5oLLfWXmdOjD7JuK2aAb1Nu2auWUAWHcV6Mtemu8pJOd58No2oWMJGDGHzKf03EjCFkAPFHB473iMfd-ivTmCgP0tZi7fUSI5ySieYhFLDfj2UfJifICEhDo7szB6iH4_W8BTMvfd-obGgwSBC2u6wPQ99YsNyk.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="720p30",NAME="720p",AUTOSELECT=YES,DEFAULT=YES #EXT-X-STREAM-INF:BANDWIDTH=2367779,RESOLUTION=1280x720,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="720p30" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsME9hCWVT2yncpRva4fI3yTAiM5hZGxFW9cLZVE3K-3FRf_HQBWOdrPz1pvJ31JnmEHYoZw3wBpk_LhdOKKftF7wzFS-OUs-FIGTo6qgH0HX6GR5lkhri3dAHszzCZVLwctbxQKrfJEZHMVIY-d-X_SF-gyWI65IQOWEWndm6q2xKSKDE0myv0HIq1xtWpTxHDVvK3lJLhNBgkxDv_N1TpalL4Izcbi7FibSoDIQ0LMalG7rE8VOvt-KFQnZYxNCyQZ0re2jAgbn0MKGda4f06FH6yuz2lMhR_epFz7Q1llLJ3k43mR6kehhLe20fzllOjNo1dV6icpCDPrMOqzY2Bbh8N9pvT6GhI2fvbVOvnVHWUjFuwKenYGrBXMI7PAdWKyIBlxw7S0exBklofv7gti-4f46eVIjKEyW5zqNfrcAmOVglFxBnT1flFpV9GvHajH1pkQg4szFi4_5-aH_dKhCY9z44MK0F-9XUBgkteK94FAz_uivtl6nwPN-rWdJVzVcVo84Dd7lvuG7uxRXL2dfHfYqnjKtrq-nbg9PfRRca-jtG5jzYSG3EC2klLSH1GOam32TXT-zb5u3sTbJ6dU9lBQLESvKUAr3IvfC6SG8c-hJnjydiImREFCcoJ3cPEZ2sGUPMqYXPY-FHY5D_dtXkDl07Rn8lJzDX5BYIJDFjLZTzZyrIYN7SFAl67-mPsVfvdWH-94jry20ccqxJNZV839eVRNGl6TV-d_lLZxit5SHHdsRe4XewquaSaVmrZL4dvxEhAhHhd0YELUFlubVh82atf5Ggz9lukHWeuM2GSFLk0.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="480p30",NAME="480p",AUTOSELECT=YES,DEFAULT=YES #EXT-X-STREAM-INF:BANDWIDTH=1422779,RESOLUTION=852x480,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="480p30" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsMEgjvTxyqeozuoD_pdmwB-8LOz0AuDQfjhMwy9t58IiitaipJ-Stq17hDUC1-ssV8GQLmVhi7nYxfOCY_TSesXEskoIXUG0C000AiZvnILKB1atMPlYV9r6zY3ne7WOps9aNdbH4mZuBOVTee21qRrJ1-fPNUZbckxd3ZIud__Z9sSZXH-NaWrfUOVBOSU8E9f0vbdCmenRMQy1AGGl6uP9_MdtfEsR_Fi3MwSs3lHwQ-baMWku6r9R-6tSEzuhQjkNCfvg60JqfpG6-zdWvuUAm1WT0GlyaZI9U80mEUBh19skgryxFLKaJALvEuRIk9FnWL9-6sNm65_mDsttJ07dfYxAa3YdSuRxLhYaQTczl_gAo9HWBDrWaC870iUzY16qNay3vt_SuqVXnKmwdRSXnEaQeRMFklJ63Tmlo4JIOAyL9TZBDIuSYbEu7twGNuU3lINrUaLzm3fQwSCJx_S5BO90dYdtUbHTm8CNr3Y_SJXTm4IHccxwZfsbxrDe2r-IuACLCbA79RTQ3S9ihMVGJTDVZ2U4pHi95lvXj2zs_6UEnrfU4OpmcxkSV7QkKapSnc9KC-KZqowygs56pbyTs9HcETPaB3Ww8hl1Z2-JwRs5hVuaYzBH-r5uGqpnFSc980xWK5LDNRDcx3efcN32RAPQxjK3ImNE-CHzEYjyBTpaBlofs3DT9qDpgtfAadvMApUcnOMwz1ETzmmPefwttSzV1xQAcn6Ge6aaphbw3k2YEkrGwus5KAGYMEiUysr52WMEhAUtq9BScmVQ-3sNjceiXFxGgy5XtpaJ1HropFPZWM.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="360p30",NAME="360p",AUTOSELECT=YES,DEFAULT=YES #EXT-X-STREAM-INF:BANDWIDTH=630000,RESOLUTION=640x360,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="360p30" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsME41AoA4txMyhsFEXdQSa-2QLl0KEpjzzqvou8k0VhBYfznRJyNCQL4Aqnjyi-RPoqEeFaNeq3R2QtQFZ43QW0lkytQGMcrO5o2lStuvJflm6gFDnvGK_Sh_9Z3Eu5vnlFV4SZDEUgthRQKBOfL5cphGJEYEIxtNXm_FzLhD30az6VxRYFtM6SwIwamyXNakZvIUaKZ5jDoKm2WlDc1J_APxRH3XcSqTtW-E3c5_fDqx4RToLVbava-mdPf6P9a-EkLKoudqISlrHf47w6rMuqesa4yTeeZQCQGV4ar-13d4npoZXarihWq4O-HgdzkHj_eyTBFMD-7WY8KQa5_GYqlZe3Rhr7vG-jdpRG0hVCdpBOkin4TmhC-rMHSnamp16gTa95A_COuWt1c2_jlGXpjJyIWIqaOFAcce84q20pvuEmXRfIOfawE11KGd2KAtRK_lrgdFwgDm_GL9xF-UeMZw0kAimIDcWy87jQbxNz6vjf3WrB3oVf005nuqtSIz6q5QzJaBGRBkUO85LOVjMXR7J7TYcp__RgYWUh2z2OFjkWqbH3SOfY6vGiekUWa0L_ZjHijINFq4MtK4BMDCSZzrapcoYTEInQhiWbTGsGeUyUlFYQYCSF7ekKSxXfsyPjDSHOSUxHxC77-diRd4foMHHb99DK54cU8XCZmS9HJjSLn_GdMn2oRg3aZQvMHy2lIdQRa5WF8872CxWAhsvnakVadjCff7mhZdBDI084HnaoRxnFfyqrtAyB9nYUMgnaX-3OEhBJUa5029kNjhlcDpIuyjq3GgxYfCiGHWS0zXieS18.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="160p30",NAME="160p",AUTOSELECT=YES,DEFAULT=YES #EXT-X-STREAM-INF:BANDWIDTH=230000,RESOLUTION=284x160,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="160p30" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsMEqCfQufrwiJqZ-0AEK-fYcAgURQT2alfEesSOVUBXLoZ6CBEeze-xcM2fI0BR-_RXO8DpJFX8kXVZDOzd3PXzukXEaFh1nOlWEkxnyzLgpBbDMeNaChTfDNCjMVBTi_dfM1h0TvUleCz-WgJfN4RxwoAtLLFttGscBCb0bcoatcdTNtWBORm-s3mwehB7Giht_DvMddPdHpgb6rRj6Ui-W7-LpHQbbMlvwOVfQfOACyrHXhvBVsovxcTIM9zbeRTZvGkLpTiAMgIUSfvxs_duz7I9SLoTjVRHi5i0BsBbp9BE7ipvE4XI8KeQOC3t_-QPdGbjL_F3DOy_BNJ1hqW6YutK6XqLWHhvSV1H5_J_on_Hz73yt3ijNMErPX0ZqvOANG2pi9iKe7je8I_9Y2ftFpr68DhZaMmVejoI6TOSznMCWRrbD5ddhDTKx18qUWy_nE1R650tlditMUmjWBlhuExGAH8buyuHvhjlSwIvVxachf-wZNbZlCZ7VTmSS3023SrAcA8U7XMO9hHK_50uBHbtxBZTuD_Mra1B6faHtjY0Kzshk_8xQ9sSr2d2MJqcMMWUqwtWuXldfqc4F_xMqonJpZknZKFsdpKHyptaAWbL14az1tIqx-JniGUaogq88xigrvKfNM1pdr_4ad8E9DG0vPdtaLK9CGByiyUOEYwjxM6pIJIH9R_riVlqtybDFXljd8QJmng6NvDKBP-1NUgsa4LUE1w7gxRsbdcvIrFw2KI03UZ1nxfGpmmqRoZu_8t5EhCFcI2VIithsthUNgG6YyjRGgwskGTctxbYJMKG4pE.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="audio_only",NAME="audio_only",AUTOSELECT=NO,DEFAULT=NO #EXT-X-STREAM-INF:BANDWIDTH=155028,CODECS="mp4a.40.2",VIDEO="audio_only" http://video-weaver.yto01.hls.ttvnw.net/v1/playlist/CsgEAGqF192u6sYjL2ikf3yGt-RKLrLIjBLF2cb6f0X0h9XF2fELl75CzoGYjCSADFs6pJcDqJ68fLg0cpHEJSGhy2zyeC2YqEWpF4l34oFE_MIT2acOIbBe7fJhOBy4mMSytv867-X4KeDEWwWCqjWJ9xOYRGEmUsh4NLxNOxVxxq_2nby8nZWLVNVPdmik9PH7xp1jn9dky8gVhIfz3dyK8O3hpshNDZUXMTGhHRjRVt3aishHNhYiPBHHckAAgLu3Uvxv7NHL-NRB8cyKmSuSwq1tm3clgcpzip4Czf6yxcfn_--hR9bSgwycvdY7zHZEbFzTEVOdGhroAk0vN4nr2JBrx4QKZp4tL-kMR3XzEj4fzXCTP4hkLkKvn8exKa2lUzgD5zS27T6Ub00Gv2xCAVFX8EjivVc3MoSEM78hRsFHB5iAUTIUeETmPReSa2jEr4_q3r6Ax8IBFqoAXALtULe-rKkd-IH-SKvXOoqjBNqzs_hHq3mrPmZmyXFmOAmrtrT8xDFR3wQrKWG2bmu4ZFcPDV55C18rYrYQWwKUpCKvmjLlvPLSjyyGQYM1PuRS9_csWcI9CDj_HJfh7YkynT8UVfOKArWhHiWf9mwsZQuILaDE6st0Jdlza4sBXTrqF_9DjS28S9dlAQdu7Rn_MACmcCDKwhYg_njhRQ7Z2u5aLh7vr019NrP3n8QOxYbkOOIKoo441Vsdk-sXtrhU5xy8DJ9ud8oUH03_6_ztJQna27_1GSWEUDE82Vf0pcdsKFe0s7bAE_4SEMYJHyN1qO7_uw0qLGOwP7UaDKChHixjxoZkJ_OFCg.m3u8 ```

Streamlink pushed an update which does seem to block ads for now, but doesn't work with the suggested iframe solution. They use a totally different api and get a different response. Which include ad format, start and stop time as well as the duration.

➜  ~ curl -s "$(streamlink --stream-url --twitch-disable-ads twitch.tv/reckful best)"
Response with ad tags ``` #EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:5 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-TWITCH-ELAPSED-SECS:48786.166 #EXT-X-TWITCH-TOTAL-SECS:48792.172 #EXT-X-START:TIME-OFFSET=0.000 #EXT-X-DATERANGE:ID="stitched-ad-1605749030-15049999993",CLASS="twitch-stitched-ad",START-DATE="2020-11-19T01:23:50.146Z",DURATION=15.050,X-TV-TWITCH-AD-AD-FORMAT="standard_video_ad",X-TV-TWITCH-AD-RADS-TOKEN="eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJicm9hZGNhc3RlciI6IjkwNzIxMTIiLCJ2aWV3ZXIiOiIiLCJzZXNzaW9uIjoiMzEzYWMxNGZhYTUyNDgwN2FjMGRiY2M1MDc2MmIzYWYiLCJ2aWRlb19zZXNzaW9uX2lkIjoiOTAxOTc2NTUxMDUyMjI3MTEzMCIsImR1cmF0aW9uIjozMCwiaXNfdmxtIjpmYWxzZSwiaWF0IjoxNjA1NzQ5MDM1LCJpc3MiOiJHcmFuZERBZHMifQ.VqWG5eCjYCHfYuNzlKByAH6hS6AdovqxGbiWnVmb-v1tgGXk-dn39FBHXmIeCncmawSnkXUcSYddYEqtVDpkTw",X-TV-TWITCH-AD-AD-SESSION-ID="313ac14faa524807ac0dbcc50762b3af",X-TV-TWITCH-AD-URL="https://help.twitch.tv/",X-TV-TWITCH-AD-ORDER-ID="7351234567890",X-TV-TWITCH-AD-LOUDNESS="-90.000000",X-TV-TWITCH-AD-POD-LENGTH="1",X-TV-TWITCH-AD-POD-POSITION="0",X-TV-TWITCH-AD-ROLL-TYPE="PREROLL",X-TV-TWITCH-AD-ADVERTISER-ID="5778044420701",X-TV-TWITCH-AD-CREATIVE-ID="2474283100401",X-TV-TWITCH-AD-LINE-ITEM-ID="2379251610201",X-TV-TWITCH-AD-CLICK-TRACKING-URL="https://example.com" [...] ```
rmanky commented 4 years ago

This might be possible on Firefox (the "Streamlink modify m3u8 playlist" method), but not on Chrome...

As far as I'm aware, modifying the response bodies would require this "bug" to not exist: https://bugs.chromium.org/p/chromium/issues/detail?id=104058

Will run some tests in Firefox

r3ndd commented 4 years ago

It seems like they detect where you're viewing the embed from and only omit ads if it's from some other website. I guess Twitch really doesn't want to have ads on legitimate embeds for some reason, so another workaround is to replace the video with an iframe to an arbitrary domain and have the extension intercept the html of that domain and turn it into a twitch embed.

svengeance commented 4 years ago

^ seems to be quite valid actually. What I found is that if the parent query parameter does not match your real domain, the content security policy will prevent anything form happening. On a whim I decided to put Twitch inside of....Twitch, and voila.

It's definitely worth doing some more experimentation, but by replacing the video player with an embed pointing to itself, I was able to skip ads (I manually paused the ad, replaced w/ the iframe). I'll work on a FF extension to automate this behavior and report back.

This is obviously quite a lazy thing as Twitch could always try to block itself from being an iframe parent, but it seems like a low-cost avenue.

r3ndd commented 4 years ago

On top of that, you can make any domain be the parent by having a parent iframe with a non-Twitch domain have an embed inside of it. It would be something like Twitch -> arbitrary domain -> Twitch, and Twitch thinks you're just watching via an embed on the arbitrary domain. They can never prevent that unless they decide to put ads on all embeds, which they've been reluctant to do.

There are several ways that can be done, the easiest of which is find domains that allow iframes (e.g. YouTube's embed subdomain) and use the extension to replace the html with a Twitch embed.

svengeance commented 4 years ago

I'm working on a Twitch-in-Twitch proof of concept extension for Firefox right now, I'll report back if it seems consistent and maybe follow up with a middleman domain strategy.

I think the best ideas to get around this is something simple which forces them to make tough decisions - is it worth sacrificing X to get Y. This is probably one of those.

rmanky commented 4 years ago

I'm going to head off to bed, but will leave my progress here. See update(s) below.

This is a Firefox addon (the background.js part), which attempts to splice out any ads from the m3u8 playlist/stream. It does not work currently, and if an ad starts playing it just kinda breaks.

function listener(details) {
  let filter = browser.webRequest.filterResponseData(details.requestId);
  let decoder = new TextDecoder("utf-8");
  let encoder = new TextEncoder();

  // Filter the incoming data
  filter.ondata = event => {
    let str = decoder.decode(event.data, {
      stream: true
    });
    // This splits the lines of the m3u8 by "#"
    let lines = str.split("#");
    // Rebuild is what gets sent to the player
    // Debug is what gets logged to the console
    let rebuild = "";
    let debug = "";
    // This pattern checks for the form #EXTINF:2.002,live
    // Plz excuse bad regex
    let pat = /EXTINF:([0-9]*\.[0-9]+|[0-9]+),[^l][^i][^v][^e]/
    // For each line...
    lines.forEach(line => {
      // Make sure it isn't empty (the first line, basically)
      if (line.length > 0) {
        // Check for a series of ad-related tags (mostly taken from Streamlink)
        if (line.indexOf("twitch-stitched-ad") == -1 &&
          line.indexOf("stitched-ad-") == -1 &&
          line.indexOf("X-TV-TWITCH-AD") == -1 &&
          !pat.test(line)) {
          rebuild += "#" + line;
          debug += "#" + line;
        } else {
          // If ignored, only add it to the debug so we can track what is happening
          debug += "<<DODGED BLOCK>>> #" + line;
        }
      }
    });
    console.log(debug);
    filter.write(encoder.encode(rebuild));
    filter.disconnect();
  }

  return {};
}

browser.webRequest.onBeforeRequest.addListener(
  listener, {
    urls: ["https://*.ttvnw.net/v1/playlist/*"]
  }, ["blocking"]
);

console.log("goodbye ads? ☠")

I think the best ideas to get around this is something simple which forces them to make tough decisions - is it worth sacrificing X to get Y. This is probably one of those.

I agree, and abusing the embed would def be the way to do that. Not a bad idea to have multiple approaches, though.

Edit 10/19/2020 @ 10:30 AM I'm having a hard time getting ads to test (ironic, I know). When I do get an ad, the preview for the ad sits there but it never plays. Pressing the play/pause button causes the video to play the last bit of the ad, and then the stream starts normally. Sometimes, the a different ad will then play a few seconds later (which I don't quite understand, since there is no ".ts" file associated with those ads). I need to look at what Streamlink is doing further, and if they are actually able to splice out ads or if they just blackscreen during them. Also, I updated the code with some comments.

Edit 10/19/2020 @ 5:50 PM Keep scrolling, good news ahead

svengeance commented 4 years ago

@rmanky You said the player stops until you toggle play, are the ads successfully skipped thereafter?

svengeance commented 4 years ago

Here is the result of Twitch-in-Twitch: https://gfycat.com/PerfectWastefulAmericancrayfish

The code is dead simple, but will get more complex to account for the usability issues that are okay for a proof of concept but not for a real extension. For one, I'm just using 7.5 seconds as a guess to replace the window. Two, we'll need to adjust the target element to replace, as you can't switch channels without manually refreshing the page. There some smaller items as well such as resizability. But - it's definitely a promising line to go down.

Dead simple code:

setTimeout(() => {
    const currentChannel = document.location.pathname.substring(1) // remove the leading slash.
    const player = document.querySelector('.video-player');
    const height = player.clientHeight;
    const embedUrl = `https://player.twitch.tv/?channel=${currentChannel}&parent=twitch.tv`;

    var frame = buildIframe(embedUrl, height);
    player.replaceWith(frame);
}, 7.5 * 1000);

const buildIframe = (src, height) => {
    var frame = document.createElement('iframe');
    frame.setAttribute('src', src);
    frame.setAttribute('height', height + 'px');
    frame.setAttribute('width', '100%');
    frame.setAttribute('allowfullscreen', true);
    return frame;
};
rmanky commented 4 years ago

@rmanky You said the player stops until you toggle play, are the ads successfully skipped thereafter?

it did a few times, something is wrong with the code though so some ads are playing... debugging

Jta26 commented 4 years ago

Here is the result of Twitch-in-Twitch: https://gfycat.com/PerfectWastefulAmericancrayfish

The code is dead simple, but will get more complex to account for the usability issues that are okay for a proof of concept but not for a real extension. For one, I'm just using 7.5 seconds as a guess to replace the window. Two, we'll need to adjust the target element to replace, as you can't switch channels without manually refreshing the page. There some smaller items as well such as resizability. But - it's definitely a promising line to go down.

Dead simple code:

setTimeout(() => {
    const currentChannel = document.location.pathname.substring(1) // remove the leading slash.
    const player = document.querySelector('.video-player');
    const height = player.clientHeight;
    const embedUrl = `https://player.twitch.tv/?channel=${currentChannel}&parent=twitch.tv`;

    var frame = buildIframe(embedUrl, height);
    player.replaceWith(frame);
}, 7.5 * 1000);

const buildIframe = (src, height) => {
    var frame = document.createElement('iframe');
    frame.setAttribute('src', src);
    frame.setAttribute('height', height + 'px');
    frame.setAttribute('width', '100%');
    frame.setAttribute('allowfullscreen', true);
    return frame;
};

instead of waiting an arbitrary amount using setTimeout, why not use document.onload?