Skip to content

Commit

Permalink
[feature/ISSUE-25] Include itag and extension in filename
Browse files Browse the repository at this point in the history
  • Loading branch information
azihassan committed Oct 6, 2023
1 parent d6e7c4e commit b54e0e5
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 9 deletions.
5 changes: 3 additions & 2 deletions source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ void handleURL(string url, int itag, StdoutLogger logger, bool displayFormats, b

logger.display(parser.getID());
logger.display(parser.getTitle());
string filename = format!"%s-%s.mp4"(parser.getTitle(), parser.getID()).sanitizePath();
YoutubeFormat youtubeFormat = parser.getFormat(itag);
string filename = format!"%s-%s-%d.%s"(parser.getTitle(), parser.getID(), itag, youtubeFormat.extension).sanitizePath();
logger.displayVerbose(filename);
string destination = buildPath(getcwd(), filename);
logger.displayVerbose(destination);
Expand All @@ -112,7 +113,7 @@ void handleURL(string url, int itag, StdoutLogger logger, bool displayFormats, b
if(parallel)
{
logger.display("Using ParallelDownloader");
downloader = new ParallelDownloader(logger, parser.getID(), parser.getTitle());
downloader = new ParallelDownloader(logger, parser.getID(), parser.getTitle(), youtubeFormat);
}
else
{
Expand Down
14 changes: 9 additions & 5 deletions source/downloaders.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import std.range : iota;
import std.net.curl : Curl, CurlOption;
import helpers : getContentLength, sanitizePath, StdoutLogger, formatSuccess;

import parsers : YoutubeFormat;

interface Downloader
{
void download(string destination, string url, string referer);
Expand Down Expand Up @@ -58,12 +60,14 @@ class ParallelDownloader : Downloader
private StdoutLogger logger;
private string id;
private string title;
private YoutubeFormat youtubeFormat;

this(StdoutLogger logger, string id, string title)
this(StdoutLogger logger, string id, string title, YoutubeFormat youtubeFormat)
{
this.id = id;
this.title = title;
this.logger = logger;
this.youtubeFormat = youtubeFormat;
}

public void download(string destination, string url, string referer)
Expand All @@ -82,8 +86,8 @@ class ParallelDownloader : Downloader
{
ulong[] offsets = calculateOffset(length, chunks, i);
string partialLink = format!"%s&range=%d-%d"(url, offsets[0], offsets[1]);
string partialDestination = format!"%s-%s-%d-%d.mp4.part.%d"(
title, id, offsets[0], offsets[1], i
string partialDestination = format!"%s-%s-%d-%d-%d.%s.part.%d"(
title, id, youtubeFormat.itag, offsets[0], offsets[1], youtubeFormat.extension, i
).sanitizePath();
destinations[i] = partialDestination;

Expand Down Expand Up @@ -133,7 +137,7 @@ class ParallelDownloader : Downloader

unittest
{
auto downloader = new ParallelDownloader(new StdoutLogger(), "", "");
auto downloader = new ParallelDownloader(new StdoutLogger(), "", "", YoutubeFormat(18, 9371359, "360p", "video/mp4"));
ulong length = 20 * 1024 * 1024;
assert([0, 5 * 1024 * 1024] == downloader.calculateOffset(length, 4, 0));
assert([5 * 1024 * 1024 + 1, 10 * 1024 * 1024] == downloader.calculateOffset(length, 4, 1));
Expand All @@ -143,7 +147,7 @@ class ParallelDownloader : Downloader

unittest
{
auto downloader = new ParallelDownloader(new StdoutLogger(), "", "");
auto downloader = new ParallelDownloader(new StdoutLogger(), "", "", YoutubeFormat(18, 9371359, "360p", "video/mp4"));
ulong length = 23;
assert([0, 5] == downloader.calculateOffset(length, 4, 0));
assert([5 + 1, 10] == downloader.calculateOffset(length, 4, 1));
Expand Down
30 changes: 28 additions & 2 deletions source/parsers.d
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import std.json;
import std.algorithm : canFind;
import std.net.curl : get;
import std.uri : decodeComponent, encodeComponent;
import std.stdio;
Expand All @@ -8,7 +7,8 @@ import std.conv : to;
import std.array : replace;
import std.file : readText;
import std.string : indexOf, format, lastIndexOf, split, strip;
import std.algorithm : reverse, map;
import std.algorithm : canFind, filter, reverse, map;
//import std.range : filter;

import helpers : parseQueryString, matchOrFail, StdoutLogger;

Expand Down Expand Up @@ -37,6 +37,17 @@ abstract class YoutubeVideoURLExtractor
return meta.attr("content").idup;
}

public YoutubeFormat getFormat(int itag)
{
YoutubeFormat[] formats = getFormats("formats") ~ getFormats("adaptiveFormats");
auto match = formats.filter!(format => format.itag == itag);
if(match.empty)
{
throw new Exception("Unknown itag : " ~ itag.to!string);
}
return match.front();
}

public YoutubeFormat[] getFormats()
{
return getFormats("formats") ~ getFormats("adaptiveFormats");
Expand Down Expand Up @@ -111,6 +122,17 @@ struct YoutubeFormat
string quality;
string mimetype;

string extension() @property nothrow
{
auto slashIndex = mimetype.indexOf("/");
auto semicolonIndex = mimetype.indexOf(";");
if(slashIndex == -1 || semicolonIndex == -1 || slashIndex >= semicolonIndex)
{
return "mp4";
}
return mimetype[slashIndex + 1 .. semicolonIndex];
}

string toString()
{
return format!`[%d] (%s) %s MB %s`(
Expand Down Expand Up @@ -149,6 +171,10 @@ unittest
assert(formats[15] == YoutubeFormat(249, 3314860, "tiny", `audio/webm; codecs="opus"`));
assert(formats[16] == YoutubeFormat(250, 4347447, "tiny", `audio/webm; codecs="opus"`));
assert(formats[17] == YoutubeFormat(251, 8650557, "tiny", `audio/webm; codecs="opus"`));

assert(YoutubeFormat(278, 6583212, "144p", `video/webm; codecs="vp9"`).extension == "webm");
assert(YoutubeFormat(140, 9371359, "tiny", `audio/mp4; codecs="mp4a.40.2"`).extension == "mp4");
assert(YoutubeFormat(140, 9371359, "unknown", `foobar`).extension == "mp4");
}

class AdvancedYoutubeVideoURLExtractor : YoutubeVideoURLExtractor
Expand Down

0 comments on commit b54e0e5

Please sign in to comment.