Rev 14 | Rev 16 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 14 | Rev 15 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | #!/usr/bin/php |
1 | #!/usr/bin/php |
2 | <?php |
2 | <?php |
3 | 3 | ||
4 | // ViaThinkSoft YouTube Downloader Util 2.3 |
4 | // ViaThinkSoft YouTube Downloader Util 2.3 |
5 | // Revision: 2022-02-06 |
5 | // Revision: 2022-02-07 |
6 | // Author: Daniel Marschall <www.daniel-marschall.de> |
6 | // Author: Daniel Marschall <www.daniel-marschall.de> |
7 | // Licensed under the terms of the Apache 2.0 License |
7 | // Licensed under the terms of the Apache 2.0 License |
8 | // |
8 | // |
9 | // For syntax and other documentation, please read the file README. |
9 | // For syntax and other documentation, please read the file README. |
10 | 10 | ||
Line 489... | Line 489... | ||
489 | if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n"; |
489 | if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n"; |
490 | ytdwn_video_id($id); |
490 | ytdwn_video_id($id); |
491 | } |
491 | } |
492 | } |
492 | } |
493 | 493 | ||
- | 494 | function template_to_wildcard($template, $video_id) { |
|
- | 495 | $x = $template; |
|
- | 496 | $x = str_replace('%(id)s', $video_id, $x); |
|
- | 497 | $x = preg_replace('@%\\(.+\\)s@ismU', '*', $x); |
|
- | 498 | $x = preg_replace('@\\*+@', '*', $x); |
|
- | 499 | return $x; |
|
- | 500 | } |
|
- | 501 | ||
- | 502 | function ytdwn_get_downloaded_filename($outputTemplate, $video_id) { |
|
- | 503 | if (strpos($outputTemplate, '%(id)s') === false) { |
|
- | 504 | // TODO: There needs to be a better way to find out the written file name !!! |
|
- | 505 | return false; |
|
- | 506 | } else { |
|
- | 507 | $wildcard = template_to_wildcard($outputTemplate, $video_id); |
|
- | 508 | $test = glob($wildcard); |
|
- | 509 | if (count($test) == 0) return false; |
|
- | 510 | return $test[0]; |
|
- | 511 | } |
|
- | 512 | } |
|
- | 513 | ||
494 | function ytdwn_video_id($video_id) { |
514 | function ytdwn_video_id($video_id) { |
495 | global $type; |
515 | global $type; |
496 | global $verbose; |
516 | global $verbose; |
497 | global $mp3id_transfer; |
517 | global $mp3id_transfer; |
498 | global $extra_args; |
518 | global $extra_args; |
Line 526... | Line 546... | ||
526 | if (!empty($format)) { |
546 | if (!empty($format)) { |
527 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --format '.escapeshellarg($format), $out, $code); |
547 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --format '.escapeshellarg($format), $out, $code); |
528 | } else { |
548 | } else { |
529 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code); |
549 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code); |
530 | } |
550 | } |
- | 551 | ||
- | 552 | $written_file = $code == 0 ? ytdwn_get_downloaded_filename($outputTemplate, $video_id) : false; |
|
- | 553 | ||
531 | } else if (substr($type,0,2) == 'a:') { |
554 | } else if (substr($type,0,2) == 'a:') { |
532 | $format = substr($type,2); |
555 | $format = substr($type,2); |
533 | if (!empty($format)) { |
556 | if (!empty($format)) { |
534 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio --audio-format '.escapeshellarg($format), $out, $code); |
557 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio --audio-format '.escapeshellarg($format), $out, $code); |
535 | } else { |
558 | } else { |
536 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code); |
559 | exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code); |
537 | } |
560 | } |
- | 561 | ||
- | 562 | $written_file = $code == 0 ? ytdwn_get_downloaded_filename($outputTemplate, $video_id) : false; |
|
- | 563 | ||
538 | if (($mp3id_transfer) && ($format == 'mp3')) mp3_transfer_vid_to_id(); |
564 | if (($code == 0) && ($mp3id_transfer) && (strtolower($format) == 'mp3')) { |
- | 565 | if ($written_file === false) { |
|
- | 566 | fwrite(STDERR, "Cannot include YouTube ID to MP3, because the default template does not contain '%(id)s', or the downloaded file could not be determined for another reason.\n"); |
|
- | 567 | } else { |
|
- | 568 | mp3_transfer_vid_to_id($written_file, $video_id); |
|
- | 569 | } |
|
- | 570 | } |
|
- | 571 | } else { |
|
539 | } else assert(false); |
572 | assert(false); |
- | 573 | return false; |
|
- | 574 | } |
|
540 | 575 | ||
541 | if ($code == 0) { |
576 | if ($code == 0) { |
- | 577 | if ($verbose) { |
|
542 | if ($verbose) fwrite(STDOUT, "Successfully downloaded video ID $video_id as ".hf_type($type)."\n"); |
578 | fwrite(STDOUT, "Successfully downloaded video ID $video_id as ".hf_type($type)."\n"); |
- | 579 | if ($written_file !== false) fwrite(STDOUT, "Output file name: $written_file\n"); |
|
- | 580 | } |
|
543 | if (!empty(_getAlreadyDownloaded())) { |
581 | if (!empty(_getAlreadyDownloaded())) { |
544 | try { |
582 | try { |
545 | addto_alreadydownloaded_file($type, $video_id); |
583 | addto_alreadydownloaded_file($type, $video_id); |
546 | } catch(Exception $e) { |
584 | } catch(Exception $e) { |
547 | fwrite(STDERR, "Cannot add to 'already downloaded' file\n"); |
585 | fwrite(STDERR, "Cannot add to 'already downloaded' file\n"); |
548 | } |
586 | } |
549 | } |
587 | } |
550 | 588 | ||
551 | // Now do the checksums |
589 | // Now do the checksums |
552 | foreach (explode(',',$checksumMode) as $mode) { |
590 | foreach (explode(',',$checksumMode) as $mode) { |
553 | $test = glob(rtrim(_getOutputDir(), '/').'/*-'.$video_id.'.*'); |
591 | if (strtolower($mode) === 'none') continue; |
554 | if (count($test) == 0) { |
592 | if ($written_file === false) { |
555 | fwrite(STDERR, "Cannot determine output file name.\n"); |
593 | fwrite(STDERR, "Cannot add to the '$mode' checksum file, because the default template does not contain '%(id)s', or the downloaded file could not be determined for another reason.\n"); |
556 | } |
- | |
557 | $written_file = $test[0]; |
- | |
558 | if (!cs_add_automatically($written_file, $mode)) { |
594 | } else if (!cs_add_automatically($written_file, $mode)) { |
559 | fwrite(STDERR, "Could not write to '$mode' checksum file!\n"); |
595 | fwrite(STDERR, "Could not write to '$mode' checksum file!\n"); |
560 | } |
596 | } |
561 | } |
597 | } |
562 | } else { |
598 | } else { |
563 | fwrite(STDERR, "Error downloading $video_id! (Code $code)\n"); |
599 | fwrite(STDERR, "Error downloading $video_id! (Code $code)\n"); |
Line 580... | Line 616... | ||
580 | 616 | ||
581 | function EndsWith($Haystack, $Needle){ |
617 | function EndsWith($Haystack, $Needle){ |
582 | return strrpos($Haystack, $Needle) === strlen($Haystack)-strlen($Needle); |
618 | return strrpos($Haystack, $Needle) === strlen($Haystack)-strlen($Needle); |
583 | } |
619 | } |
584 | 620 | ||
585 | function mp3_transfer_vid_to_id() { |
621 | function mp3_transfer_vid_to_id(&$written_file, $video_id) { |
586 | global $verbose; |
622 | global $verbose; |
587 | global $default_template; |
623 | global $default_template; |
588 | 624 | ||
589 | if (!command_exists('id3v2')) { |
625 | if (!command_exists('id3v2')) { |
590 | if ($verbose) echo "Note: Tool id3v2 is not installed. Will not transfer the YouTube ID into the MP3 ID Tag\n"; |
626 | fwrite(STDERR, "Tool id3v2 is not installed. Will not transfer the YouTube ID into the MP3 ID Tag. Use paramter '-N' to stop trying the transfer.\n"); |
591 | return false; |
627 | return false; |
592 | } |
628 | } |
593 | 629 | ||
594 | if (!EndsWith($default_template, '-%(id)s.%(ext)s')) { |
630 | $orig_ts = filemtime($written_file); |
- | 631 | $ec = -1; |
|
595 | if ($verbose) echo "Note: Cannot transfer video tag to MP3 because default template does not end with '-%(id)s.%(ext)s'.\n"; |
632 | system('id3v2 -c '.escapeshellarg($video_id).' '.escapeshellarg($written_file), $ec); |
- | 633 | touch($written_file, $orig_ts); |
|
- | 634 | if ($ec != 0) { |
|
- | 635 | fwrite(STDERR, "Cannot set ID tag for file $written_file\n"); |
|
596 | return false; |
636 | return false; |
597 | } |
637 | } |
598 | 638 | ||
599 | $allok = true; |
- | |
600 | $files = glob(rtrim(_getOutputDir(), '/').'/*-???????????.mp3'); |
- | |
601 | foreach ($files as $filename) { |
639 | $target_filename = $written_file; |
602 | $m = null; |
- | |
603 | if (!preg_match('@-([a-zA-Z0-9\-_]{11})\.mp3$@ismU', $filename, $m)) continue; |
- | |
604 | $yt_id = $m[1]; |
- | |
605 | - | ||
606 | if (!yt_check_video_id($yt_id)) continue; // just to be sure |
- | |
607 | 640 | ||
- | 641 | // Things like '<title>-<id>.mp3' become '<title>.mp3' (our default template) |
|
- | 642 | // But templates like '<title> (<id>).mp3' could become '<title> ().mp3', which is not nice |
|
- | 643 | // So, we try our best to find the most common template types... |
|
608 | $orig_ts = filemtime($filename); |
644 | $target_filename = str_replace('-'.$video_id, '', $target_filename); |
609 | $ec = -1; |
645 | $target_filename = str_replace('_'.$video_id, '', $target_filename); |
- | 646 | $target_filename = str_replace(' '.$video_id, '', $target_filename); |
|
610 | system('id3v2 -c '.escapeshellarg($yt_id).' '.escapeshellarg($filename), $ec); |
647 | $target_filename = str_replace('('.$video_id.')', '', $target_filename); |
611 | touch($filename, $orig_ts); |
648 | $target_filename = str_replace('['.$video_id.']', '', $target_filename); |
- | 649 | $target_filename = str_replace($video_id, '', $target_filename); // must be the last! |
|
612 | if ($ec != 0) { |
650 | if ($target_filename === $written_file) { |
613 | fwrite(STDERR, "Cannot set ID tag for file $filename\n"); |
651 | fwrite(STDERR, "Could not remove VideoID from filename '$written_file'\n"); // should not happen |
614 | $allok = false; |
652 | return false; |
615 | continue; |
- | |
616 | } |
653 | } |
617 | 654 | ||
618 | $target_filename = str_replace("-$yt_id.mp3", '.mp3', $filename); |
- | |
619 | if (!intelligent_rename($filename, $target_filename)) { |
655 | if (!intelligent_rename($written_file, $target_filename)) { |
620 | fwrite(STDERR, "Cannot move file $filename to $target_filename\n"); |
656 | fwrite(STDERR, "Could not rename '$written_file' to '$target_filename'\n"); |
621 | $allok = false; |
657 | return false; |
622 | continue; |
- | |
623 | } |
- | |
624 | } |
658 | } |
- | 659 | ||
- | 660 | $written_file = $target_filename; // was modified by intelligent_rename() |
|
625 | return $allok; |
661 | return true; |
626 | } |
662 | } |
627 | 663 | ||
628 | function curl_to_cid($channel_url) { |
664 | function curl_to_cid($channel_url) { |
629 | return yt_get_channel_id_from_url($channel_url); |
665 | return yt_get_channel_id_from_url($channel_url); |
630 | } |
666 | } |
Line 877... | Line 913... | ||
877 | } else { |
913 | } else { |
878 | return 0; |
914 | return 0; |
879 | } |
915 | } |
880 | } |
916 | } |
881 | 917 | ||
882 | function intelligent_rename($src, $dest) { |
918 | function intelligent_rename($src, &$dest) { |
883 | $pos = strrpos($dest, '.'); |
919 | $pos = strrpos($dest, '.'); |
884 | $ext = substr($dest, $pos); |
920 | $ext = substr($dest, $pos); |
885 | $namewoext = substr($dest, 0, $pos); |
921 | $namewoext = substr($dest, 0, $pos); |
886 | $failcnt = 1; |
922 | $failcnt = 1; |
887 | $dest_neu = $dest; |
923 | $dest_neu = $dest; |
888 | while (file_exists($dest_neu)) { |
924 | while (file_exists($dest_neu)) { |
889 | $failcnt++; |
925 | $failcnt++; |
890 | $dest_neu = "$namewoext ($failcnt)$ext"; |
926 | $dest_neu = "$namewoext ($failcnt)$ext"; |
891 | } |
927 | } |
892 | return rename($src, $dest_neu); |
928 | $res = rename($src, $dest_neu); |
- | 929 | if ($res) $dest = $dest_neu; |
|
- | 930 | return $res; |
|
893 | } |
931 | } |
894 | 932 | ||
895 | function get_latest_ytdl_md5sum() { |
933 | function get_latest_ytdl_md5sum() { |
896 | $ch = curl_init(); |
934 | $ch = curl_init(); |
897 | curl_setopt($ch, CURLOPT_URL, 'https://yt-dl.org/downloads/latest/MD5SUMS'); |
935 | curl_setopt($ch, CURLOPT_URL, 'https://yt-dl.org/downloads/latest/MD5SUMS'); |