0,0 → 1,878 |
#!/usr/bin/php |
<?php |
|
// ViaThinkSoft YouTube Downloader Util 2.1 |
// Revision: 2018-04-16 |
// Author: Daniel Marschall <www.daniel-marschall.de> |
// Licensed under the terms of GPLv3 |
// |
// Syntax: |
// ./ytdwn [-t|--type v:[ext]|a:[ext]] (default v:) |
// [-o|--outputDir <dir>] (default current working directory) |
// [-a|--alreadyDownloaded <file>] |
// [-f|--failList <file> <treshold>] (This file logs failures) |
// [-F|--failTreshold <num>] (Don't download if failure (-f) treshold is reached. Default: 3) |
// [-V|--version] (shows version) |
// [-v|--verbose] (displays verbose information to STDOUT) |
// [-h|--help] (shows help) |
// [-N|--no-mp3-tagtransfer] (disables transfer of video ID to MP3 ID tag) |
// (This feature requires the package "id3v2") |
// [-T|--default-template <t>] (Sets default filename template.) |
// (Default: '%(title)s-%(id)s.%(ext)s') |
// [-X|--extra-args <args>] (Additional arguments passed through) |
// (youtube-dl. Default "-ic") |
// [-A|--api-key <file|key>] (specifies the API key, or a file containing the API key) |
// (Default: ~/.yt_api_key) |
// [-C|--resultcache <file>] (allows video results to be cached in this file) |
// (only for playlists or channels) |
// [-O|--create-outputdir] (allows creation of the output directories, recursively) |
// [--] |
// <resource> [<resource> ...] |
// |
// For all paths (outputDir, alreadyDownloaded, apikey, failList and resultcache), you can use the |
// term '[listname]' which will be replaced by the basename of the current list file (without file extension). |
// For example you can do following: |
// ./ytdwn -o 'downloads/[listname]' -- list:*.list |
// If no list file is processed, it will be replaced with nothing. |
// |
// The "alreadyDownloaded" argument contains a file which will be managed by ytdwn. |
// It will contain all video IDs which have been downloaded. This allows you to |
// move away the already downloaded files, and ytdwn will not download them again. |
// |
// Examples for type: |
// v: best video quality |
// a: best audio only |
// a:mp3 audio only, mp3 |
// Valid audio formats according to "man youtube-dl": |
// "best", "aac", "flac", "mp3", "m4a", "opus", "vorbis", or "wav"; "best" by default |
// |
// A <resource> can be one of the following: |
// vid:<video ID> |
// vurl:<youtube video URL> |
// pid:<playlist ID> |
// purl:<playlist URL> |
// cid:<channel id> |
// cname:<channel name> |
// curl:<channel or username URL> |
// list:<file with resource entries> (comments can be #) |
// search:<searchterm> |
// |
// For channels (cid, cname, curl) you can also perform a search to filter the results. |
// This can be done like this: |
// cname:[search="Elvis Presley"]channel_1234 |
// For the search option, following parameters are possible: |
// search:[order=date][maxresults=50]"Elvis Presley" |
// Acceptable order values are: date, rating, relevance, title, videoCount, viewCount |
// Default values are order=relevance and maxresults=10 |
// Use maxresults=-1 to download everything which matches the searchterm. |
// |
// Requirements: |
// - PHP CLI |
// - Package "youtube-dl" (ytdwn will try to download it automatically, if possible) |
// - A YouTube API key (can be obtained here: https://console.developers.google.com/apis/credentials ) |
// - If you want to extract audio, you need additionally: ffmpeg or avconv and ffprobe or avprobe. |
// - Optional: package "id3v2" to allow the YouTube video id to be transferred to the MP3 ID tag |
|
// ------------------------------------------------------------------------------------------------ |
|
error_reporting(E_ALL | E_NOTICE | E_STRICT | E_DEPRECATED); |
|
define('AUTO_API_KEY', '~/.yt_api_key'); |
define('DOWNLOAD_SIMULATION_MODE', false); |
define('DEFAULT_SEARCH_ORDER', 'relevance'); |
define('DEFAULT_SEARCH_MAXRESULTS', 10); |
|
putenv("LANG=de_DE.UTF-8"); // required if video titles contain non-ASCII symbols |
|
require_once __DIR__ . '/youtube_functions.inc.phps'; |
|
// Check if we are running in command line |
|
if (PHP_SAPI !== 'cli') { |
fwrite(STDERR, "Error: Can only run in CLI mode\n"); |
exit(2); |
} |
|
// Global vars |
|
$listFilenameStack = array(); |
|
// Default values |
|
$allow_creation_outputdir = false; |
$type = 'v:'; |
$outputDir = ''; |
$alreadyDownloaded = ''; |
$failList = ''; |
$failTreshold = 3; |
$rest_args = array(); |
$verbose = false; |
$mp3id_transfer = true; |
$apikey = ''; |
$resultcache = ''; |
$extra_args = |
// '-k ' . // The additional "-k" option in the above makes youtube-dl keep downloaded videos. |
'-i ' . // continue upon download errors |
'-c '; // resume partially downloaded video files |
$default_template = '%(title)s-%(id)s.%(ext)s'; |
|
// Parse arguments |
// We do not use getopt() at the moment, because the important functionality "optind" is only available in PHP 7.1, which is not yet distributed with most of the stable Linux distros |
|
$init_extra_args = false; |
$argv_bak = $argv; |
array_shift($argv_bak); |
while (count($argv_bak) > 0) { |
$arg = array_shift($argv_bak); |
$arg2 = $arg . ' ' . (isset($argv_bak[0]) ? $argv_bak[0] : ''); |
if (preg_match('@^(/t|\-t|\-\-type)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$type = $m[3]; |
} else if (preg_match('@^(/o|\-o|\-\-outputDir)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$outputDir = $m[3]; |
} else if (preg_match('@^(/a|\-a|\-\-alreadyDownloaded)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$alreadyDownloaded = $m[3]; |
} else if (preg_match('@^(/f|\-f|\-\-failList)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$failList = $m[3]; |
} else if (preg_match('@^(/F|\-F|\-\-failTreshold)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$failTreshold = $m[3]; |
} else if (preg_match('@^(/C|\-C|\-\-resultcache)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$resultcache = $m[3]; |
} else if (preg_match('@^(/T|\-T|\-\-default-template)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$default_template = $m[3]; |
} else if (preg_match('@^(/A|\-A|\-\-api-key)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$apikey = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3]; |
} else if (preg_match('@^(/X|\-X|\-\-extra-args)(\s+|=)(.*)$@s', $arg2, $m)) { |
array_shift($argv_bak); |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
if ($init_extra_args) { |
$extra_args .= ' ' . $m[3]; // user has multiple "-X" arguments. append. |
} else { |
$extra_args = $m[3]; // overwrite defaults |
$init_extra_args = true; |
} |
} else if (preg_match('@^(/\?|/h|\-\?|\-h|\-\-help)$@s', $arg, $m)) { |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
help(); |
} else if (preg_match('@^(/V|\-V|\-\-version)$@s', $arg, $m)) { |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
version(); |
} else if (preg_match('@^(/v|\-v|\-\-verbose)$@s', $arg, $m)) { |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$verbose = true; |
} else if (preg_match('@^(/N|\-N|\-\-no-mp3-tagtransfer)$@s', $arg, $m)) { |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$mp3id_transfer = false; |
} else if (preg_match('@^(/O|\-O|\-\-create-outputdir)$@s', $arg, $m)) { |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$allow_creation_outputdir = true; |
} else if ($arg == '--') { |
if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]); |
$rest_args = $argv_bak; |
break; |
} else { |
$rest_args[] = $arg; |
} |
} |
unset($arg); |
unset($argv_bak); |
unset($init_extra_args); |
|
// Validity checks |
|
if ((substr($type,0,2) != 'a:') && (substr($type,0,2) != 'v:')) syntax_error("Type must be either 'v:' or 'a:'. '$type' is not valid."); |
|
if (count($rest_args) == 0) syntax_error("Please enter at least one desired video for downloading"); |
|
if ($failTreshold <= 0) syntax_error("Fail treshold has invalid value. Must be >0."); |
|
// Try to install youtube-dl, if it does not exist |
|
if (command_exists(__DIR__.'/youtube-dl')) { |
define('YTDL_EXE', __DIR__.'/youtube-dl'); |
} else if (command_exists('youtube-dl')) { |
define('YTDL_EXE', 'youtube-dl'); |
} else { |
@file_put_contents(__DIR__.'/youtube-dl', file_get_contents('https://yt-dl.org/downloads/latest/youtube-dl')); |
@chmod(__DIR__.'/youtube-dl', 0544); |
if (command_exists(__DIR__.'/youtube-dl')) { |
define('YTDL_EXE', __DIR__.'/youtube-dl'); |
} else { |
fwrite(STDERR, "This script requires the tool/package 'youtube-dl'. Please install it first.\n"); |
exit(1); |
} |
} |
|
// Now process the videos |
|
yt_set_apikey_callback('_getApikey'); |
|
foreach ($rest_args as $resource) { |
if ($verbose) echo "Handle: $resource\n"; |
if (strpos($resource, ':') === false) { |
fwrite(STDERR, "Invalid resource '$resource' (you are missing the prefix, e.g. vurl: or vid:). Skipping.\n"); |
} else { |
list($resourceType, $resourceValue) = explode(':', $resource, 2); |
ytdwn_handle_resource($resourceType, $resourceValue); |
} |
} |
|
// ------------------------------------------------------------------------------------------------ |
|
function ytdwn_handle_resource($resourceType, $resourceValue) { |
if ($resourceType == 'vid') { |
$video_id = parse_quoting($resourceValue); |
ytdwn_video_id($video_id); |
} else if ($resourceType == 'vurl') { |
$video_url = parse_quoting($resourceValue); |
$video_id = getVideoIDFromURL($video_url); |
if (!$video_id) { |
fwrite(STDERR, "$video_url is not a valid YouTube video URL. Skipping.\n"); |
} else { |
ytdwn_video_id($video_id); |
} |
} else if ($resourceType == 'pid') { |
$playlist_id = parse_quoting($resourceValue); |
ytdwn_playlist_id($playlist_id); |
} else if ($resourceType == 'purl') { |
$playlist_url = parse_quoting($resourceValue); |
$playlist_id = getPlaylistIDFromURL($playlist_url); |
if (!$playlist_id) { |
fwrite(STDERR, "$playlist_url is not a valid YouTube playlist URL. Skipping\n"); |
} else { |
ytdwn_playlist_id($playlist_id); |
} |
} else if ($resourceType == 'cid') { |
$channel_id = parse_quoting($resourceValue); |
|
if (preg_match('@\[search=(.+)\]@ismU', $channel_id, $m)) { |
$search = $m[1]; |
$channel_id = preg_replace('@\[search=(.+)\]@ismU', '', $channel_id); |
} else { |
$search = ''; // default |
} |
$search = parse_quoting($search); |
|
ytdwn_channel_id($channel_id, $search); |
} else if ($resourceType == 'cname') { |
$channel_name = parse_quoting($resourceValue); |
|
if (preg_match('@\[search=(.+)\]@ismU', $channel_name, $m)) { |
$search = $m[1]; |
$channel_name = preg_replace('@\[search=(.+)\]@ismU', '', $channel_name); |
} else { |
$search = ''; // default |
} |
$search = parse_quoting($search); |
|
$channel_name = parse_quoting($channel_name); |
$channel_id = yt_get_channel_id($channel_name); |
if (!$channel_id) { |
fwrite(STDERR, "URL $channel_name is a valid YouTube channel or username. Skipping.\n"); |
} else { |
ytdwn_channel_id($channel_id, $search); |
} |
} else if ($resourceType == 'curl') { |
$channel_url = parse_quoting($resourceValue); |
|
if (preg_match('@\[search=(.+)\]@ismU', $channel_url, $m)) { |
$search = $m[1]; |
$channel_url = preg_replace('@\[search=(.+)\]@ismU', '', $channel_url); |
} else { |
$search = ''; // default |
} |
$search = parse_quoting($search); |
|
$channel_url = parse_quoting($channel_url); |
$channel_id = curl_to_cid($channel_url); |
if (!$channel_id) { |
fwrite(STDERR, "URL $channel_url is a valid YouTube channel oder username URL. Skipping\n"); |
} else { |
ytdwn_channel_id($channel_id, $search); |
} |
} else if ($resourceType == 'search') { |
$searchterm = parse_quoting($resourceValue); |
|
$order = ''; |
if (preg_match('@\[order=(.+)\]@ismU', $searchterm, $m)) { |
$order = $m[1]; |
$searchterm = preg_replace('@\[order=(.+)\]@ismU', '', $searchterm); |
} else { |
$order = DEFAULT_SEARCH_ORDER; // default |
} |
$order = parse_quoting($order); |
|
$maxresults = ''; |
if (preg_match('@\[maxresults=(.+)\]@ismU', $searchterm, $m)) { |
$maxresults = $m[1]; |
$searchterm = preg_replace('@\[maxresults=(.+)\]@ismU', '', $searchterm); |
} else { |
$maxresults = DEFAULT_SEARCH_MAXRESULTS; // default |
} |
$maxresults = parse_quoting($maxresults); |
|
$searchterm = parse_quoting($searchterm); |
|
ytdwn_search($searchterm, $order, $maxresults); |
} else if ($resourceType == 'list') { |
$list_files = glob(parse_quoting($resourceValue)); // in case the user entered a wildcard, e.g. *.list |
foreach ($list_files as $list_file) { |
if (!file_exists($list_file)) { |
fwrite(STDERR, "List file $list_file does not exist. Skipping\n"); |
} else { |
ytdwn_list_file($list_file); |
} |
} |
} else { |
fwrite(STDERR, "Resource type '$resourceType' is not valid. Skipping $resourceType:$resourceValue.\n"); |
} |
} |
|
function ytdwn_list_file($list_file) { |
global $listFilenameStack, $verbose; |
|
if ($verbose) echo "Processing list file '$list_file' ...\n"; |
|
$listFilenameStack[] = $list_file; |
$lines = file($list_file); |
foreach ($lines as $line) { |
$line = trim($line); |
if ($line == '') continue; |
if ($line[0] == '#') continue; |
if (strpos($line, ':') === false) { |
fwrite(STDERR, "Invalid resource '$line' (you are missing the prefix, e.g. vurl: or vid:). Skipping.\n"); |
} else { |
list($resourceType, $resourceValue) = explode(':',$line,2); |
ytdwn_handle_resource($resourceType, $resourceValue); |
} |
} |
array_pop($listFilenameStack); |
} |
|
function ytdwn_channel_id($channel_id, $search='') { |
global $type; |
global $verbose; |
|
if ($verbose) echo "Processing channel ID '$channel_id' ...\n"; |
|
// List the videos of the channel |
|
$cont = !empty(_getResultcache()) && file_exists(_getResultcache()) ? file_get_contents(_getResultcache()) : ''; |
$out = json_decode($cont, true); |
if ($out == NULL) $out = array(); |
|
if (!empty(_getResultcache())) { |
$stats = yt_get_channel_stats($channel_id); |
$videocount = $stats['videoCount']; |
|
$key = (!empty($search)) ? 'cid:'.$channel_id.'/'.$search : 'cid:'.$channel_id; |
|
if (!isset($out[$key])) $out[$key] = array(); |
$videocount_old = isset($out[$key]['count']) ? $out[$key]['count'] : -1; |
} else { |
$videocount = -1; |
$videocount_old = -2; |
$key = ''; |
} |
|
if ($videocount_old != $videocount) { // Attention: This might not work if videos are deleted and added (= the count stays the same) |
if ($verbose && !empty(_getResultcache())) echo "Video count changed from $videocount_old to $videocount\n"; |
$out[$key]['count'] = $videocount; |
if (!empty($search)) { |
$out[$key]['results'] = yt_channel_items($channel_id, $search); |
} else { |
$out[$key]['results'] = yt_channel_items($channel_id); |
} |
} else { |
if ($verbose) echo "Video count for channel is still $videocount, keep ".count($out[$key]['results'])." results.\n"; |
} |
|
// Save the cache |
|
try { |
if (!empty(_getResultcache())) file_put_contents(_getResultcache(), json_encode($out)); |
} catch(Exception $e) { |
fwrite(STDERR, "Cannot write result cache\n"); |
} |
|
// Now download |
|
foreach ($out[$key]['results'] as list($id, $title)) { |
if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n"; |
ytdwn_video_id($id); |
} |
} |
|
function ytdwn_playlist_id($playlist_id) { |
global $type; |
global $verbose; |
|
if ($verbose) echo "Processing playlist ID '$playlist_id' ...\n"; |
|
// List the videos of the playlist |
|
$cont = !empty(_getResultcache()) && file_exists(_getResultcache()) ? file_get_contents(_getResultcache()) : ''; |
$out = json_decode($cont, true); |
if ($out == NULL) $out = array(); |
|
if (!empty(_getResultcache())) { |
$stats = yt_get_playlist_stats($playlist_id); |
$videocount = $stats['itemCount']; |
|
$key = 'pid:'.$playlist_id; |
|
if (!isset($out[$key])) $out[$key] = array(); |
$videocount_old = isset($out[$key]['count']) ? $out[$key]['count'] : -1; |
} else { |
$videocount = -1; |
$videocount_old = -2; |
$key = ''; |
} |
|
if ($videocount_old != $videocount) { // Attention: This might not work if videos are deleted and added (= the count stays the same) |
if ($verbose && !empty(_getResultcache())) echo "Video count changed from $videocount_old to $videocount\n"; |
$out[$key]['count'] = $videocount; |
$out[$key]['results'] = yt_playlist_items($playlist_id); |
} else { |
if ($verbose) echo "Video count for playlist is still $videocount, keep ".count($out[$key]['results'])." results.\n"; |
} |
|
// Save the cache |
|
try { |
if (!empty(_getResultcache())) file_put_contents(_getResultcache(), json_encode($out)); |
} catch(Exception $e) { |
fwrite(STDERR, "Cannot write result cache\n"); |
} |
|
// Now download |
|
foreach ($out[$key]['results'] as list($id, $title)) { |
if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n"; |
ytdwn_video_id($id); |
} |
} |
|
function ytdwn_search($search, $order='', $maxresults=-1) { |
global $type; |
global $verbose; |
|
if ($verbose) echo "Searching for '$search' ...\n"; |
|
// Perform the search and list the videos |
|
$results = yt_search_items($search, $order, $maxresults); |
|
// Now download |
|
foreach ($results as list($id, $title)) { |
if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n"; |
ytdwn_video_id($id); |
} |
} |
|
function ytdwn_video_id($video_id) { |
global $type; |
global $verbose; |
global $mp3id_transfer; |
global $extra_args; |
global $default_template; |
global $failTreshold; |
|
if (DOWNLOAD_SIMULATION_MODE) { |
echo "SIMULATE download of video id $video_id as ".hf_type($type)." to "._getOutputDir()."\n"; |
return; |
} |
|
if (!empty(_getAlreadyDownloaded()) && in_alreadydownloaded_file($type, $video_id)) { |
if ($verbose) echo "Video $video_id has already been downloaded. Skip.\n"; |
return true; |
} |
|
if (!empty(_getFailList()) && (ytdwn_fail_counter($type, $video_id) >= $failTreshold)) { |
if ($verbose) echo "Video $video_id has failed too often. Skip.\n"; |
return true; |
} |
|
$out = ''; |
$code = -1; |
|
$outputTemplate = rtrim(_getOutputDir(), '/').'/'.$default_template; |
|
if (substr($type,0,2) == 'v:') { |
$format = substr($type,2); |
if (!empty($format)) { |
exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)).' --format '.escapeshellarg($format), $out, $code); |
} else { |
exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code); |
} |
} else if (substr($type,0,2) == 'a:') { |
$format = substr($type,2); |
if (!empty($format)) { |
exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio --audio-format '.escapeshellarg($format), $out, $code); |
} else { |
exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code); |
} |
if (($mp3id_transfer) && ($format == 'mp3')) mp3_transfer_vid_to_id(); |
} else assert(false); |
|
if ($code == 0) { |
if ($verbose) fwrite(STDOUT, "Successfully downloaded video ID $video_id as ".hf_type($type)."\n"); |
if (!empty(_getAlreadyDownloaded())) { |
try { |
addto_alreadydownloaded_file($type, $video_id); |
} catch(Exception $e) { |
fwrite(STDERR, "Cannot add to already downloaded file\n"); |
} |
} |
} else { |
fwrite(STDERR, "Error downloading $video_id! (Code $code)\n"); |
if (!empty(_getFailList())) { |
try { |
ytdwn_register_fail($type, $video_id, $code); |
} catch(Exception $e) { |
fwrite(STDERR, "Cannot register fail\n"); |
} |
} |
return false; |
} |
|
return true; |
} |
|
function vid_to_vurl($video_id) { |
return "http://www.youtube.com/watch/?v=$video_id"; |
} |
|
function EndsWith($Haystack, $Needle){ |
return strrpos($Haystack, $Needle) === strlen($Haystack)-strlen($Needle); |
} |
|
function mp3_transfer_vid_to_id() { |
global $verbose; |
global $default_template; |
|
if (!command_exists('id3v2')) { |
if ($verbose) echo "Note: Tool id3v2 is not installed. Will not transfer the YouTube ID into the MP3 ID Tag\n"; |
return false; |
} |
|
if (!EndsWith($default_template, '-%(id)s.%(ext)s')) { |
if ($verbose) echo "Note: Cannot transfer video tag to MP3 because default template does not end with '-%(id)s.%(ext)s'.\n"; |
return false; |
} |
|
$allok = true; |
$files = glob(rtrim(_getOutputDir(), '/').'/*-???????????.mp3'); |
foreach ($files as $filename) { |
if (!preg_match('@-([a-zA-Z0-9\-_]{11})\.mp3$@ismU', $filename, $m)) continue; |
$yt_id = $m[1]; |
|
if (!yt_check_video_id($yt_id)) continue; // just to be sure |
|
$orig_ts = filemtime($filename); |
system('id3v2 -c '.escapeshellarg($yt_id).' '.escapeshellarg($filename), $ec); |
touch($filename, $orig_ts); |
if ($ec != 0) { |
fwrite(STDERR, "Cannot set ID tag for file $filename\n"); |
$allok = false; |
continue; |
} |
|
$target_filename = str_replace("-$yt_id.mp3", '.mp3', $filename); |
if (!intelligent_rename($filename, $target_filename)) { |
fwrite(STDERR, "Cannot move file $filename to $target_filename\n"); |
$allok = false; |
continue; |
} |
} |
return $allok; |
} |
|
function curl_to_cid($channel_url) { |
if (preg_match("@https{0,1}://(www\\.|)youtube\\.com/user/(.*)(&|\\?)@ismU", $channel_url.'&', $m)) { |
$username = $m[2]; |
$channel_id = yt_get_channel_id($username); |
return $channel_id; |
} else if (preg_match("@https{0,1}://(www\\.|)youtube\\.com/channel/(.*)(&|\\?)@ismU", $channel_url.'&', $m)) { |
$channel_id = $m[2]; |
return $channel_id; |
} else { |
return false; |
} |
} |
|
function in_alreadydownloaded_file($type, $video_id) { |
$lines = file(_getAlreadyDownloaded()); |
foreach ($lines as $line) { |
if (trim($line) == rtrim($type,':').':'.$video_id) { |
return true; |
} |
} |
return false; |
} |
|
function addto_alreadydownloaded_file($type, $video_id) { |
file_put_contents(_getAlreadyDownloaded(), rtrim($type,':').':'.$video_id."\n", FILE_APPEND); |
} |
|
function syntax_error($msg) { |
fwrite(STDERR, "Syntax error: ".trim($msg)."\n"); |
fwrite(STDERR, "Please use argument '--help' to show the syntax rules.\n"); |
exit(2); |
} |
|
function _help() { |
global $argv; |
$out = ''; |
$own = file_get_contents($argv[0]); |
$help = explode('// ----', $own, 2)[0]; |
$help = preg_match_all('@^//(.*)$@mU', $help, $m); |
foreach ($m[1] as $line) { |
$out .= substr($line,1)."\n"; |
} |
return $out; |
} |
|
function help() { |
echo _help(); |
exit(0); |
} |
|
function version() { |
echo explode("\n\n", _help(), 2)[0]."\n"; |
exit(0); |
} |
|
function command_exists($command) { |
// https://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script |
|
$ec = -1; |
system('command -v '.escapeshellarg($command).' > /dev/null', $ec); |
return ($ec == 0); |
} |
|
function hf_type($type) { |
if (strpos($type, ':') === false) return $type; // invalid type (missing ':') |
list($av, $format) = explode(':', $type); |
|
if ($av == 'a') $av = 'audio'; |
else if ($av == 'v') $av = 'video'; |
else return $type; // invalid type |
|
return (!empty($format)) ? $format.'-'.$av : $av; |
} |
|
function expand_tilde($path) { |
// Source: http://jonathonhill.net/2013-09-03/tilde-expansion-in-php/ |
|
if (function_exists('posix_getuid') && strpos($path, '~') !== false) { |
$info = posix_getpwuid(posix_getuid()); |
$path = str_replace('~', $info['dir'], $path); |
} |
|
return $path; |
} |
|
function _getLastListname() { |
global $listFilenameStack; |
$listname = ''; // default |
if (count($listFilenameStack) > 0) { |
$listname = $listFilenameStack[count($listFilenameStack)-1]; |
$listname = pathinfo($listname, PATHINFO_FILENAME); // remove file extension, e.g. ".list" |
} |
return $listname; |
} |
|
function _getApiKey() { |
global $apikey; |
|
$out = $apikey; |
if (empty($out)) { |
$auto_api_key = AUTO_API_KEY; |
$auto_api_key = expand_tilde($auto_api_key); |
$auto_api_key = str_replace('[listname]', _getLastListname(), $auto_api_key); |
|
if (file_exists($auto_api_key)) { |
$out = trim(file_get_contents($auto_api_key)); |
} else { |
syntax_error("Please specify a YouTube API key with argument '-A'."); |
} |
} else { |
$out = str_replace('[listname]', _getLastListname(), $out); |
$out = expand_tilde($out); |
|
if (file_exists($out)) { |
$out = trim(file_get_contents($out)); |
} else { |
// Assume, $out is a key, not a file |
} |
} |
|
if (!yt_check_apikey_syntax($out)) syntax_error("'$out' is not a valid API key, not an existing file containing an API key.\n"); |
|
return $out; |
} |
|
function _getResultCache() { |
global $resultcache; |
if (empty($resultcache)) return ''; |
|
$out = expand_tilde($resultcache); |
|
$out = str_replace('[listname]', _getLastListname(), $out); |
|
if (!file_exists($out)) { |
@touch($out); |
if (!file_exists($out)) { |
fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n"); |
return ''; |
} |
} |
|
return $out; |
} |
|
function _getAlreadyDownloaded() { |
global $alreadyDownloaded; |
if (empty($alreadyDownloaded)) return ''; |
|
$out = expand_tilde($alreadyDownloaded); |
|
$out = str_replace('[listname]', _getLastListname(), $out); |
|
if (!file_exists($out)) { |
@touch($out); |
if (!file_exists($out)) { |
fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n"); |
return ''; |
} |
} |
|
return $out; |
} |
|
function _getFailList() { |
global $failList; |
if (empty($failList)) return ''; |
|
$out = expand_tilde($failList); |
|
$out = str_replace('[listname]', _getLastListname(), $out); |
|
if (!file_exists($out)) { |
@touch($out); |
if (!file_exists($out)) { |
fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n"); |
return ''; |
} |
} |
|
return $out; |
} |
|
function _getOutputDir() { |
global $outputDir, $allow_creation_outputdir; |
if (empty($outputDir)) return '.'; |
|
$out = expand_tilde($outputDir); |
|
$out = str_replace('[listname]', _getLastListname(), $out); |
|
if ($allow_creation_outputdir) { |
if (!is_dir($out)) { |
mkdir($out, true); |
if (!is_dir($out)) { |
fwrite(STDERR, "Output directory '$out' does not exist.\n"); |
exit(1); |
} |
} |
} else { |
if (!is_dir($out)) { |
fwrite(STDERR, "Output directory '$out' does not exist.\n"); |
exit(1); |
} |
} |
|
return $out; |
} |
|
function parse_quoting($str) { |
if ((substr($str,0,1) == '"') && (substr($str,-1,1) == '"')) { |
$str = substr($str,1,strlen($str)-2); |
|
$escape = false; |
$out = ''; |
for ($i=0; $i<strlen($str); $i++) { |
$char = $str[$i]; |
|
if ($char == '\\') { |
if ($escape) { |
$out .= $char; |
$escape = false; |
} else { |
$escape = true; |
} |
} else { |
$out .= $char; |
} |
|
} |
$str = $out; |
|
} |
|
return $str; |
} |
|
function ytdwn_register_fail($type, $video_id, $code) { |
// Note: Error code $code ist currently not used |
|
$file = _getFailList(); |
$cont = file_get_contents($file); |
if (preg_match("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", $cont, $m)) { |
$cont = preg_replace("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", |
"Video ID $video_id failed ".($m[1]+1)." time(s) with type $type", $cont); |
file_put_contents($file, $cont); |
} else { |
file_put_contents($file, "Video ID $video_id failed 1 time(s) with type $type\n", FILE_APPEND); |
} |
} |
|
function ytdwn_fail_counter($type, $video_id) { |
$file = _getFailList(); |
$cont = file_get_contents($file); |
if (preg_match("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", $cont, $m)) { |
return $m[1]; |
} else { |
return 0; |
} |
} |
|
function intelligent_rename($src, $dest) { |
$pos = strrpos($dest, '.'); |
$ext = substr($dest, $pos); |
$namewoext = substr($dest, 0, $pos); |
$failcnt = 1; |
$dest_neu = $dest; |
while (file_exists($dest_neu)) { |
$failcnt++; |
$dest_neu = "$namewoext ($failcnt)$ext"; |
} |
return rename($src, $dest_neu); |
} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |