Skip to content

Commit

Permalink
Update use pytube to get title, description duration and thumbs
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Neto committed Nov 27, 2024
1 parent b0543b7 commit 94a7044
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 20 deletions.
118 changes: 115 additions & 3 deletions objects/Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,19 +543,99 @@ static function isPythonAndPytubeInstalled()
return true;
}

public static function downloadWithPytube($video_url, $filename)
public static function getTitleFromLinkWithPytube($video_url)
{
global $global;

$downloadWithPytubeFilename = 'video_download_' . md5($video_url);
$metadataFile = "{$global['systemRootPath']}videos/pytube/{$downloadWithPytubeFilename}/metadata.json";
if(!file_exists($metadataFile)){
$response = self::downloadWithPytube($video_url, $downloadWithPytubeFilename, 'metadata');
}

if(file_exists($metadataFile)){
$content = file_get_contents($metadataFile);
$json = json_decode($content);
return $json->title;
}
return false;
}

public static function getDescriptionFromLinkWithPytube($video_url)
{
global $global;

$downloadWithPytubeFilename = 'video_download_' . md5($video_url);
$metadataFile = "{$global['systemRootPath']}videos/pytube/{$downloadWithPytubeFilename}/metadata.json";
if(!file_exists($metadataFile)){
$response = self::downloadWithPytube($video_url, $downloadWithPytubeFilename, 'metadata');
}

if(file_exists($metadataFile)){
$content = file_get_contents($metadataFile);
$json = json_decode($content);
return $json->description;
}
return false;
}

public static function getDurationFromLinkWithPytube($video_url)
{
global $global;

$downloadWithPytubeFilename = 'video_download_' . md5($video_url);
$metadataFile = "{$global['systemRootPath']}videos/pytube/{$downloadWithPytubeFilename}/metadata.json";
if(!file_exists($metadataFile)){
$response = self::downloadWithPytube($video_url, $downloadWithPytubeFilename, 'metadata');
}

if(file_exists($metadataFile)){
$content = file_get_contents($metadataFile);
$json = json_decode($content);
return $json->duration_seconds;
}
return false;
}

public static function getThumbsFromLinkWithPytube($video_url, $returnFileName = false)
{
global $global;

$downloadWithPytubeFilename = 'video_download_' . md5($video_url);
$File = "{$global['systemRootPath']}videos/pytube/{$downloadWithPytubeFilename}/thumbs.jpg";
if(!file_exists($File)){
$response = self::downloadWithPytube($video_url, $downloadWithPytubeFilename, 'thumbnail');
}

if(file_exists($File)){
if ($returnFileName) {
return $File;
} else {
$content = url_get_contents($File);
//unlink($returnTmpfname);
return $content;
}
}
return false;
}

public static function downloadWithPytube($video_url, $filename, $action = 'video')
{
global $global;

$pythonScript = $global['systemRootPath'] . "objects/youtube.py";
$command = escapeshellcmd("python3 $pythonScript " . escapeshellarg($video_url) . " " . escapeshellarg($filename));
$command = escapeshellcmd("python3 $pythonScript " . escapeshellarg($video_url) . " " . escapeshellarg($filename)." {$action}");
_error_log("downloadWithPytube($video_url, $filename) " . $command);
exec($command, $output, $return_var);

$response = new stdClass();
$response->command = $command;
$response->output = $output;
$response->error = $return_var !== 0;
$response->filename = $filename;
$response->metadata = "{$global['systemRootPath']}videos/pytube/{$response->filename}/metadata.json";
$response->thumbnail = "{$global['systemRootPath']}videos/pytube/{$response->filename}/thumbs.jpg";
$response->video = "{$global['systemRootPath']}videos/pytube/{$response->filename}/video.mp4";

if ($response->error) {
$response->msg = "Error downloading video. Check progress.json for details.";
Expand Down Expand Up @@ -2704,13 +2784,20 @@ public static function getReverseVideosJsonListFromLink($link, $streamers_id, $a

public static function getTitleFromLink($link, $streamers_id, $addOauthFromProvider = '')
{
if (self::isPythonAndPytubeInstalled() && isYouTubeUrl($link)) {
$resp = self::getTitleFromLinkWithPytube($link);
if(!empty($resp)){
return array('error' => false, 'output' => $resp);
}
}
$prepend = '';
if (!isWindows()) {
$prepend = 'LC_ALL=en_US.UTF-8 ';
}

$link = str_replace("'", '', $link);
$link = escapeshellarg($link);
$response = array('error' => true, 'output' => array());
$response = array('error' => true, 'output' => '');
$cmd = $prepend . self::getYouTubeDLCommand($addOauthFromProvider, $streamers_id) . " --no-check-certificate --no-playlist --force-ipv4 --skip-download -e {$link}";
exec($cmd . " 2>&1", $output, $return_val);
if ($return_val !== 0) {
Expand All @@ -2730,6 +2817,12 @@ public static function getTitleFromLink($link, $streamers_id, $addOauthFromProvi

public static function getDurationFromLink($link, $streamers_id, $addOauthFromProvider = '')
{
if (self::isPythonAndPytubeInstalled() && isYouTubeUrl($link)) {
$resp = self::getDurationFromLinkWithPytube($link);
if(!empty($resp)){
return static::parseSecondsToDuration($resp);
}
}
$link = escapeshellarg($link);
$cmd = self::getYouTubeDLCommand($addOauthFromProvider, $streamers_id) . " --no-check-certificate --no-playlist --force-ipv4 --get-duration --skip-download {$link}";
exec($cmd . " 2>&1", $output, $return_val);
Expand All @@ -2751,6 +2844,13 @@ public static function getDurationFromLink($link, $streamers_id, $addOauthFromPr

public static function getThumbsFromLink($link, $streamers_id, $returnFileName = false, $addOauthFromProvider = '')
{
if (self::isPythonAndPytubeInstalled() && isYouTubeUrl($link)) {
$resp = self::getThumbsFromLinkWithPytube($link, $returnFileName);
if(!empty($resp)){
return $resp;
}
}

$link = str_replace(array('"', "'"), array('', ''), $link);
$link = escapeshellarg($link);

Expand Down Expand Up @@ -2796,6 +2896,13 @@ public static function getDescriptionFromLink($link, $streamers_id, $addOauthFro
if (empty($link)) {
return '';
}

if (self::isPythonAndPytubeInstalled() && isYouTubeUrl($link)) {
$resp = self::getDescriptionFromLinkWithPytube($link);
if(!empty($resp)){
return $resp;
}
}
$link = escapeshellarg($link);
$tmpfname = _get_temp_file('thumbs');
$cmd = self::getYouTubeDLCommand($addOauthFromProvider, $streamers_id) . " --no-check-certificate --no-playlist --force-ipv4 --write-description --skip-download -o \"{$tmpfname}\" {$link}";
Expand Down Expand Up @@ -2836,6 +2943,11 @@ public static function streamerHasOauth($OauthFromProvider, $streamers_id = 0)
public static function getYouTubeDLCommand($addOauthFromProvider = '', $streamers_id = 0, $forceYoutubeDL = false)
{
global $global;
$cacheDir = '/var/www/.cache/';
if(!is_dir($cacheDir)){
@mkdir($cacheDir);
}

$ytdl = "youtube-dl ";
if (!empty($global['youtube-dl'])) {
$ytdl = $global['youtube-dl'] . ' ';
Expand Down
97 changes: 80 additions & 17 deletions objects/youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys
import subprocess
import urllib.request
from datetime import datetime, timedelta

# Function to ensure pytube is installed
def ensure_pytube_installed():
Expand Down Expand Up @@ -64,23 +65,50 @@ def patched_get_throttling_function_name(js: str) -> str:

def save_metadata(yt, folder):
metadata = {
"title": yt.title,
"description": yt.description,
"url": yt.watch_url
"title": yt.title if yt.title else "No Title",
"description": yt.description if yt.description else "No Description",
"url": yt.watch_url,
"duration_seconds": yt.length, # Add duration in seconds
"created_date": datetime.now().isoformat() # Track creation time
}
with open(os.path.join(folder, "metadata.json"), "w") as meta_file:
os.makedirs(folder, exist_ok=True)
metadata_file_path = os.path.join(folder, "metadata.json")
with open(metadata_file_path, "w") as meta_file:
json.dump(metadata, meta_file, indent=4)
print(f"Metadata saved successfully to '{metadata_file_path}'.")

def save_thumbnail(yt, folder):
thumbnail_url = yt.thumbnail_url
"""Save the highest resolution thumbnail available."""
video_id = yt.video_id
thumbnail_urls = [
f"https://img.youtube.com/vi/{video_id}/maxresdefault.jpg", # Highest resolution
f"https://img.youtube.com/vi/{video_id}/sddefault.jpg", # Standard definition
f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg", # High quality
f"https://img.youtube.com/vi/{video_id}/mqdefault.jpg", # Medium quality
yt.thumbnail_url # Default thumbnail
]

thumbnail_path = os.path.join(folder, "thumbs.jpg")
urllib.request.urlretrieve(thumbnail_url, thumbnail_path)
os.makedirs(folder, exist_ok=True)

for url in thumbnail_urls:
try:
urllib.request.urlretrieve(url, thumbnail_path)
print(f"Thumbnail downloaded successfully to '{thumbnail_path}' from URL: {url}")
return # Exit the loop on success
except Exception as e:
print(f"Failed to download thumbnail from '{url}': {e}")

print(f"Could not download any thumbnails for video '{yt.title}'.")



def download_video(yt, folder):
video_stream = yt.streams.get_highest_resolution()
video_path = os.path.join(folder, "video.mp4")
yt.register_on_progress_callback(lambda stream, chunk, bytes_remaining: save_progress(stream, bytes_remaining, folder))
video_stream.download(output_path=folder, filename="video.mp4")
print(f"Video downloaded successfully to '{video_path}'.")

def save_progress(stream, bytes_remaining, folder):
total_size = stream.filesize
Expand All @@ -90,28 +118,63 @@ def save_progress(stream, bytes_remaining, folder):
"downloaded": downloaded,
"progress": round((downloaded / total_size) * 100, 2)
}
with open(os.path.join(folder, "progress.json"), "w") as progress_file:
os.makedirs(folder, exist_ok=True)
progress_file_path = os.path.join(folder, "progress.json")
with open(progress_file_path, "w") as progress_file:
json.dump(progress, progress_file, indent=4)
print(f"Progress saved to '{progress_file_path}'.")

def clean_old_folders(base_folder, days=7):
"""Delete folders older than a specified number of days."""
now = datetime.now()
cutoff = now - timedelta(days=days)

for folder in os.listdir(base_folder):
folder_path = os.path.join(base_folder, folder)
if os.path.isdir(folder_path):
metadata_path = os.path.join(folder_path, "metadata.json")
if os.path.exists(metadata_path):
try:
with open(metadata_path, "r") as meta_file:
metadata = json.load(meta_file)
created_date = datetime.fromisoformat(metadata.get("created_date"))
if created_date < cutoff:
print(f"Deleting folder '{folder_path}' (created on {created_date})")
subprocess.call(["rm", "-rf", folder_path])
except Exception as e:
print(f"Error processing folder '{folder_path}': {e}")

def main():
if len(sys.argv) != 3:
print("Usage: python yt_downloader.py <YouTube_URL> <Folder_Name>")
if len(sys.argv) < 3:
print("Usage: python yt_downloader.py <YouTube_URL> <Folder_Name> [metadata|thumbnail|video|all]")
sys.exit(1)

# Get the directory where the script is located
script_dir = os.path.dirname(os.path.abspath(__file__))
base_folder = os.path.join(script_dir, '../videos/pytube/')
url = sys.argv[1]
folder_name = '../videos/pytube/'+sys.argv[2]
folder_name = os.path.join(base_folder, sys.argv[2])
action = sys.argv[3].lower() if len(sys.argv) > 3 else "video"

# Create the folder
os.makedirs(folder_name, exist_ok=True)

try:
# Download YouTube Video
yt = YouTube(url)
save_metadata(yt, folder_name)
save_thumbnail(yt, folder_name)
download_video(yt, folder_name)

print(f"Download completed. Files saved in '{folder_name}'.")
if action == "metadata":
save_metadata(yt, folder_name)
elif action == "thumbnail":
save_thumbnail(yt, folder_name)
elif action == "video":
download_video(yt, folder_name)
elif action == "all":
save_metadata(yt, folder_name)
save_thumbnail(yt, folder_name)
download_video(yt, folder_name)
else:
print("Invalid action specified. Use 'metadata', 'thumbnail', 'video', or 'all'.")

# Clean old folders after the operation
clean_old_folders(base_folder)
except Exception as e:
print(f"Error: {e}")

Expand Down

0 comments on commit 94a7044

Please # to comment.