Jump to content

Recording ended early


iiiJoe
Go to solution Solved by Luke,

Recommended Posts

49 minutes ago, Brian_M said:

Here is my scripts I wrote that I use on Linux to solve this problem.  While I know this may not help you all out, maybe it will give you some ideas.

Here's my ffmpeg script.  The real ffmpeg was renamed to ffmpeg.bin

#!/bin/bash

APP_DIR=/opt/emby-server

export AMDGPU_IDS=$APP_DIR/share/libdrm/amdgpu.ids
export FONTCONFIG_PATH=$APP_DIR/etc/fonts
export LD_LIBRARY_PATH=$APP_DIR/lib:$APP_DIR/extra/lib
export LIBVA_DRIVERS_PATH=$APP_DIR/extra/lib/dri
export OCL_ICD_VENDORS=$APP_DIR/extra/etc/OpenCL/vendors
export PCI_IDS_PATH=$APP_DIR/share/hwdata/pci.ids
export SSL_CERT_FILE=$APP_DIR/etc/ssl/certs/ca-certificates.crt

$APP_DIR/bin/ffmpeg.bin "$@"

if echo $5 | grep recording; then
  LOGFILE=`echo $5|sed s/graph//`

  for ((restart = 1; restart <= 10; restart++)); do
    sleep 5
    if ! grep "\[q\] command" $LOGFILE; then
      echo "*** "`date`" - FAIL - RESTART #"$restart" - ""${52}" - log $LOGFILE >> /var/lib/emby/logs/failed-recordings
      echo
      echo "**** recording failed, attempting restart #$restart ****"
      echo
      $APP_DIR/bin/ffmpeg.bin $1 $2 $3 $4 "$5" $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} "${20}" ${21} ${22} ${23} ${24} ${25} ${26} ${27} ${28} ${29} ${30} ${31} 15 ${33} "${34}" ${35} ${36} ${37} ${38} ${39} ${40} ${41} ${42} ${43} ${44} ${45} ${46} ${47} ${48} ${49} ${50} ${51} -f mpegts - >> "${52}"
    else
      break
    fi
  done
fi

Basically all it does is check the log file for "[q] command" and if this doesn't exist, the recording has failed, so it will restart the recording for up to 10 times.  After that many restarts, I've found it pointless to try anymore.  The recording is just appended to the one that failed.  I think I had to add the sleep timer in there to wait until the logs flushed. 

I've always had to "recopy" IPTV recordings to make comskip work in the first place, but this may be needed to make these files playable after that fact.  Here's my postprocessing script.  I should probably use a temporary directory to do the recopy in so that Emby doesn't detect it and make .nfo and thumb files for the copy while it's being processed.

#!/bin/bash
LD_LIBRARY_PATH=
ffmpeg -i "$1" -codec copy "$1-fixed.ts" 2> "$1.fix.log"
ls -l "$1" >> "$1.fix.log"
ls -l "$1-fixed.ts" >> "$1.fix.log"
mv "$1-fixed.ts" "$1"
rm "$1-fixed.nfo"
rm "$1-fixed-thumb.jpg"
/usr/bin/comskip --ini=/usr/local/etc/comskip.ini "$1" > "$1-comskip.log"

I know this is the Windows forum, so I apologize I'm not much help, but maybe some ideas can be had from this.  I'm just very surprised that all of this is needed in the first place really, if I just straight up kill ffmpeg Emby will automatically restart the recording, but it will not do so for these kinds of failures.

Btw, I’m very surprised that this is needed as well. 

Link to comment
Share on other sites

joekingcool
8 hours ago, iiiJoe said:

Not sure why Emby can’t be programmed to restart the recording when “seek to start” fails.

agreed ! but after seeing all the updates in the beta, i can see how they have allot on there plate. 

but for now i think i could make a script/app to do the same. check every min and restart if failed. 

but im stuck with the cmdline of ffmpeg that's in the failed log file. and that cmd starts the recording back up.

1.but it dont stop when its suppose to too.

2.and it dont create another log file afterwards. 

example:

