-
-
Save donghyuck/235b88366aa3c9b597944d33ad9e256f to your computer and use it in GitHub Desktop.
Youtube Downloader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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