Guest User

Untitled

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