E:\Emby-Server\system\ffmpeg.exe -loglevel +timing -y -print_graphs_file "E:\Emby-Server\programdata\logs\recording-conversion-c65f64a6-e096-4fca-9370-6fe7a7e25394_1graph.txt" -copyts -start_at_zero -fflags +genpts -analyzeduration 3000000 -f hls -stream_loop -1 -c:v:0 h264 -noautorotate -user_agent "VLC/3.0.0-git LibVLC/3.0.0-git" -multiple_requests 1 -reconnect 1 -reconnect_on_network_error 1 -reconnect_on_http_error 5xx,4xx -reconnect_streamed 1 -reconnect_delay_max 4 -i "https://justsomeurl.m3u8" -map 0:0 -map 0:1 -sn -c:v:0 copy -c:a:0 copy -disposition:a:0 default -avoid_negative_ts disabled -map_metadata -1 -map_chapters -1 "E:\Emby-Server\recordings\Christmas Class Reunion (2022)\Christmas Class Reunion (2022).ts"
 

PS. just would like my wife to not remind me of another failed hallmark Christmas show, and why i choose emby over plex.

 

Link to comment
Share on other sites

3 hours ago, iiiJoe said:

Thank you for this. I will pass it along to my friend. Question: does your script start a new recording or does it continue the previous? Also, does the script stagger the restarts with like 30 second (or some other interval) pauses?

It continues the previous recording.  It just adds onto it.  I think without any postprocessing it will still play back just fine but I usually do that so comskip will work correctly.

Link to comment
Share on other sites

joekingcool
15 minutes ago, Brian_M said:

It continues the previous recording. 

did you post a script or plugin that your able to do this? i think i remember you saying you used linux? if so id be willing to look at the code to see if i can learn something that i could use in windows.  

Link to comment
Share on other sites

34 minutes ago, joekingcool said:

agreed ! but after seeing all the updates in the beta, i can see how they have allot on there plate. 

but for now i think i could make a script/app to do the same. check every min and restart if failed. 

but im stuck with the cmdline of ffmpeg that's in the failed log file. and that cmd starts the recording back up.

1.but it dont stop when its suppose to too.

2.and it dont create another log file afterwards.

In my case, Emby still manages to kill the original ffmpeg program it started up which then will kill the child "real ffmpeg" that my script started.  I just made a wrapper that so Emby starts my script instead of ffmpeg itself.  I think if you could have Emby fire off your script/app instead of ffmpeg directly it would probably work for you too.  You don't need to worry about checking the log file until ffmpeg exits to see if it failed so the script/app wouldn't have to be a separate process running in the background the same time the real ffmpeg is running.

Link to comment
Share on other sites

3 minutes ago, joekingcool said:

did you post a script or plugin that your able to do this? i think i remember you saying you used linux? if so id be willing to look at the code to see if i can learn something that i could use in windows.  

Yes, I just posted it today in this topic.  I use Emby under Linux so I know it's not super helpful but thought it might give some ideas.

Link to comment
Share on other sites

joekingcool
3 minutes ago, Brian_M said:

Yes, I just posted it today in this topic.  I use Emby under Linux so I know it's not super helpful but thought it might give some ideas.

im going to check it out, thanks!  mostly need ffmpeg format i think which should be very similar windows/linux

Link to comment
Share on other sites

15 minutes ago, joekingcool said:

im going to check it out, thanks!  mostly need ffmpeg format i think which should be very similar windows/linux

You'll probably laugh at this but I did make this batch file for @iiiJoeto try out awhile back.  I had to convert it to an EXE with a BAT to EXE converter so Emby would run it as ffmpeg.exe.  I did have an Emby Windows server setup to test with and it seemed to work as expected.  You can have this print out all of the parameters for you.  It's only purpose was to change parameter 34 for the recording delay.

@echo off
set /a count=1
:repeat
    set /a count+=1
    set "params_%count%=%~1"
    shift
    if defined params_%count% (
        goto :repeat
    ) else (
        set /a count-=1
    )    
set /a params_0=count

setlocal enabledelayedexpansion
    for /l %%i in (1,1,!params_0!) do (
        echo params_%%i: "!params_%%i!"
    )

REM check graph file for the word recording and substitute param 34, reconnect_delay_max
set str1=%params_6%
if x%str1:recording=%==x%str1% goto not_recording

echo.
echo This is a recording...
echo.
echo Executing ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! "!params_6!" !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27! !params_28! !params_29!,!params_30! !params_31! !params_32! !params_33! 127 !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! "!params_54!" !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!

ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! "!params_6!" !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27! !params_28! !params_29!,!params_30! !params_31! !params_32! !params_33! 127 !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! "!params_54!" !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!

