Guest User

Untitled

a guest
Jun 2nd, 2018
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.57 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. # MYTHCUTKEY Version 0.1
  4. # This script cuts myth recordings at key frames using the MythTV seek table.
  5. # Why? No external programs required, lossless and fast.
  6. # Updates the myth database with sql calls including rebuilding the seek table.
  7. # Output files may NOT be easily editable again and may have glitches at cut points.
  8. #
  9. # Undo option allows restoration of original recording and associated database
  10. # records including seek table and markup table.
  11. # NOTE: The undo function requires a directory for which mysql has write permissions.
  12. # The default is /tmp/ however you will lose your undo information when you reboot.
  13. # Undo file names are the recording file base name with seekN.sql, markN.sql or recN.sql
  14. # appended, where N is a number starting from zero and the highest number indicating the
  15. # latest undo files. The undo files are not huge but may eat up disk space over time
  16. # after many edits.
  17. #
  18. # The script does not delete the original recording so you need disk free space equal
  19. # to something less than the original recording file size depending on how much you cut
  20. # from the file.
  21.  
  22. OVERWRITE=0
  23. OUTPUT_DIR=""
  24. CHANID=-9999
  25. STARTTIME=""
  26. rec_dir=""
  27. basename=""
  28. USER="root"
  29. PASS="" # Change this to "-p your_password" if necessary
  30. UNDO=0
  31. UNDO_DIR="/tmp/"
  32. STARTSECS=`date +%s`
  33.  
  34. USAGE="$0 -c channel_id -s starttime [-d dir] [-f filename] [-o directory] [-h] [-Z]
  35. [-u undo_dir] [-U]
  36. -h Help/usage
  37. -c Channel ID (required, %CHANID% from Mythtv)
  38. -s Start time (required, %STARTTIME% from Mythtv)
  39. -d Directory where the recording is store (optional, %DIR% from Mythtv)
  40. -f Recording file name (optional, %FILE% from Mythtv)
  41. -o Output directory (optional, recording directory if not specified)
  42. -Z Overwrite original recording (optional, default is false, ignored if -o specified)
  43. -u Directory where undo info is saved (optional, mysql must have permission to write
  44. to this directory, default is /tmp/)
  45. -U Undo the last edit
  46.  
  47. Example usage overwriting original recording (original is renamed, not deleted)
  48. mythcutkey -c %CHANID% -s %STARTTIME% -Z
  49. Example usage to save the file to a different directory
  50. mythcutnz -c %CHANID% -s %STARTTIME% -o /your/directory/
  51. Example usage specifying an undo directory
  52. mythcutnz -c %CHANID% -s %STARTTIME% -Z -u /myundodir
  53. Example usage to undo the last edit
  54. mythcutnz -c %CHANID% -s %STARTTIME% -U -u /myundodir
  55.  
  56. Warning: The end files may not be editable and/or playable outside MythTV. Keep the original
  57. recording if you think you may want to do further edits or conversions in future. This script does
  58. NOT delete the original recording. It is renamed with the extension \".old\"."
  59.  
  60.  
  61. while getopts "c:s:o:d:f:t:u:hZU" opt
  62. do
  63. case $opt in
  64. c )
  65. CHANID="$OPTARG"
  66. ;;
  67. s )
  68. STARTTIME="$OPTARG"
  69. ;;
  70. d )
  71. rec_dir="$OPTARG"
  72. ;;
  73. f )
  74. basename="$OPTARG"
  75. ;;
  76. h )
  77. echo "Usage: $USAGE" >&2
  78. exit 0
  79. ;;
  80. o )
  81. OUTPUT_DIR="$OPTARG"
  82. ;;
  83. u )
  84. UNDO_DIR="$OPTARG"
  85. ;;
  86. Z ) OVERWRITE=1 # Will be reset to false if -o directory is specified
  87. ;;
  88. U ) UNDO=1
  89. ;;
  90. ? )
  91. echo "Invalid option: -$OPTARG" >&2
  92. echo "Usage: $USAGE" >&2
  93. exit -1
  94. ;;
  95. esac
  96. done
  97.  
  98. if [ "$OUTPUT_DIR" != "" ]; then
  99. OVERWRITE=0
  100. fi
  101.  
  102. if [ $CHANID == -9999 -o "$STARTTIME" == "" ]; then
  103. echo "Channel ID and/or Start Time missing" >&2
  104. echo "Usage: $USAGE" >&2
  105. exit -1
  106. fi
  107.  
  108. #############################################################################
  109. # Find where the recording is stored if not specified with -d and -f options
  110. #############################################################################
  111.  
  112. if [ "$rec_dir" == "" -o "$basename" == "" ]; then
  113. storage_dirs=`echo "select dirname from storagegroup;" | mysql -N -u $USER $PASS mythconverg`
  114. basename=`echo "select basename from recorded where chanid=$CHANID and starttime=\"$STARTTIME\";" \
  115. | mysql -N -u $USER $PASS mythconverg`
  116. #echo $basename
  117. #echo $storage_dirs
  118.  
  119. found=0
  120. for f in $storage_dirs; do
  121. if [ -e "$f$basename" ]; then
  122. rec_dir=$f
  123. found=1
  124. fi
  125. done
  126. echo $rec_dir$basename
  127.  
  128. if [ $found == 0 ]; then
  129. echo "Can't find recording, aborting" >&2
  130. exit -1
  131. fi
  132. fi
  133.  
  134. # Set output directory to recording directory if not specified with -o option
  135. if [ "$OUTPUT_DIR" == "" ]; then
  136. OUTPUT_DIR=$rec_dir
  137. fi
  138.  
  139. if [ ! -d $UNDO_DIR ]; then
  140. echo "Can't find undo directory "$UNDO_DIR", aborting" >&2
  141. exit -1
  142. fi
  143.  
  144. #############################################################################
  145. # Undo the last edit if -U option specified
  146. #############################################################################
  147.  
  148. if [ $UNDO == 1 ]; then
  149. if [ ! -e $rec_dir$basename".old" ]; then
  150. echo "Can't find original recording "$rec_dir$basename".old, aborting" >&2
  151. exit -1
  152. fi
  153. # Don't delete. Just rename.
  154. mv $rec_dir$basename $rec_dir$basename".new"
  155. mv $rec_dir$basename".old" $rec_dir$basename
  156. # Undo various database changes
  157. num=0
  158. while [ -e $UNDO_DIR$basename.seek$num.sql ]; do
  159. let num++
  160. done
  161. let num--
  162. echo "delete from recordedseek where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  163. echo "load data infile \"$UNDO_DIR$basename.seek$num.sql\" into table recordedseek;" | mysql -u $USER $PASS mythconverg
  164. echo "delete from recordedmarkup where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  165. echo "load data infile \"$UNDO_DIR$basename.mark$num.sql\" into table recordedmarkup;" | mysql -u $USER $PASS mythconverg
  166. echo "delete from recorded where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  167. echo "load data infile \"$UNDO_DIR$basename.rec$num.sql\" into table recorded;" | mysql -u $USER $PASS mythconverg
  168. exit 0
  169. fi
  170.  
  171. #############################################################################
  172. # Edit the recording based on cuts at keyframes
  173. #############################################################################
  174.  
  175. thefile=$rec_dir$basename
  176. outfile=$OUTPUT_DIR$basename".new"
  177. MARKTYPES="0,1"
  178.  
  179. # Get the cuts from recordedmarkup table and store in an array
  180. cuts=( `echo "select type,mark from recordedmarkup where chanid=$CHANID and starttime=\"$STARTTIME\" \
  181. and type in ($MARKTYPES) order by mark" | mysql -N -u $USER $PASS mythconverg` )
  182.  
  183. i=0
  184. pieces=${#cuts[@]}
  185.  
  186. # Insert a record for the start of the recording if it does not exist
  187. if [ ${cuts[1]} != 0 ]; then
  188. let k=pieces-1
  189. while [ $k -gt 0 ]; do
  190. cuts[k+2]=${cuts[k]}
  191. cuts[k+1]=${cuts[k-1]}
  192. let k-=2
  193. done
  194. cuts[0]=0
  195. cuts[1]=0
  196. let pieces=pieces+2
  197. fi
  198.  
  199. # Check if last cut is not end of recording, i.e., we want to keep the end of the recording
  200. if [ ${cuts[pieces-2]} == 0 ]; then
  201. lastcut=${cuts[pieces-1]}
  202. lastseek=""
  203. lastseek=`echo "select mark from recordedseek where chanid=$CHANID and starttime=\"$STARTTIME\" and mark>$lastcut;" | \
  204. mysql -N -u $USER $PASS mythconverg | tail -n 1`
  205. if [ "$lastseek" != "" ]; then
  206. if [ $lastseek -gt $lastcut ]; then
  207. cuts[pieces]=1
  208. let cuts[pieces+1]=$lastseek+100 # Assume end of recording is within 100 frames of last seek table entry
  209. let pieces=pieces+2
  210. fi
  211. fi
  212. fi
  213.  
  214. let pieces=pieces-3
  215. k=0
  216. nextmark=0
  217. totaloffset=0
  218. part=1
  219. while [ $i -le $pieces ]; do
  220. if [ ${cuts[i]} == 0 -o ${cuts[i]} == 5 ]; then # Look for a cut end, i.e., the start of a segment we want to keep
  221. # Cuts at keyframes are actually keyframe+1 so fix
  222. cutstart=$((${cuts[i+1]}-1))
  223. key1=""
  224. while [ "$key1" == "" ]; do
  225. key1=`echo "select offset from recordedseek where mark=$cutstart and chanid=$CHANID and starttime=\"$STARTTIME\";" | \
  226. mysql -N -u $USER $PASS mythconverg`
  227. let cutstart--
  228. if [ $cutstart -le 0 ]; then
  229. key1=0
  230. cutstart=-1
  231. fi
  232. done
  233. cutend=$((${cuts[i+3]}-1))
  234. key2=""
  235. while [ "$key2" == "" ]; do
  236. key2=`echo "select offset from recordedseek where mark=$cutend and chanid=$CHANID and starttime=\"$STARTTIME\";" | \
  237. mysql -N -u $USER $PASS mythconverg`
  238. # Check if cutend is past the last record in the seek table
  239. lastseek=`echo "select mark from recordedseek where chanid=$CHANID and starttime=\"$STARTTIME\" order by mark desc limit 1;" | \
  240. mysql -N -u $USER $PASS mythconverg`
  241. if [ $cutend -gt $lastseek ]; then
  242. key2=`echo "select filesize from recorded where chanid=$CHANID and starttime=\"$STARTTIME\";" | \
  243. mysql -N -u $USER $PASS mythconverg`
  244. let key2--
  245. else
  246. let cutend++
  247. fi
  248. done
  249.  
  250. startmark[k]=$(($cutstart+1))
  251. endmark[k]=$(($cutend-1))
  252. markshift[k]=$(($cutstart+1-$nextmark))
  253. nextmark=$(($cutend-1-${markshift[k]})) # +1
  254. offsetshift[k]=$(($key1-$totaloffset))
  255. bytes=$(($key2-$key1)) # +188
  256. totaloffset=$(($totaloffset+$bytes))
  257.  
  258. let k++
  259.  
  260. echo "Segment start="$key1" bytes (frame="$(($cutstart+1))", cutpoint frame was "$((${cuts[i+1]}-1))")"
  261. echo "Segment end="$key2" bytes (frame="$(($cutend-1))", cutpoint frame was "$((${cuts[i+3]}-1))")"
  262.  
  263. kblocks=100
  264. blocksize=$((1024*$kblocks))
  265. blockstart=$((($key1+1)/$blocksize+1))
  266. pre=$(($blockstart*$blocksize-$key1))
  267. blocks=$((($bytes-$pre)/$blocksize))
  268. post=$(($bytes-$pre-$blocks*$blocksize))
  269. poststart=$((($blockstart+$blocks)*$blocksize))
  270.  
  271. echo "Total bytes in segment="$bytes" ="$pre" bytes+"$blocks" blocks+"$post" bytes"
  272.  
  273. # Cut and paste append the segment with dd
  274. echo "Cutting and pasting segment "$part"a"
  275. if [ $part == 1 ]; then
  276. ionice -c3 dd ibs=1c obs=1K skip=$key1 count=$pre if=$thefile of=$outfile
  277. else
  278. ionice -c3 dd ibs=1c obs=1K skip=$key1 count=$pre if=$thefile of=$outfile oflag=append conv=notrunc
  279. fi
  280. echo "Cutting and pasting segment "$part"b"
  281. ionice -c3 dd bs=$kblocks"K" skip=$blockstart count=$blocks if=$thefile of=$outfile oflag=append conv=notrunc
  282. if [ $post -gt 0 ]; then
  283. echo "Cutting and pasting segment "$part"c"
  284. ionice -c3 dd ibs=1c obs=1c skip=$poststart count=$post if=$thefile of=$outfile oflag=append conv=notrunc
  285. fi
  286. let part++
  287. # Increment loop counter by 4 since array has cut start/end pairs plus cut types
  288. let i+=4
  289. else
  290. # Skips the case where the first cut point is a cut start, i.e., when start of recording is deleted
  291. let i+=2
  292. fi
  293. done
  294. segs=$k
  295.  
  296. #############################################################################
  297. # Reset the cut list, seek table, etc. for final file
  298. #############################################################################
  299.  
  300. finalfile=$OUTPUT_DIR$basename
  301. if [ $OVERWRITE == 1 ]; then
  302. # Don't delete original. Just rename.
  303. mv $rec_dir$basename $rec_dir$basename".old"
  304. mv $outfile $finalfile
  305. # Delete tmp file if it exists
  306. if [ -e $OUTPUT_DIR$basename".tmp" ]; then
  307. ionice -c3 rm $OUTPUT_DIR$basename".tmp"
  308. fi
  309.  
  310. # Backup various data so we can undo the changes
  311. num=0
  312. while [ -e $UNDO_DIR$basename.seek$num.sql ]; do
  313. let num++
  314. done
  315. echo "select * from recordedseek where chanid=$CHANID and starttime=\"$STARTTIME\" into outfile \"$UNDO_DIR$basename.seek$num.sql\";" | \
  316. mysql -u $USER $PASS mythconverg
  317. echo "select * from recordedmarkup where chanid=$CHANID and starttime=\"$STARTTIME\" into outfile \"$UNDO_DIR$basename.mark$num.sql\";" | \
  318. mysql -u $USER $PASS mythconverg
  319. echo "select * from recorded where chanid=$CHANID and starttime=\"$STARTTIME\" into outfile \"$UNDO_DIR$basename.rec$num.sql\";" | \
  320. mysql -u $USER $PASS mythconverg
  321.  
  322. # Clear the markup table
  323. echo "delete from recordedmarkup where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  324. # Clear bookmark, cutlist, commflagged flags
  325. echo "update recorded set bookmark=0, cutlist=0, commflagged=0 where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  326. # Update file size
  327. FILESIZE=`du -b $OUTPUT_DIR$basename | awk '{print $1}'`
  328. echo "update recorded set filesize=$FILESIZE where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  329. # Rebuild the seek table
  330. i=0
  331. while [ $i -lt $segs ]; do
  332. echo "Rebuilding seek table part "$i" of "$(($segs-1))
  333. if [ ${startmark[i]} -lt $((${endmark[i]}-${markshift[i]})) ]; then
  334. delmark=${startmark[i]}
  335. else
  336. delmark=$((${endmark[i]}-${markshift[i]}))
  337. fi
  338. echo "delete from recordedseek where chanid=$CHANID and starttime=\"$STARTTIME\" and \
  339. mark>=$((${startmark[i]}-${markshift[i]})) and mark<$delmark;" | mysql -u $USER $PASS mythconverg
  340. echo "update recordedseek set mark=mark-${markshift[i]}, offset=offset-${offsetshift[i]} \
  341. where chanid=$CHANID and starttime=\"$STARTTIME\" and mark>=${startmark[i]} and mark<${endmark[i]} order by mark;" | mysql -u $USER $PASS mythconverg
  342. let i++
  343. done
  344. echo "delete from recordedseek where mark>=$nextmark and chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
  345. else
  346. # Don't accidentally overwrite an existing file
  347. if [ -e $finalfile ]; then
  348. i=1
  349. while [ -e $finalfile"."$i".mpg" ]; do
  350. let i++
  351. done
  352. mv $outfile $finalfile"."$i".mpg"
  353. else
  354. mv $outfile $finalfile
  355. fi
  356. fi
  357.  
  358. ENDSECS=`date +%s`
  359. SECS=$(($ENDSECS-$STARTSECS))
  360. MINS=$(($SECS/60))
  361. SECS=$(($SECS-$MINS*60))
  362. echo "Elapsed time: "$MINS" minutes "$SECS" seconds"
Add Comment
Please, Sign In to add comment