From dd71f707f69e5d7927eac5b61498c948dac7fa33 Mon Sep 17 00:00:00 2001 From: chu23465 <130033130+chu23465@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:13:05 +0530 Subject: [PATCH] Changes Implemented track download skip if file already exists. A few Linux support changes. Implemented caching cookies to profile cookies path. --- README.md | 10 +++++++-- install.sh | 3 ++- poetry.lock | 12 ++++++++++- pyproject.toml | 1 + vinetrimmer/commands/dl.py | 39 +++++++++++++++++++++++----------- vinetrimmer/key_store.db | Bin 389120 -> 409600 bytes vinetrimmer/objects/tracks.py | 21 +++++++++++++----- vinetrimmer/services/hulu.py | 5 ++++- vinetrimmer/services/jio.py | 3 +-- vinetrimmer/utils/io.py | 12 ++++++++--- 10 files changed, 79 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 7360ef2..2ccad82 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,11 @@ Support for Sport replays or live streams is not planned. It's a whole thing wit ## Features - Progress Bars for decryption ([mp4decrypt](https://github.com/chu23465/bentoOldFork), Shaka) - - Refresh Token fixed for Amazon service - Reprovision .prd automatically after 2 days - ISM manifest support (Microsoft Smooth Streaming) (WIP/Experimental) - N_m3u8DL-RE downloader support (Experimental) - Atmos audio with ISM manifest (Amazon) is Fixed - - Resume failed download has been sort of implemented. If a track has been successfully downloaded previously and exists in `Temp` directory, VT will not download said track again and will resume download. + - Resume failed download has been implemented. If a track has been successfully downloaded previously and exists in `Temp` directory (encrypted or decrypted), VT will not download said track again. ## Usage @@ -230,6 +229,11 @@ If you are getting an `AssertionError` with Amazon, then try reprovisioning the - From my testing, when using with VPN, it causes lots of issues, mainly needing to clear `Cache` folder and login repeatedly. Use residential proxies if available. Don't hammer service. Try waiting a minute or two before logging in again. - If you are getting `No 2160p track found` error for a title you know has 4k, then try passing `-r DV` or `-r HDR`. Make sure your account can access highest qualities. +### Hulu + + - Authorization: cookies saved to `vinetrimmer/Cookies/Hulu/default.txt` + - Windscribe VPN sometimes fails + ### Example Command Amazon Example: @@ -352,6 +356,7 @@ Tested so far on Amazon, AppleTVPlus, Max, and DisneyPlus. - `--standalone` will give a folder of compiled pythonic objects. Zip it to distribute. This is recommended. - If you don't want to carry around/deal with a zip, instead use `--onefile`. This has the drawback of setting the default folders to the temp folder in whatever OS you are using. This could be fixed with some extra code but that is currently not implemented. + - Refer to [link](https://nuitka.net/user-documentation/user-manual.html) if anything errors out. ## Broken / To-Do (Descending order of priority) @@ -385,6 +390,7 @@ Tested so far on Amazon, AppleTVPlus, Max, and DisneyPlus. ### Amazon Specific + - [ ] Refresh Token for Amazon service - [ ] Pythonic implementation of init.mp4 builder for ism manifest for avc, hvcc, dv, ac3, eac3, eac3-joc codecs - [ ] Make a pure python requests based downloader for ISM/MSS manifest. Write init.mp4 then download each segment to memory, decrypt in memory and write it to a binary merged file. Download segments in batches. Batch size based on thread count passed to program. Download has to be sequentially written. - [ ] `--bitrate CVBR+CBR` is currently broken diff --git a/install.sh b/install.sh index 2c8d52c..8077696 100644 --- a/install.sh +++ b/install.sh @@ -22,4 +22,5 @@ cp ffprobe ./binaries/ cp ffplay ./binaries/ cp mkvtoolnix ./binaries/ cd ./binaries/ -find . -type f -print0 | xargs -0 chmod -x \ No newline at end of file +find . -type f -print0 | xargs -0 chmod +x +chmod +x /home/hidden/VT/VT-PR/binaries/* \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index a56c34d..ef8c180 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2344,6 +2344,16 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "ushlex" +version = "0.99.1" +description = "Replacement for shlex (that works with unicode) for Python 2.X." +optional = false +python-versions = "*" +files = [ + {file = "ushlex-0.99.1.tar.gz", hash = "sha256:6d681561545a9781430d5254eab9a648bade78c82ffd127d56c9228ae8887d46"}, +] + [[package]] name = "validators" version = "0.18.2" @@ -2537,4 +2547,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "f840ef73c0dc08490a0894e655df27f34ab60e5a5f8c4e3ae8e23ec15f548dad" +content-hash = "a6d1d8597c66d0b914da73da39508244167ff2ea215b36569f40e0d0c909b74a" diff --git a/pyproject.toml b/pyproject.toml index 2e6b97c..c37ba57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ validators = "^0.18.2" websocket-client = "^1.1.0" xmltodict = "^0.14.2" yt-dlp = "^2024.11.11" +ushlex = "^0.99.1" [tool.poetry.group.dev.dependencies] flake8 = "^3.8.4" diff --git a/vinetrimmer/commands/dl.py b/vinetrimmer/commands/dl.py index 5cb42a7..34d01ae 100644 --- a/vinetrimmer/commands/dl.py +++ b/vinetrimmer/commands/dl.py @@ -168,6 +168,17 @@ def get_cookie_jar(service, profile): return cookie_jar return None +def save_cookies(service, profile): + """Save cookies from service session to profile's cookies.""" + cookie_file = os.path.join(directories.cookies, service.lower(), f"{profile}.txt") + if not os.path.isfile(cookie_file): + cookie_file = os.path.join(directories.cookies, service, f"{profile}.txt") + + if os.path.isfile(cookie_file): + cookie_jar = MozillaCookieJar(cookie_file) + for cookie in service.session.cookies: + cookie_jar.set_cookie(cookie) + cookie_jar.save(ignore_discard=False, ignore_expires=False) def get_credentials(service, profile="default"): """Get the profile's credentials if available.""" @@ -310,7 +321,7 @@ def dl(ctx, profile, cdm, *_, **__): @dl.result_callback() @click.pass_context def result(ctx, service, quality, closest_resolution, range_, wanted, alang, slang, audio_only, subs_only, chapters_only, audio_description, - list_, keys, cache, no_cache, no_subs, no_audio, no_video, no_chapters, atmos, vbitrate: int, abitrate: int, no_mux, mux, selected, latest_episode, strip_sdh, *_, **__): + list_, keys, cache, no_cache, no_subs, no_audio, no_video, no_chapters, atmos, vbitrate, abitrate: int, no_mux, mux, selected, latest_episode, strip_sdh, *_, **__): def ccextractor(): log.info("Extracting EIA-608 captions from stream with CCExtractor") track_id = f"ccextractor-{track.id}" @@ -332,13 +343,12 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla else: log.info(" + No captions found") - global content_keys - log = service.log service_name = service.__class__.__name__ - if service_name in ["DisneyPlus", "Hulu"]: # Always retrieve fresh keys for DSNP so that content_keys variable has 2 kid:key pairs + if service_name in ["DisneyPlus", "Hulu"]: # Always retrieve fresh keys for DSNP so that content_keys variable has 2 kid:key pairs, change this to fetch all keys for title from cache + global content_keys no_cache = True log.info("Retrieving Titles") @@ -477,9 +487,11 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla proxy = next(iter(service.session.proxies.values()), None) else: proxy = None + + if service: + save_cookies(service, ctx.obj.profile) track.download(directories.temp, headers=service.session.headers, proxy=proxy) log.info(" + Downloaded") - # To-Do: For DSNP KID add -> mp4info (Bento4) with --verbose option show default kid from init.mp4 file if isinstance(track, VideoTrack) and track.needs_ccextractor_first and not no_subs: ccextractor() if track.encrypted: @@ -487,7 +499,7 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla if track.key: log.info(f" + KEY: {track.key} (Static)") elif not no_cache: - track.key, vault_used = ctx.obj.vaults.get(track.kid, title.id) + track.key, vault_used = ctx.obj.vaults.get(track.kid, title.id) #To-Do return all keys for title.id for DSNP, HULU if track.key: log.info(f" + KEY: {track.key} (From {vault_used.name} {vault_used.type.name} Key Vault)") for vault in ctx.obj.vaults.vaults: @@ -549,6 +561,9 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla ) else: raise log.exit("Unable to license") + + if service: + save_cookies(service, ctx.obj.profile) content_keys = [ (str(x.kid).replace("-", ""), x.key.hex()) for x in ctx.obj.cdm.get_keys(session_id) if x.type == "CONTENT" ] if "common_privacy_cert" in dir(ctx.obj.cdm) else [ @@ -603,12 +618,12 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla executable = next((x for x in (shutil.which(x) for x in names) if x), None) if not executable: raise log.exit(" - Unable to find packager binary") - dec = os.path.splitext(track.locate())[0] + ".dec.mp4" + dec = os.path.splitext(track.locate())[0].replace("enc", "dec.mp4") os.makedirs(directories.temp, exist_ok=True) try: os.makedirs(directories.temp, exist_ok=True) - subprocess.run([ + args = [ executable, "input={},stream={},output={}".format( track.locate(), @@ -633,7 +648,8 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla ] ), "--temp_dir", directories.temp - ], check=True) + ] + subprocess.run(args, check=True) except subprocess.CalledProcessError: raise log.exit(" - Failed!") @@ -641,7 +657,7 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla executable = shutil.which("mp4decrypt") if not executable: raise log.exit(" - Unable to find mp4decrypt binary") - dec = os.path.splitext(track.locate())[0] + ".dec.mp4" + dec = os.path.splitext(track.locate())[0].replace("enc", "dec.mp4") os.makedirs(directories.temp, exist_ok=True) try: os.makedirs(directories.temp, exist_ok=True) @@ -675,7 +691,7 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla try: os.makedirs(directories.temp, exist_ok=True) exec_string = f"{executable} -i {track.locate()} -hide_banner -loglevel error -map 0 -c:a copy {eac3}" - subprocess.run(exec_string) + subprocess.run(exec_string, check=True) except subprocess.CalledProcessError: raise log.exit(" - Failed!") track.swap(eac3) @@ -759,7 +775,6 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla muxed_location, os.path.join(final_file_path, f"{title.parse_filename(media_info=media_info)}.{extension}") ) - log.info("Processed all titles!") diff --git a/vinetrimmer/key_store.db b/vinetrimmer/key_store.db index b0d95671a4ced9af213565b65caad91b18aeb013..50ae2614f5e169df6ebd88b363e2fd1eebfdb0e5 100644 GIT binary patch delta 8128 zcmbtZ3%C@;m7c2VnV$DtKTyJ|0U{s>gWc6{LGEw`2EyCds;uNzb#)`6qDHSul(~_O zT#OsH=h>UyQ6GAR>FZ2R2@lb>VUc=5$w2 z_o=FL&VT-My7R4mot^!cAAigr6N!X?|A+q6+eaGtKPE=`s8HIII1X*Teg4TQw3)0R z^VbZjw4$MlkLj!S>(d9_l(~xWOM;8%Er5UQj58;UJA1;wv&XefoiGpv*6hq&hEhY0 zOZD^m)Mu&RD8blwD17-tjy*J%iN%x3iCH;wEoxKXwv**k?YLT3M^8tNZ11a7GcY)6qB3n7mOJ;L3Pua- z+u`CEIY*FcbjLU4;y2O6=xC^{&&Kf${U^W;NIrKwswh+7id^wBo>c$E=|4h3F8EgS zK0^+Jqa<8SoLVo@iS+@k)6u!|qZd(i&2L-H7OWH(@0iipF$48)L557WqgITsl;hjc zz%*W$T$FT_j~bV?BOT$yOY&}pHr3*3QtAhuVc1M_9Mk2x2WOnR)YF+QY}cW_Lp7i2 z4r7{O28PWIBhZ)=gtl$Kb*0#Rj3rFRv2;gg2D43zYd(#KoG{dR;OorfhV7f0~ z_oxr57po_w-bme%nx0aV8qqQ7`Mt*>rh5^ ztw$5bS&n5h?!mS=zGGUh&|K;ZVd{?Hp2Kt|G~qEl)Lot1OmH7|NVh^w1V%uqrX=LY zkD;lJd)6U=>T^#Y*MO-#sWy}iXn#pf{~@6#Za|W*M%Cd@ptsar>TlG!>cG^isTHXy z*+;WiWk+SUWq#&`BKial_56U!YgeOL^@XRk$=rkJH8~J`Ro+%3mBu9xp?8Tq{!>(u z!?&W%vbYINll2!+mmCAjQ}RLLW%=wQ=;mZTK)=Lt^p+1V{ASM5CqAKS-hHh30--n~!a1@2hGAxJx9CZH2di2k@G4Cz3a>0n~hJ>BC z5uv-t#O!tHt}M;$$lQ~giXXvO;1TFO^)vNh^-^_cYHO}8ht0%eSmMU~Tyksrx%{Bq zYdIBeKlG=@b8=uGU0b&}xSPdI)3w0+4c+I$(p~BpU>hI8!*CrZ44ja1$D*EQXr?d% zGvGqOji+bg-I-{LRgz$XB0CzXJPYusALY)Sh%Pd_8rc+H& z@P7sY<{BUj=Ibs*7Q~jJG$#p;4pHk^9uJx68y2%dM>kAxWbQZ+-=X7LmgQPDOc{)4 zn$#_60kyauJ=hO*CDnLsHa=M?;bE|SixS|?ZFnDkSf*!^Nhc`B=DwXH+4b2DbvJo7 zdunET=5Bmx=7LP0^fQgCW|Fl4JQL)|Gs(F6hSRF`XU0_Jh70g3HP_{Cpj$9yO$W!- zJ;xWJ6b^|B#FOdn(v^6>a$jkL(oV)FH#Yq~ zwX(RX>EaXPWibnONV7xB3Igyzhr3*3fn{qp_cfbF0O^FnVc>(@v@`<%1#r{?dqB$Y zm>_!^wYTsCNVdI#C&`X?@U8U~W80GPv*qf(q#}R48Q;+u@GgcxPOStJeE&W8;p$JK z6uMaE-pAEsaTPiNV)+NS(va_CR~=^sF7v4k*vw2lG#E&sn~ocT2bn^%IOn$JFjoYg z&|Nb^da%A`L1tsZq)Jkr`ys9>rFDsF60Z`GJXzxmR;5 za#M0jc0GPLT~R(%_o@$(ZgsvoB()_aQ!`VUV~_s{x5?q};Hgy}`X1A~&~;p4vVgh{^WhBAgllLT2Mv%ZAc2ICH*GE; zV^W`T&klhV01faIpfVBHIdHO4QOU2HC%-C0C!Nic%KfB*$vFANJ9zRzdoR$GitIm_ zOosg?TVyzxoG~@|J6N=DLS}_Uxf);&;1A=Wu9?gM$^naYEzRSA58!Q_S{hiu-x7W9=H2sG8xYC4OmX! zhbjjrcitPACH*rSZ)D6E`@+Q7bUh)PU z&3GN$>X~l%`d~R}#l&rtw%~b4?u{;9vlTCu_rC!b|8WzZDhEeLS3eK8Zh0N9-uXJj zxbgzNRrcA0C(GfR@pyUuCfwHe;S1Qoa@g~Dn%uStk4wg8tv`2s<>E}dp`Oq@4Lp?r z0gdn+;0X8*kTokXVFZ9(*tQGV0t{jUjWg{4QcOrSJ!GCOgp$oF{Q=Dvk;Ec=IX(rw zO~$MH@cu(-KeaNwM1HeH8HLl&%Bd~N-~xKPxo=H-vST7ASz$1i&v%n1E;NzGVbK$bpF&u4{x`*pLU94#dqiEDiXW z1*zS#UBD}@lfAJ{psy@|MccyQkiA3jN&pAOTuV5@g+VI-5CC2gK>Sk3BsMQma8GIm zkN}0p$*#t+{gt!S#zmKtAGHo3Z3z>c0c}<0ltut_y<1#`F9iTA6@Cx&X`}p`c2cP& zuJ6HOe~z}2Zn6|^g}-=K0k?QyX^ssk*NpHe0tlDGw{DpxH(6*=8=%?q9AP-N6HovX zpq&=vX8|-uNjD!u;SpT3ndL(10o8-AhXG_x%MNTUfLMau533JEKm({UxT^!}pyrj>LoRYi?uN=d~ssU%~mC`v4eLSl&(98092SSFN2B*_7C0OUEC zd_lg52lkWwvHYBT9?N}XUo1Z(pT%-7*&E9}WKS%2lijiWlzbY?U1V1*KOvtqE_#Ce zB$u)iHC#Mbrn|}f#?{^At^%Hj<$}ve1>yQ-t<}b=EoAO7F)H> zbX!!=s8KDA=S#zm{i8>&2QJ(((( zKSGwuZl6q-?=w=V|J0q*`0sUOc1%6NwBrr}-p)O^G8tEzz87*0Kwjt6fxzM(q*%sn zDma861DfeukcmT>n9gml<`;vKeYd8JMzL)_us9 zCWQoK2OUcm#fodeUk|Orj;vm2z9H6Z!d8Z6%>&v3*;5#r2lMs-m!JaGpd|No;ki*S zA)>{cLH*&fP=gs?={!Vd>bIzqp%bc3J=$>nSj);X94?!?Bhlh&WA-Bx~?-QX@u?@ zP$~c*dLgh?r-$Z%3P=>pF53X9*sS%eHV+k>Jv3Xwhfs0DasUjn4COu8+4Te*QOnbzfu*^;V@XG>`AExx z+E?p>UTD6$Y3Cy+AizIreojwM$e zTyyWsg43hApck6I8*8qQ*;Q%Go|wEhqoQKs6v#hva5oy?q8y*fC7(z!9rWoBzW>>@N4fcATuZFO{|o4a_lPwIBI|%>L|{v z^q#9UTbQyk44Sq7dOvBMOqvzyk#fGTQa)$}@BbE5sJ9FwljSo5$prbrFmjvRK8Q?c Kv=1j2qW=L`&Eu2+ delta 2423 zcmYLL3s98T75>jX_kaJpi*XV~e1I=hl=#L6unXd@iU_`l0VE5?h*5)4YpWdrW2U|$ z68}2KRMFb0R!3`9lp7{BLsN~~T7^VMC5=jId{jqGiHVJ|_O4+2&+N>7oOA9y-}%0C zx1u7t;#!BQn0AedqP!-5&p++`{zIHbC2h0O%JL+lgnQ6W4d(+l^-RxV= zDeI4RwRf#I)qCEa;tX*X*9;vPwCHpLq@#4B`NeFUtB;-p=~Q?GLtL)KQ$+&{xwqtE z5>>_UXNwc)?nnqmwtLf&Viq#xD0(c<$^tqU0|7Nt&U5LJfRMW<PKI}3Oh?8gg9uYexZzXB2sb-6kOrh!L< zD_|kgwvVAw6ut|)6B0)#E5Q25T4D__ZWw$(y02;&Qw39VLh{|@VDQY}m*|Ch2D^+zE~oc$8E zGBLLvw)lGR7$r%mP;F)!tlh6yNJP?O8=ej+zmr+3IoSq*?~s zBA^91738R$X>$@EL??38@y~B^T;62=MKDDEWOXp30~et(Qkf9FF-;v$HyULM2js~8 zi9aBcrmGVT9;r61o~f>-#3r~bZWpRCAow+SQW`HEla$i{+c9V@GuBvx%{%6ABlThl z@U{MfenelO_tvgb{~VUw!(B6Q^_7 z_a;3qW|!Q?bj$1z}B)pBRiQKcO1Wp$|4F1ZnXj5J7=Jb1)m$S*|4#pdUh>AUww!{zRSY3D zpyT~kChYq?%1LVL^wBa@cYgIr^M;5zh!@+|LDE0dl)Z?tLuMWqu|EMeUcfZQdT)ofx}KTjFGvxi?t4$m@%7AnIuFROUN)-ZL6AoNpyQbDUVE%)V@I zvNLUN9bNpMQ;mTBZ~ZW?*XQZIv@05EIhv_{!iyv1 zWu^Q+-`k3Xq!7zun@hRm)3#PV_1%mCK+1`eWu2d_h3I;m1g9WX3%Q$8)7!G&_!I&R z=F{WuGW#DQR)PJJhG%=APUdJWqs^>zAP-PsPf3BljnJMw1@BQ}F|MJ&2TPW7bmrd+TNM zmbnugzWcclBNB|rPb>*bC>a|{{fk+eICF{J2T|}kOW+JTQ$q>6L`#aJOb(uPt-{HG>A@2ZJu>sd2r9D7u%d=>Z z%rpBCZl>AM{5eJkxm=ug7%!5qE1yV}HMo|-(OjprEL#mT_RN~c9R5AM}Y0o9hnO1fH$8^xi{JR5Zh11#nx zLa)c0O#X#f4pjAL_S=b)sJwTsJ?=8+F@rS zo%9DdNQ`=bf70E7f#=Uf#861XB>;$1hR#!A2efz{+K;^-ruz?)IE%eG$o8Z zUx?I^^?~L##mc9}%^N*NoCxDIq^pN;qL}?UUPJadqo!||6k;~@E5=PCW-eywt4m_J zD2Y|Kb;B36_M`_OL1xsf8cSC%R+X%*QaAZwK;#G2-KHiF=xq2&KL{hG%~M_YKZFyy K;9>R0jsFEcV#I6! diff --git a/vinetrimmer/objects/tracks.py b/vinetrimmer/objects/tracks.py index c475d3e..28003c4 100644 --- a/vinetrimmer/objects/tracks.py +++ b/vinetrimmer/objects/tracks.py @@ -337,13 +337,21 @@ class Track: ) self.url = segments - if (Path(save_path).is_file() or Path(os.path.splitext(save_path)[0] + ".dec.mp4").is_file()) and not (os.stat(save_path).st_size <= 3): + if ( + Path(save_path).is_file() and not + (os.stat(save_path).st_size <= 3) + ) or ( + Path(save_path.replace("enc", "dec")).is_file() and not + (os.stat(save_path.replace("enc", "dec")).st_size <= 3) + ): log = logging.getLogger("Tracks") log.info("File already exists, assuming it's from previous unfinished download") - if Path(os.path.splitext(save_path)[0] + ".dec.mp4").is_file(): + if Path(save_path.replace("enc", "dec")).is_file(): self.encrypted = False - self._location = save_path + self._location = save_path.replace("enc", "dec") + else: + self._location = save_path return save_path if self.source == "CORE": @@ -432,7 +440,10 @@ class Track: if not os.path.exists(target) or not self._location: return False os.unlink(self._location) - os.rename(target, self._location) + if "dec.mp4" in target: + self._location = target + else: + os.rename(target, self._location) return True @staticmethod @@ -1225,7 +1236,7 @@ class Tracks: if not with_forced: self.subtitles = [x for x in self.subtitles if not x.forced] if by_language: - self.subtitles = list(self.select_by_language(by_language, self.subtitles, one_per_lang=True)) + self.subtitles = list(self.select_by_language(by_language, self.subtitles, one_per_lang=False)) def export_chapters(self, to_file=None): """Export all chapters in order to a string or file.""" diff --git a/vinetrimmer/services/hulu.py b/vinetrimmer/services/hulu.py index 30f2a80..fd13298 100644 --- a/vinetrimmer/services/hulu.py +++ b/vinetrimmer/services/hulu.py @@ -16,7 +16,10 @@ class Hulu(BaseService): \b Authorization: Cookies - Security: UHD@L3 + Security: + Hulu original show/movies 4K SDR: L3/SL2000 + Hulu original show/movies 720/1080/4K HDR/DV:L1/SL3000 + Licensed show/movies 4K SDR 720/1080/4K HDR/DV: L1/SL3000 """ ALIASES = ["HULU"] diff --git a/vinetrimmer/services/jio.py b/vinetrimmer/services/jio.py index 454ca8b..3109018 100644 --- a/vinetrimmer/services/jio.py +++ b/vinetrimmer/services/jio.py @@ -209,7 +209,6 @@ class Jio(BaseService): self.log.warning('No mpd found') for track in tracks: - #track.language = Language.get('ta') track.needs_proxy = True return tracks @@ -261,7 +260,7 @@ class Jio(BaseService): def login(self): self.log.info(' + Logging into JioCinema') if not self.PHONE_NUMBER: - self.log.exit('Please provide Jiocinema registered Phone number....') + self.PHONE_NUMBER = input('Please provide Jiocinema registered Phone number with country code: ') guest = self.session.post( url="https://auth-jiocinema.voot.com/tokenservice/apis/v4/guest", json={ diff --git a/vinetrimmer/utils/io.py b/vinetrimmer/utils/io.py index 307f43f..a583868 100644 --- a/vinetrimmer/utils/io.py +++ b/vinetrimmer/utils/io.py @@ -17,6 +17,9 @@ from vinetrimmer.utils.collections import as_list from sys import platform +if "win" not in platform: + import shlex + def load_yaml(path): if not os.path.isfile(path): #print(f"Service config does not exist -> {path}") @@ -241,11 +244,11 @@ async def saldl(uri, out, headers=None, proxy=None): async def m3u8dl(uri, out, track, headers=None, proxy=None): - executable = shutil.which("N_m3u8DL-RE") or shutil.which("m3u8DL") or "/usr/bin/N_m3u8DL-RE" + executable = shutil.which("N_m3u8DL-RE") or shutil.which("m3u8DL") if not executable: raise EnvironmentError("N_m3u8DL-RE executable not found...") - ffmpeg_binary = shutil.which("ffmpeg") or "/usr/bin/ffmpeg" + ffmpeg_binary = shutil.which("ffmpeg") arguments = [ executable, track.original_url or uri, @@ -309,6 +312,9 @@ async def m3u8dl(uri, out, track, headers=None, proxy=None): try: arg_str = " ".join(arguments) #print(arg_str) - p = subprocess.run(arg_str, check=True) + if "win" in platform: + p = subprocess.run(arg_str, check=True) + else: + p = subprocess.run(shlex.split(arg_str), check=True) except subprocess.CalledProcessError: raise ValueError("N_m3u8DL-RE failed too many times, aborting") \ No newline at end of file