exit

:not_recording
echo.
echo This is not a recording...
echo.
echo Executing ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! !params_6! !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27!,!params_28! !params_29! !params_30! !params_31! !params_32! !params_33! !params_34! !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! !params_54! !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!
echo.

ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! !params_6! !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27!,!params_28! !params_29! !params_30! !params_31! !params_32! !params_33! !params_34! !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! !params_54! !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!

goto :eof

 

Link to comment
Share on other sites

joekingcool
6 minutes ago, Brian_M said:

You'll probably laugh at this

yes for so many reasons. lol  i just was thinking the language would be similar. this shows im not much of coder. also i just asked my buddy whos a powershell and bash guru to convert it. hes probably laughing at me right now. lol  i usually do autohotkey and muddle my way threw with allot of help from online. 

Link to comment
Share on other sites

10 minutes ago, joekingcool said:

yes for so many reasons. lol  i just was thinking the language would be similar. this shows im not much of coder. also i just asked my buddy whos a powershell and bash guru to convert it. hes probably laughing at me right now. lol  i usually do autohotkey and muddle my way threw with allot of help from online. 

Sounds like you've got some awesome help!  I'm not a programmer either by any means.  Unfortunately my Windows skills are very lacking.  It was a challenge just to make that work lol.  I hope you all are able to come up with something, gotta try to keep the wives/girlfriends happy! 

  • Agree 2
Link to comment
Share on other sites

joekingcool
20 minutes ago, Brian_M said:

It's only purpose was to change parameter 34 for the recording delay

oh ok... so you convert this batch to exe, and label exe as ffmpeg.exe . so that emby uses the custom ffmpeg which creates the real ffmpeg as a subprocess.  i seen where it was suggested by changing that param, that it help it stay alive. but i didnt know that solved it. 

i get "seek to failed" the rest emby seems to recover from.  

Link to comment
Share on other sites

7 minutes ago, joekingcool said:

oh ok... so you convert this batch to exe, and label exe as ffmpeg.exe . so that emby uses the custom ffmpeg which creates the real ffmpeg as a subprocess.  i seen where it was suggested by changing that param, that it help it stay alive. but i didnt know that solved it. 

i get "seek to failed" the rest emby seems to recover from.  

Yep, that's what it does.  That's as far as I got with it.  Unfortunately, changing that param does not fix the "seek to failed" message though.  😞

Link to comment
Share on other sites

Just now, joekingcool said:

does your bash script do the same thing?

It does change that param to 15, which ended up being a good number for me, but they key is it checks the log file after ffmpeg exits for "[q] command"  This is what Emby seems to send ffmpeg and is logged when the recording was successful.  You may see something different in Windows though, but I imagine it's quite similar.  If it fails for any other reason, like "seek to failed" or whatever, that will not be in the log file and the script will go through a loop where it tries to restart "real ffmpeg" for up to 10 times to finish the recording.  FYI, even if the recording finished successfully after a restart, you will never see the "[q] command" in the logs, but Emby manages to kill the ffmpeg process when the recording time is up anyway. 

Link to comment
Share on other sites

joekingcool
7 minutes ago, Brian_M said:

Emby manages to kill the ffmpeg process when the recording time is up anyway. 

you got my gears turning about this wrapper thing. to get the parms that emby is sending to ffmpeg. im going to look into that. i was running the cmd in the log file. which started the recording on same channel. but it didnt know when to stop...

Link to comment
Share on other sites

2 hours ago, Brian_M said:

You'll probably laugh at this but I did make this batch file for @iiiJoeto try out awhile back.  I had to convert it to an EXE with a BAT to EXE converter so Emby would run it as ffmpeg.exe.  I did have an Emby Windows server setup to test with and it seemed to work as expected.  You can have this print out all of the parameters for you.  It's only purpose was to change parameter 34 for the recording delay.

@echo off
set /a count=1
:repeat
    set /a count+=1
    set "params_%count%=%~1"
    shift
    if defined params_%count% (
        goto :repeat
    ) else (
        set /a count-=1
    )    
set /a params_0=count

setlocal enabledelayedexpansion
    for /l %%i in (1,1,!params_0!) do (
        echo params_%%i: "!params_%%i!"
    )

REM check graph file for the word recording and substitute param 34, reconnect_delay_max
set str1=%params_6%
if x%str1:recording=%==x%str1% goto not_recording

