Skip to content

Instantly share code, notes, and snippets.

@donghyuck
Created December 12, 2022 13:57
Show Gist options
  • Save donghyuck/235b88366aa3c9b597944d33ad9e256f to your computer and use it in GitHub Desktop.
Save donghyuck/235b88366aa3c9b597944d33ad9e256f to your computer and use it in GitHub Desktop.
Youtube Downloader
package architecture.community.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends Exception {
public NotFoundException() {
}
public NotFoundException(String message) {
super(message);
}
public NotFoundException(Throwable cause) {
super(cause);
}
public NotFoundException(String message, Throwable cause) {
super(message, cause);
}
public NotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<beans:description><![CDATA[
Services Defined Context ..
]]></beans:description>
<!-- ================================= -->
<!-- Youtube -->
<!-- ================================= -->
<beans:bean id="youtubeService" class="architecture.studio.services.YoutubeService" />
</beans:beans>
package architecture.studio.web.spring.controller.data;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.github.kiulian.downloader.model.videos.VideoInfo;
import com.github.kiulian.downloader.model.videos.formats.Format;
import architecture.community.exception.NotFoundException;
import architecture.studio.services.YoutubeService;
@Controller("services-youtube-data-controller")
@RequestMapping("/data/youtube")
public class YoutubeDataController {
private Logger log = LoggerFactory.getLogger(getClass());
@Autowired
@Qualifier("youtubeService")
private YoutubeService youtubeService;
@RequestMapping(value = {"/{videoId}", "/{videoId}/info.json" }, method = { RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public VideoInfo getVideoInfo(@PathVariable("videoId") String videoId,
HttpServletRequest request, HttpServletResponse response) throws NotFoundException {
log.info("service : {}", youtubeService);
VideoInfo videoInfo = youtubeService.getVideoInfo(videoId);
log.info("video : {} : {}", videoId, videoInfo);
return videoInfo;
}
@RequestMapping(value = {"/{videoId}/format:{format}" }, method = { RequestMethod.GET })
@ResponseBody
public ResponseEntity<InputStreamResource> download( @PathVariable("videoId") String videoId, @PathVariable("format") String format,
HttpServletRequest request, HttpServletResponse response) throws IOException, NotFoundException {
VideoInfo videoInfo = youtubeService.getVideoInfo(videoId);
log.debug("Youtube video : {} will download with format : {} ", videoId, format);
String formatToUse = StringUtils.defaultString(format, Format.AUDIO).toLowerCase();
Optional<Format> targetFormat = Optional.empty();
if( StringUtils.equals(formatToUse, Format.AUDIO))
{
targetFormat = Optional.of ( videoInfo.bestAudioFormat() );
}else if (StringUtils.equals(formatToUse, Format.VIDEO) ) {
targetFormat = Optional.of ( videoInfo.bestVideoFormat() );
}else if (StringUtils.equals(formatToUse, Format.AUDIO_VIDEO) ) {
targetFormat = Optional.of ( videoInfo.bestVideoWithAudioFormat() );
}
Format saveFormat = targetFormat.orElseThrow(() -> new IllegalArgumentException() );
File dir = youtubeService.getYoutubeDownlaodDir();
File videoDir = new File( dir, videoInfo.details().videoId() );
File saveTo = new File (videoDir, formatToUse );
if(!saveTo.exists()){
saveTo.mkdirs();
}
String filename = getFileName( videoInfo.details().title(), saveFormat );
log.debug("MineType : {} , name : {} ", saveFormat.mimeType(), filename );
File saveFile = null;
for( File file : FileUtils.listFiles(saveTo, new String[]{ saveFormat.extension().value() }, false)){
log.debug("exist file : {} ", file.getAbsolutePath() );
saveFile = file;
}
if( saveFile == null ){
saveFile = youtubeService.downloadVideoFile( saveFormat, saveTo);
}
int fileSize = (int) FileUtils.sizeOf(saveFile);
InputStream is = new BufferedInputStream(new FileInputStream(saveFile));
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Transfer-Encoding", "binary")
.header("Content-Disposition", "attachment; fileName=\"" + getURLEncodedFileName( filename ) +"\";" )
.contentLength(fileSize)
.body(new InputStreamResource(is));
}
private String getFileName( String title, Format format ){
StringBuilder sb = new StringBuilder();
sb.append(sanitizeFilename(title));
sb.append(".").append(format.extension().value());
return sb.toString();
}
private String sanitizeFilename(String inputName) {
return inputName.replaceAll("[^a-zA-Z0-9-_\\.]", "_");
}
private String getURLEncodedFileName(String name) {
try {
return URLEncoder.encode(name, "UTF-8");
} catch (UnsupportedEncodingException e) {
return name;
}
}
}
package architecture.studio.services;
import java.io.File;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import com.github.kiulian.downloader.downloader.request.RequestVideoFileDownload;
import com.github.kiulian.downloader.downloader.request.RequestVideoInfo;
import com.github.kiulian.downloader.downloader.response.Response;
import com.github.kiulian.downloader.model.videos.VideoInfo;
import com.github.kiulian.downloader.model.videos.formats.Format;
import architecture.community.exception.NotFoundException;
public class YoutubeService {
private String DEFAULT_DOWNLOAD_DIR = "/data/download";
private com.github.kiulian.downloader.YoutubeDownloader downloader = new com.github.kiulian.downloader.YoutubeDownloader();
protected Logger log = LoggerFactory.getLogger(getClass().getName());
private File downloadDir;
public void initialize() throws Exception {
getYoutubeDownlaodDir();
}
public File downloadVideoFile(Format format , File saveTo) {
Response<File> responseFile = downloader.downloadVideoFile(new RequestVideoFileDownload(format).saveTo(saveTo));
return responseFile.data();
}
public VideoInfo getVideoInfo(String videoId) throws NotFoundException {
Response<VideoInfo> response = downloader.getVideoInfo( new RequestVideoInfo(videoId));
return response.data();
}
public File getYoutubeDownlaodDir() {
File dir = new File(getDownloadDir(), "youtube");
if (!dir.exists()) {
dir.mkdir();
}
return dir;
}
protected synchronized File getDownloadDir() {
if (downloadDir == null) {
downloadDir = DEFAULT_DOWNLOAD_DIR ;/** 프로퍼티 또는 설정을 사용하여 다운로드를 위한 경로를 지정한다. */
if (!downloadDir.exists()) {
boolean result = downloadDir.mkdir();
if (!result)
log.error((new StringBuilder()).append("Unable to create download directory: '").append(downloadDir)
.append("'").toString());
}
}
return downloadDir;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment