Start decoder TV channels with one click in Jellyfin Web
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
// Isolated headless browser; temporary server account is always removed.
|
||||
const {execFileSync}=require('node:child_process');
|
||||
const {chromium}=require('C:/Users/michal/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright');
|
||||
const ssh=py=>execFileSync('ssh',['-o','BatchMode=yes','root@192.168.100.25','pct exec 100191 -- python3 -'],{input:py,encoding:'utf8'}).trim().split('\n').at(-1);
|
||||
const setup=`import sqlite3,json,secrets,urllib.request
|
||||
c=sqlite3.connect('file:/var/lib/jellyfin/data/jellyfin.db?mode=ro',uri=True);k=c.execute('SELECT AccessToken FROM ApiKeys ORDER BY Id LIMIT 1').fetchone()[0];c.close()
|
||||
def api(p,d):
|
||||
r=urllib.request.Request('http://127.0.0.1:8096'+p,headers={'Authorization':'MediaBrowser Token="'+k+'", Client="DecoderIntegration", Device="Maintenance", DeviceId="decoder-maintenance", Version="7"','Content-Type':'application/json'},data=json.dumps(d).encode());b=urllib.request.urlopen(r).read();return json.loads(b) if b else None
|
||||
n='decoder-browser-check-'+secrets.token_hex(4);p=secrets.token_urlsafe(24);u=api('/Users/New',{'Name':n});api('/Users/'+u['Id']+'/Password',{'CurrentPw':'','NewPw':p});print(json.dumps({'id':u['Id'],'name':n,'password':p}))
|
||||
`;
|
||||
(async()=>{
|
||||
const user=JSON.parse(ssh(setup));let browser;
|
||||
try{
|
||||
browser=await chromium.launch({headless:true,channel:'msedge'});const page=await browser.newPage({viewport:{width:1280,height:900}});
|
||||
const errors=[];page.on('pageerror',e=>errors.push(e.message));
|
||||
const base='http://192.168.100.191:8096/web/index.html';
|
||||
await page.goto(base);await page.locator('#txtManualName').fill(user.name);await page.locator('#txtManualPassword').fill(user.password);await page.getByRole('checkbox').check();await page.getByRole('button',{name:'Sign In',exact:true}).click();
|
||||
await page.waitForURL(/home/,{timeout:20000});
|
||||
await page.waitForFunction(()=>JSON.parse(localStorage.getItem('jellyfin_credentials')||'{}').Servers?.some(s=>s.AccessToken&&s.UserId));
|
||||
await page.goto('http://192.168.100.191:8096/DecoderIntegration/Channels');
|
||||
try { await page.locator('#channelRows section').first().waitFor({timeout:30000}); }
|
||||
catch(error) { console.log(JSON.stringify({url:page.url(),text:await page.locator('body').innerText(),errors})); await page.screenshot({path:process.argv[2]||'decoder-channels.png'});throw error; }
|
||||
await page.goto(base+'#/livetv?tab=2');
|
||||
const image=page.locator('.card[data-type="TvChannel"]').filter({has:page.locator('a.textActionButton',{hasText:'TVP INFO HD'})}).locator('a.cardImageContainer');
|
||||
const playback=page.waitForResponse(r=>r.url().includes('/PlaybackInfo')&&r.request().method()==='POST',{timeout:60000});playback.catch(()=>{});
|
||||
await image.click({position:{x:20,y:20}}).catch(async e=>{console.log(JSON.stringify({image_count:await image.count(),error:e.message.slice(0,600)}));throw e;});const r=await playback;if(!r.ok())throw Error('Native channel playback failed');
|
||||
await page.waitForFunction(()=>[...document.querySelectorAll('video')].some(v=>v.currentTime>0&&v.readyState>=2),{},{timeout:60000});
|
||||
console.log(JSON.stringify({native_channel_image_one_click_playback:'PASS'}));
|
||||
}finally{
|
||||
if(browser)await browser.close();
|
||||
ssh(`import sqlite3,urllib.request,pathlib\nc=sqlite3.connect('file:/var/lib/jellyfin/data/jellyfin.db?mode=ro',uri=True);k=c.execute('SELECT AccessToken FROM ApiKeys ORDER BY Id LIMIT 1').fetchone()[0];c.close()\nurllib.request.urlopen(urllib.request.Request('http://127.0.0.1:8096/Users/${user.id}',headers={'Authorization':'MediaBrowser Token="'+k+'", Client="DecoderIntegration", Device="Maintenance", DeviceId="decoder-maintenance", Version="7"'},method='DELETE')).read()\npathlib.Path('/var/lib/jellyfin/decoder-integration/users/${user.id.replace(/-/g,'')}.json').unlink(missing_ok=True)\nprint('clean')`);
|
||||
}
|
||||
})().catch(e=>{console.error(e.message);process.exitCode=1});
|
||||
Reference in New Issue
Block a user