echo.
echo This is a recording...
echo.
echo Executing ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! "!params_6!" !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27! !params_28! !params_29!,!params_30! !params_31! !params_32! !params_33! 127 !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! "!params_54!" !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!

ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! "!params_6!" !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27! !params_28! !params_29!,!params_30! !params_31! !params_32! !params_33! 127 !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! "!params_54!" !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!

exit

:not_recording
echo.
echo This is not a recording...
echo.
echo Executing ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! !params_6! !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27!,!params_28! !params_29! !params_30! !params_31! !params_32! !params_33! !params_34! !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! !params_54! !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!
echo.

ffmpeg-bin.exe !params_2! !params_3! !params_4! !params_5! !params_6! !params_7! !params_8! !params_9! !params_10! !params_11! !params_12! !params_13! !params_14! !params_15! !params_16! !params_17! !params_18! "!params_19!" "!params_20!" "!params_21!" !params_22! !params_23! !params_24! !params_25! !params_26! !params_27!,!params_28! !params_29! !params_30! !params_31! !params_32! !params_33! !params_34! !params_35! !params_36! !params_37! !params_38! !params_39! !params_40! !params_41! !params_42! !params_43! !params_44! !params_45! !params_46! !params_47! !params_48! !params_49! !params_50! !params_51! !params_52! !params_53! !params_54! !params_55! !params_56! !params_57! !params_58! !params_59! !params_60! !params_61! !params_62! !params_63! !params_64! !params_65! !params_66! !params_67! !params_68! !params_69! !params_70! !params_71! !params_72! !params_73! !params_74! !params_75! !params_76! !params_77! !params_78! !params_79! !params_80! !params_81! !params_82! !params_83! !params_84! !params_85! !params_86! !params_87! !params_88! !params_89! !params_90! !params_91! !params_92! !params_93! !params_94! !params_95! !params_96! !params_97! !params_98! !params_99! !params_100! !params_101! !params_102! !params_103! !params_104! !params_105! !params_106! !params_107! !params_108! !params_109!

goto :eof

 

Really appreciated it too!!! Hoping we can get this ironed out 🙏

Link to comment
Share on other sites

joekingcool
20 hours ago, joekingcool said:

Emby manages to kill the ffmpeg process when the recording time is up anyway.

well i made a wrapper with ahk to see the params its passing threw. it shows them all. although emby keeps sending same cmd about every min or so. so im guessing thats its way of keeping it alive. also it didnt run hardware log correctly. i did get your batch/exe to work although not for the seek error. still not giving up yet 

Link to comment
Share on other sites

joekingcool

thought id post the few scripts i made to help for windows users in ahk. you can compile to exe or i can for you if you would like. 

1 deletes *.jpg *.nfo and empty folders from recordings folder.

dir= E:\Emby-Server\recordings

Loop, Files, %dir%\*.jpg, FR
FileDelete, %A_LoopFileFullPath%

Loop, Files, %dir%\*.nfo, FR
FileDelete, %A_LoopFileFullPath%

Loop, Files, % dir . "\*", DR
{
   if DllCall("Shlwapi\PathIsDirectoryEmpty", "Str", A_LoopFilePath)
      FileRemoveDir, %A_LoopFilePath%
}
if DllCall("Shlwapi\PathIsDirectoryEmpty", "Str", parentFolder)
   FileRemoveDir, %dir%

Loop, Files, % dir . "\*", DR
{
   if DllCall("Shlwapi\PathIsDirectoryEmpty", "Str", A_LoopFilePath)
      FileRemoveDir, %A_LoopFilePath%
}
if DllCall("Shlwapi\PathIsDirectoryEmpty", "Str", parentFolder)
   FileRemoveDir, %dir%

2. easly remove old recordings from embys timers.

sleep 3000
KEY := []
KEY[j, k] := A_LoopReadLine
KEYCount := 0
Loop, Read, %A_ScriptDir%\custom.txt
{
      KEYCount += 1
      KEY[KEYCount] := A_LoopReadLine
}
Loop % KEYCount
{
     element := KEY[A_Index]
}

password := KEY[3]

runwait, curl -X POST "http://localhost:8096/emby/System/Shutdown?api_key=%password%" ,,hide

Movies_New := ""
Movies_All := ""
Movies_Brief := ""
Recordings_All := ""
Recordings_Brief := ""
Find_Movie := ""
Find_Movies := "`"`":`"`"MV"
Fileread, file, % KEY[1]"\timers.json"
arr := Jxon_Load(file)

;----------------format ALL recordings, export to VAR Recordings_All----------------
for each, obj in arr			
		     Recordings_All .= "{`"`"ProviderIds`"`":{},`"`"SeriesProviderIds`"`":{},`"`"Tags`"`":[],`"`"Id`"`":`"`"" obj.Id "`"`",`"`"ChannelId`"`":`"`"" obj.ChannelId "`"`",`"`"ProgramId`"`":`"`"" obj.ProgramId "`"`",`"`"Name`"`":`"`""  obj.Name "`"`",`"`"Overview`"`":`"`"" obj.Overview "`"`",`"`"SeriesId`"`":`"`"" obj.SeriesId "`"`",`"`"StartDate`"`":`"`"" obj.StartDate "`"`",`"`"EndDate`"`":`"`"" obj.EndDate "`"`",`"`"Status`"`":`"`"" obj.Status "`"`",`"`"PrePaddingSeconds`"`":" obj.PrePaddingSeconds ",`"`"PostPaddingSeconds`"`":" obj.PostPaddingSeconds ",`"`"IsPrePaddingRequired`"`":false,`"`"IsPostPaddingRequired`"`":false,`"`"IsManual`"`":true,`"`"Priority`"`":0,`"`"RetryCount`"`":0,`"`"IsMovie`"`":true,`"`"IsKids`"`":false,`"`"IsSports`"`":false,`"`"IsNews`"`":false,`"`"IsSeries`"`":false,`"`"IsLive`"`":false,`"`"IsPremiere`"`":false,`"`"ProductionYear`"`":" obj.ProductionYear ",`"`"IsProgramSeries`"`":false,`"`"IsRepeat`"`":false,`"`"IsNew`"`":false,`"`"OfficialRating`"`":`"`"" obj.OfficialRating "`"`",`"`"Genres`"`":[],`"`"KeepUntil`"`":`"`"UntilDeleted`"`"},"                                                            "`n"

;--------find movieS and export to VAR Movie_All---------------
Loop, Parse, Recordings_All, `n, `r
   If instr(A_LoopField, Find_Movies)
	  Movies_All .= A_LoopField "`n"

;----------------Brief format ALL recordings, export to VAR Recordings_All----------------
for each, obj in arr			
		Recordings_Brief .=  obj.StartDate "`"`":`"`"" obj.SeriesId " " obj.Name            "`n"
;--------brief find movieS and export to VAR Movie_All---------------
Loop, Parse, Recordings_Brief, `n, `r
   If instr(A_LoopField, Find_Movies)
	  Movies_Brief .= A_LoopField "`n"

StringReplace, Movies_Brief, Movies_Brief, .0000000Z, , All
StringReplace, Movies_Brief, Movies_Brief, ":",  , All
Movies_Brief := RegExReplace(Movies_Brief, "MV\d{12}")
msgbox, %Movies_Brief%




begin:
Gui, Destroy
Gui, New
Gui, Add, Edit, w180 h20  vFind_Movie,
Gui, Add, Button, w180 h20 gRemove, Remove
gui, show, , THE GUI
return

GuiClose:
loc := KEY[2]
run, %loc%\EmbyServer.exe ,,hide
ExitApp

Remove:
Gui, Submit, NoHide
;--------find movie and remove from movieS and export to VAR Movies_New---------------

Loop, Parse, Movies_All, `n, `r
   If Not instr(A_LoopField, Find_Movie)
	  Movies_New .= A_LoopField "`n"

Movies_All := Movies_New
Movies_New := ""

StringReplace, json, Movies_All, `n, , All
StringTrimRight, json, json, 1
json = [%json%]
FileDelete, % KEY[1]"\timers.json"
FileAppend, %json%, % KEY[1]"\timers.json"

;--------find movie and remove from movieS and export to VAR Movies_New---------------
Loop, Parse, Movies_Brief, `n, `r
   If Not instr(A_LoopField, Find_Movie)
	  Movies_New .= A_LoopField "`n"

Movies_Brief := Movies_New
Movies_New := ""

StringReplace, Movies_Brief, Movies_Brief, .0000000Z, , All
StringReplace, Movies_Brief, Movies_Brief, ":", , All



msgbox, %Movies_Brief%
goto, begin




; -------------------------------------------------------------------------------------------------------------------------
; Coco's Jxon_Load function: https://github.com/cocobelgica/AutoHotkey-JSON/blob/master/Jxon.ahk
Jxon_Load(ByRef src, args*)
{
	static q := Chr(34)

	key := "", is_key := false
	stack := [ tree := [] ]
	is_arr := { (tree): 1 }
	next := q . "{[01234567890-tfn"
	pos := 0
	while ( (ch := SubStr(src, ++pos, 1)) != "" )
	{
		if InStr(" `t`n`r", ch)
			continue
		if !InStr(next, ch, true)
		{
			ln := ObjLength(StrSplit(SubStr(src, 1, pos), "`n"))
			col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1))

			msg := Format("{}: line {} col {} (char {})"
			,   (next == "")      ? ["Extra data", ch := SubStr(src, pos)][1]
			  : (next == "'")     ? "Unterminated string starting at"
			  : (next == "\")     ? "Invalid \escape"
			  : (next == ":")     ? "Expecting ':' delimiter"
			  : (next == q)       ? "Expecting object key enclosed in double quotes"
			  : (next == q . "}") ? "Expecting object key enclosed in double quotes or object closing '}'"
			  : (next == ",}")    ? "Expecting ',' delimiter or object closing '}'"
			  : (next == ",]")    ? "Expecting ',' delimiter or array closing ']'"
			  : [ "Expecting JSON value(string, number, [true, false, null], object or array)"
			    , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1]
			, ln, col, pos)

			throw Exception(msg, -1, ch)
		}

		is_array := is_arr[obj := stack[1]]

		if i := InStr("{[", ch)
		{
			val := (proto := args[i]) ? new proto : {}
			is_array? ObjPush(obj, val) : obj[key] := val
			ObjInsertAt(stack, 1, val)
			
			is_arr[val] := !(is_key := ch == "{")
			next := q . (is_key ? "}" : "{[]0123456789-tfn")
		}

		else if InStr("}]", ch)
		{
			ObjRemoveAt(stack, 1)
			next := stack[1]==tree ? "" : is_arr[stack[1]] ? ",]" : ",}"
		}

		else if InStr(",:", ch)
		{
			is_key := (!is_array && ch == ",")
			next := is_key ? q : q . "{[0123456789-tfn"
		}

		else ; string | number | true | false | null
		{
			if (ch == q) ; string
			{
				i := pos
				while i := InStr(src, q,, i+1)
				{
					val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C")
					static end := A_AhkVersion<"2" ? 0 : -1
					if (SubStr(val, end) != "\")
						break
				}
				if !i ? (pos--, next := "'") : 0
					continue

				pos := i ; update pos

				  val := StrReplace(val,    "\/",  "/")
				, val := StrReplace(val, "\" . q,    q)
				, val := StrReplace(val,    "\b", "`b")
				, val := StrReplace(val,    "\f", "`f")
				, val := StrReplace(val,    "\n", "`n")
				, val := StrReplace(val,    "\r", "`r")
				, val := StrReplace(val,    "\t", "`t")

				i := 0
				while i := InStr(val, "\",, i+1)
				{
					if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0
						continue 2

					; \uXXXX - JSON unicode escape sequence
					xxxx := Abs("0x" . SubStr(val, i+2, 4))
					if (A_IsUnicode || xxxx < 0x100)
						val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6)
				}

				if is_key
				{
					key := val, next := ":"
					continue
				}
			}

			else ; number | true | false | null
			{
				val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos)
			
			; For numerical values, numerify integers and keep floats as is.
			; I'm not yet sure if I should numerify floats in v2.0-a ...
				static number := "number", integer := "integer"
				if val is %number%
				{
					if val is %integer%
						val += 0
				}
			; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo,
			; SOMETIMES return strings due to certain optimizations. Since it
			; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a
				else if (val == "true" || val == "false")
					val := %value% + 0
			; AHK_H has built-in null, can't do 'val := %value%' where value == "null"
			; as it would raise an exception in AHK_H(overriding built-in var)
				else if (val == "null")
					val := ""
			; any other values are invalid, continue to trigger error
				else if (pos--, next := "#")
					continue
				
				pos += i-1
			}
			
			is_array? ObjPush(obj, val) : obj[key] := val
			next := obj==tree ? "" : is_array ? ",]" : ",}"
		}
	}

	return tree[1]
}

3. easy see what shows failed with seek to start error.

File := "E:\Emby-Server\programdata\logs\*.txt" 
StringCheck := "Seek to start failed"  
FileHit:=""              
path:=""
video:=""

Loop, %File%, , 1
{
   FileRead, FileCheck, %A_LoopFileLongPath%
   IfInString, FileCheck, %StringCheck% 
	{
	FileHit%A_Index% := A_LoopFilePath
	FileRead, string, %A_LoopFileLongPath%
	failed := BetweenWords(string, "-map_chapters -1 """, ".ts")
	path := failed[1]
	SplitPath, path, name
	video .=  name "`n"
	}	
}
Loop, 10000
{
   If (FileHit%A_Index% <> "")
      FileHit .= FileHit%A_Index% . "`n"
}



if (FileHit = "")
exitapp
if (FileHit != "")
msgbox, %video%
;MsgBox, % FileHit




BetweenWords(string, before, after) {
	arr := []
	template := "\Q" . before . "\E(.*?)\Q" . after . "\E"
	while pos := RegExMatch(string, template, Match, pos ? pos + StrLen(Match) : 1) {
		arr.Push(Match1)
	}
	Return arr
}

 

 

Link to comment
Share on other sites

On 12/12/2023 at 5:43 PM, joekingcool said:

3. easy see what shows failed with seek to start error.

Sorry guys but this is all Greek to me. Are you saying the script is showing why the “seek to start error” was triggered?

Link to comment
Share on other sites

joekingcool
8 hours ago, iiiJoe said:

Sorry guys but this is all Greek to me. Are you saying the script is showing why the “seek to start error” was triggered?

no it just shows you a list of shows that failed because of that error. i have it run every second then have it send me a text msg with another app i made. so i can restart as soon as possible. the reason is most likely the provider changing streams and/or recording shows back to back on same channel and you set the pre/post recording time. 

i still feel emby could monitor for this unique error like me and redo the recording from that point. with this error ffmpeg dont give a exit cmd and emby or ffmpeg dont try to restart. so its not the typical ffmpeg error. this is what ive learned over the past couple weeks. but not sure, im a noob as well. 

Link to comment
Share on other sites

2 hours ago, joekingcool said:

Amazing work!! I have a few questions. Does this create a new recording? Or, does it just add to the existing recording file? Are you using this on Windows? When it detects a failed recording is the entire Emby program closed and re-started or does it simply re-start the recording. Just wondering if this affects others users on the server during the re-start. What has been your success rate on re-starting “seek to start failed” recordings? Again, amazing job!!

Edited by iiiJoe
Link to comment
Share on other sites

joekingcool

thanks! it creates new recording like emby normally would in the same format -1 -2 etc. but i have a script when you run it, its searches threw record folder and uses ffmpeg to splice together. having figured out proper logic yet to integrate them both together yet, but soon i assume , time permitting. yes using on windows 10 updated. yes when it detects it shutdown emby so it can modify the timers.json file that emby uses for recordings, then starts back up. my provider noticed i was testing multiple stream earlier in the testing period and limited me back to 5 streams at time. so recently i tested 3 stream multiple times and less than minute later they where all recording again. yesterday the failure rate was high. today i think i worked them all out. did multiple dozens in row, that it all successfully restarted. not saying its perfect but it should be pretty close. you can hold off for month or 2 and wait to see long term or test it out your self. i created a simple firewall rule in windows to block ffmpeg outgoing. togged on/off blocking ffmpeg to trigger failed start error in log. then watched it fix in less then minute. ive spent all today and most yesterday testing different types of situations the videos file format would be in. then using allot of regex to format so it would match  with what's in the json file. 

btw it needs to be placed next to the system folder for emby and the recording folder needs to be next to the system folder. that's just how i have it setup on my pc. but uses relative placement so it can be portable. i needed emby to be run as a service for couple reasons. easier safe shutdown / startup and in my app if i shutdown like others on here using curl i was fine, but when i started it back up it was seen running under my app, which then made it a problem for the app to startup and shutdown. plus the other way the user would have to generate an api key bla bla :) sorry im just excited to not have to hear multiple times a week another failed recording .... 

restarting the recording independently of emby was a nightmare to do reliably. the other scripts i have does a great job at spicing them together.

Edited by joekingcool
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...