Skip to content

whd793/podcast-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

95 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎧 PodStream

Screen Shot 2025-02-20 at 4 20 35 AM


ν•œκ΅­μ–΄

🎧 PodStream - 팟캐슀트 슀트리밍 ν”Œλž«νΌ

πŸ“Œ ν”„λ‘œμ νŠΈ μ†Œκ°œ

PodStream은 μ‚¬μš©μžλ“€μ΄ μ˜€λ””μ˜€μ™€ λΉ„λ””μ˜€ 팟캐슀트λ₯Ό μ‰½κ²Œ κ³΅μœ ν•˜κ³  μ‹œμ²­/μ²­μ·¨ν•  수 μžˆλŠ” 슀트리밍 ν”Œλž«νΌμž…λ‹ˆλ‹€. React와 Node.jsλ₯Ό 기반으둜 κ°œλ°œλ˜μ—ˆμœΌλ©°, 직관적인 UI/UX와 μ•ˆμ •μ μΈ 슀트리밍 μ„œλΉ„μŠ€λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

⭐ μ£Όμš” κΈ°λŠ₯

μ‚¬μš©μž 인증

  • JWT 기반 둜그인/νšŒμ›κ°€μž…
  • 카카였 OAuth μ†Œμ…œ 둜그인
  • 이메일 인증 및 λΉ„λ°€λ²ˆν˜Έ μž¬μ„€μ •

λ―Έλ””μ–΄ ν”Œλ ˆμ΄μ–΄

  • μ»€μŠ€ν…€ μ˜€λ””μ˜€/λΉ„λ””μ˜€ ν”Œλ ˆμ΄μ–΄
  • μž¬μƒ μ§„ν–‰λ₯  μ‹€μ‹œκ°„ 좔적
  • λ³Όλ₯¨ 쑰절 및 μž¬μƒ μ œμ–΄
  • 이전/λ‹€μŒ μ—ν”Όμ†Œλ“œ μ „ν™˜

μ½˜ν…μΈ  관리

  • 팟캐슀트 μ—…λ‘œλ“œ 및 관리
  • μ—ν”Όμ†Œλ“œ μΆ”κ°€ 및 μˆ˜μ •
  • μΉ΄ν…Œκ³ λ¦¬λ³„ μ½˜ν…μΈ  λΆ„λ₯˜
  • 즐겨찾기 κΈ°λŠ₯

λ‹€κ΅­μ–΄ 지원

  • ν•œκ΅­μ–΄/μ˜μ–΄ 지원
  • μ‚¬μš©μž μœ„μΉ˜ 기반 μžλ™ μ–Έμ–΄ μ„€μ •

πŸ›  기술 μŠ€νƒ

ν”„λ‘ νŠΈμ—”λ“œ

  • React
  • Redux Toolkit (μƒνƒœ 관리)
  • Styled-Components (μŠ€νƒ€μΌλ§)
  • Material-UI (UI μ»΄ν¬λ„ŒνŠΈ)
  • i18next (λ‹€κ΅­μ–΄ 처리)

λ°±μ—”λ“œ

  • Node.js
  • Express.js
  • MongoDB (λ°μ΄ν„°λ² μ΄μŠ€)
  • JWT (인증)
  • AWS S3 (파일 μŠ€ν† λ¦¬μ§€)

πŸ’» μ„€μΉ˜ 및 μ‹€ν–‰ 방법

ν”„λ‘ νŠΈμ—”λ“œ μ‹€ν–‰

# μ €μž₯μ†Œ 클둠
git clone [μ €μž₯μ†Œ URL]

# 디렉토리 이동
cd podstream/frontend

# μ˜μ‘΄μ„± μ„€μΉ˜
npm install

# 개발 μ„œλ²„ μ‹€ν–‰
npm start

λ°±μ—”λ“œ μ‹€ν–‰

# 디렉토리 이동
cd podstream/backend

# μ˜μ‘΄μ„± μ„€μΉ˜
npm install

# μ„œλ²„ μ‹€ν–‰
npm start

πŸ”§ ν™˜κ²½ λ³€μˆ˜ μ„€μ •

# Backend .env
MONGODB_URL=your_mongodb_url
JWT_SECRET=your_jwt_secret
AWS_S3_BUCKET=your_bucket_name
AWS_ACCESS_KEY=your_access_key
AWS_SECRET_KEY=your_secret_key
EMAIL_SERVICE=your_email_service
EMAIL_USERNAME=your_email_username
EMAIL_PASSWORD=your_email_password
KAKAO_CLIENT_ID=your_kakao_client_id

# Frontend .env
REACT_APP_API_URL=your_api_url
REACT_APP_KAKAO_KEY=your_kakao_key

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰

podstream/
β”œβ”€β”€ frontend/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ pages/
β”‚   β”‚   β”œβ”€β”€ redux/
β”‚   β”‚   β”œβ”€β”€ utils/
β”‚   β”‚   └── App.js
β”‚   └── package.json
β”‚
└── backend/
    β”œβ”€β”€ controllers/
    β”œβ”€β”€ models/
    β”œβ”€β”€ routes/
    β”œβ”€β”€ middleware/
    └── server.js

πŸ” μ£Όμš” API

인증 κ΄€λ ¨

  • POST /api/auth/signup - νšŒμ›κ°€μž…
  • POST /api/auth/signin - 둜그인
  • POST /api/auth/kakao - 카카였 둜그인
  • GET /api/auth/generateotp - OTP 생성
  • GET /api/auth/verifyotp - OTP 검증

팟캐슀트 κ΄€λ ¨

  • POST /api/podcasts - 팟캐슀트 생성
  • GET /api/podcasts - 팟캐슀트 λͺ©λ‘ 쑰회
  • POST /api/podcasts/episode - μ—ν”Όμ†Œλ“œ μΆ”κ°€
  • POST /api/podcasts/favorit/:id - 즐겨찾기 ν† κΈ€
  • GET /api/podcasts/search - 팟캐슀트 검색

πŸ”’ λ³΄μ•ˆ κΈ°λŠ₯

  • JWT 기반 인증
  • λΉ„λ°€λ²ˆν˜Έ μ•”ν˜Έν™”
  • 이메일 인증
  • OAuth λ³΄μ•ˆ

🌟 핡심 κ΅¬ν˜„ 사항

λ―Έλ””μ–΄ ν”Œλ ˆμ΄μ–΄

const AudioPlayer = ({ episode, currenttime }) => {
  // μž¬μƒ μƒνƒœ 관리
  // μ§„ν–‰λ₯  좔적
  // λ³Όλ₯¨ μ œμ–΄
};

μƒνƒœ 관리

const audioPlayerSlice = createSlice({
  name: 'audioplayer',
  initialState,
  reducers: {
    // ν”Œλ ˆμ΄μ–΄ μƒνƒœ 관리
    // μž¬μƒ μ œμ–΄
  }
});

인증 μ‹œμŠ€ν…œ

// 카카였 OAuth 및 이메일 인증 κ΅¬ν˜„
export const kakaoAuthSignIn = async (req, res, next) => {
  try {
    const user = await User.findOne({ email: req.body.email });
    if (!user) {
      // μƒˆ μ‚¬μš©μž 생성 둜직
      const user = new User({ ...req.body, kakaoSignIn: true });
      await user.save();
      const token = jwt.sign({ id: user._id }, process.env.JWT);
      res.status(200).json({ token, user: user });
    }
    // ... κΈ°μ‘΄ μ‚¬μš©μž 처리 둜직
  } catch (err) {
    next(err);
  }
};

λ―Έλ””μ–΄ ν”Œλ ˆμ΄μ–΄

// μ»€μŠ€ν…€ μ˜€λ””μ˜€ ν”Œλ ˆμ΄μ–΄ κ΅¬ν˜„
const AudioPlayer = ({ episode, podid, currenttime, index }) => {
  const [isPlaying, setIsPlaying] = useState(false);
  const [volume, setVolume] = useState(1);
  const audioRef = useRef(null);

  const handleTimeUpdate = () => {
    dispatch(
      setCurrentTime({
        currenttime: audioRef.current.currentTime,
      })
    );
  };

  // 이전/λ‹€μŒ μ—ν”Όμ†Œλ“œ 처리
  const goToNextPodcast = () => {
    if (podid.episodes.length === index + 1) {
      dispatch(
        openSnackbar({
          message: 'This is the last episode',
          severity: 'info',
        })
      );
      return;
    }
    // ... λ‹€μŒ μ—ν”Όμ†Œλ“œ μž¬μƒ 둜직
  };
};

λ°μ΄ν„°λ² μ΄μŠ€ ꡬ쑰

// μ΅œμ ν™”λœ MongoDB μŠ€ν‚€λ§ˆ
const PodcastsSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  desc: String,
  thumbnail: String,
  creator: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
  },
  tags: [String],
  type: String,
  category: String,
  views: {
    type: Number,
    default: 0,
  },
  episodes: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Episodes',
  }]
});

μƒνƒœ 관리

// Reduxλ₯Ό ν™œμš©ν•œ μƒνƒœ 관리
const audioplayer = createSlice({
  name: 'audioplayer',
  initialState: {
    openplayer: false,
    type: 'audio',
    episode: null,
    podid: null,
    currenttime: 0,
    index: 0,
  },
  reducers: {
    openPlayer: (state, action) => {
      state.openplayer = true;
      state.type = action.payload.type;
      state.episode = action.payload.episode;
      // ... 기타 μƒνƒœ μ—…λ°μ΄νŠΈ
    },
  },
});

λ‹€κ΅­μ–΄ 지원

useEffect(() => {
  fetch('https://ipapi.co/json/')
    .then((response) => response.json())
    .then((data) => {
      const userCountry = data.country_code;
      if (userCountry === 'KR') {
        i18n.changeLanguage('ko');
      } else {
        i18n.changeLanguage('en');
      }
    })
    .catch(() => i18n.changeLanguage('en'));
}, []);

🎯 ν”„λ‘œμ νŠΈ μ„±κ³Ό

  • μ‚¬μš©μž 인증 및 κΆŒν•œ 관리 μ‹œμŠ€ν…œ ꡬ좕
  • μ•ˆμ •μ μΈ λ―Έλ””μ–΄ 슀트리밍 μ„œλΉ„μŠ€ κ΅¬ν˜„
  • λ‹€κ΅­μ–΄ μ§€μ›μœΌλ‘œ μ„œλΉ„μŠ€ μ ‘κ·Όμ„± ν–₯상
  • 직관적인 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€ 섀계

πŸ“ ν”„λ‘œμ νŠΈλ₯Ό 톡해 배운 점

  1. React와 Node.jsλ₯Ό ν™œμš©ν•œ ν’€μŠ€νƒ 개발 κ²½ν—˜
  2. JWTλ₯Ό ν™œμš©ν•œ 인증 μ‹œμŠ€ν…œ κ΅¬ν˜„ 방법
  3. λ―Έλ””μ–΄ 슀트리밍 μ„œλΉ„μŠ€ 개발 λ…Έν•˜μš°
  4. MongoDBλ₯Ό ν™œμš©ν•œ 데이터 λͺ¨λΈλ§
  5. μƒνƒœ 관리 라이브러리 ν™œμš© 방법

πŸš€ ν–₯ν›„ κ°œμ„  κ³„νš

  1. 검색 κΈ°λŠ₯ κ°•ν™”
  2. μ‚¬μš©μž μƒν˜Έμž‘μš© κΈ°λŠ₯ μΆ”κ°€
  3. μ„±λŠ₯ μ΅œμ ν™”
  4. λͺ¨λ°”일 λ°˜μ‘μ„± κ°œμ„ 

πŸ“± μŠ€ν¬λ¦°μƒ·

ν™ˆ ν™”λ©΄ 팟캐슀트 λͺ©λ‘ μ—ν”Όμ†Œλ“œ ν™”λ©΄

ν”Œλ ˆμ΄μ–΄ ν™”λ©΄ 검색 κ²°κ³Ό μ„€μ • ν™”λ©΄

Screen Shot 2025-02-20 at 4 20 35 AM

πŸš€ 배포 정보

  • Frontend: Vercel
  • Backend: Render
  • Database: MongoDB Atlas
  • Storage: AWS S3

πŸš€ ν–₯ν›„ 개발 κ³„νš

  1. 기술적 κ°œμ„ 

    • GraphQL λ„μž… κ²€ν† 
    • μ‹€μ‹œκ°„ 처리 μ‹œμŠ€ν…œ κ°•ν™”
    • λ¨Έμ‹ λŸ¬λ‹ 기반 μΆ”μ²œ μ‹œμŠ€ν…œ κ΅¬ν˜„
  2. μƒˆλ‘œμš΄ κΈ°λŠ₯

    • μ‹€μ‹œκ°„ μ±„νŒ… μ‹œμŠ€ν…œ
    • κ³ κΈ‰ 검색 κΈ°λŠ₯
    • μ†Œμ…œ κΈ°λŠ₯ ν™•μž₯

πŸ“ κ²°λ‘ 

이 ν”„λ‘œμ νŠΈλ₯Ό 톡해 ν˜„λŒ€μ μΈ μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 개발의 전체 라이프사이클을 κ²½ν—˜ν•˜λ©°, 특히 μ„±λŠ₯ μ΅œμ ν™”μ™€ μ‚¬μš©μž κ²½ν—˜ κ°œμ„ μ— 쀑점을 두어 μ‹€μ œ μ„œλΉ„μŠ€ μˆ˜μ€€μ˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€. 지속적인 κ°œμ„ κ³Ό μƒˆλ‘œμš΄ 기술 λ„μž…μ„ 톡해 더 λ‚˜μ€ μ„œλΉ„μŠ€λ₯Ό μ œκ³΅ν•˜κΈ° μœ„ν•΄ λ…Έλ ₯ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

πŸ‘₯ κΈ°μ—¬ 방법

  1. ν”„λ‘œμ νŠΈ 포크
  2. κΈ°λŠ₯ 개발용 브랜치 생성
  3. λ³€κ²½ 사항 컀밋
  4. ν’€ λ¦¬ν€˜μŠ€νŠΈ μš”μ²­

πŸ“ λΌμ΄μ„ μŠ€

MIT License

πŸ“ž 문의


English

πŸŽ™οΈ PodStream - Modern Podcast Streaming Platform

PodStream is a full-stack podcast streaming platform that allows users to discover, listen to, and manage their favorite podcasts. Built with modern web technologies, it offers a seamless experience for both audio and video podcast content.

🌟 Features

User Features

  • πŸ” Secure authentication (Email/Password & Kakao OAuth)
  • πŸ“± Responsive design for all devices
  • 🎧 Audio & Video podcast streaming
  • ❀️ Favorite podcasts functionality
  • πŸ” Advanced search and filtering
  • 🏷️ Category-based browsing
  • πŸ“Š View count tracking
  • 🌐 Multi-language support (English & Korean)

Content Creator Features

  • πŸ“€ Upload audio/video podcasts
  • πŸ“ Episode management
  • πŸ“Έ Thumbnail uploads
  • 🏷️ Add tags and categories
  • πŸ“Š View statistics

Technical Features

  • πŸ”„ Real-time progress tracking
  • 🎚️ Volume control
  • ⌨️ Keyboard controls for playback
  • πŸ”” Toast notifications
  • πŸ”’ JWT authentication
  • πŸ“§ Email verification system
  • πŸŒ“ Dark/Light theme support

πŸ› οΈ Technology Stack

Frontend

  • React.js - UI Library
  • Redux Toolkit - State Management
  • Styled Components - Styling
  • Material UI - UI Components
  • i18next - Internationalization
  • React Router - Navigation
  • Axios - HTTP Client

Backend

  • Node.js & Express.js - Server Framework
  • MongoDB & Mongoose - Database
  • JWT - Authentication
  • Bcrypt - Password Hashing
  • Nodemailer - Email Service
  • AWS S3 - File Storage

πŸ“Š Database Schema

User Model

{
  name: String,
  email: String,
  password: String,
  img: String,
  kakaoSignIn: Boolean,
  podcasts: [PodcastId],
  favorits: [PodcastId]
}

Podcast Model

{
  name: String,
  desc: String,
  thumbnail: String,
  creator: UserId,
  tags: [String],
  type: String,
  category: String,
  views: Number,
  episodes: [EpisodeId]
}

Episode Model

{
  name: String,
  desc: String,
  thumbnail: String,
  creator: UserId,
  type: String,
  duration: String,
  file: String
}

πŸš€ API Endpoints

Authentication

  • POST /auth/signup - User registration
  • POST /auth/signin - User login
  • POST /auth/kakao - Kakao OAuth
  • GET /auth/generateotp - Generate OTP for verification
  • GET /auth/verifyotp - Verify OTP

Podcasts

  • GET /podcasts - Get all podcasts
  • GET /podcasts/get/:id - Get specific podcast
  • POST /podcasts - Create podcast
  • POST /podcasts/episode - Add episode
  • POST /podcasts/favorit/:id - Toggle favorite
  • GET /podcasts/search - Search podcasts
  • GET /podcasts/category - Get by category

πŸ”₯ Key Features Implementation

Real-time Audio/Video Player

const AudioPlayer = ({ episode, podid, currenttime, index }) => {
  // Custom player with progress tracking and volume control
  // Keyboard shortcuts for playback control
  // Automatic progress saving
}

Authentication Flow

const signin = async (req, res, next) => {
  // Email/Password validation
  // JWT token generation
  // Session management
}

File Upload System

const uploadFile = async (file) => {
  // AWS S3 integration
  // Progress tracking
  // File validation
}

πŸ“± οΏ½Screenshots

ν™ˆ ν™”λ©΄ 팟캐슀트 λͺ©λ‘ μ—ν”Όμ†Œλ“œ ν™”λ©΄

ν”Œλ ˆμ΄μ–΄ ν™”λ©΄ 검색 κ²°κ³Ό μ„€μ • ν™”λ©΄

Screen Shot 2025-02-20 at 4 20 35 AM

πŸ”§ Installation & Setup

  1. Clone the repository
git clone https://github.com/yourusername/podstream.git
  1. Install dependencies
cd podstream
npm install
  1. Environment Variables
MONGODB_URL=your_mongodb_url
JWT_SECRET=your_jwt_secret
AWS_S3_BUCKET=your_s3_bucket
EMAIL_SERVICE_CREDENTIALS=your_email_credentials
  1. Run the application
npm run dev  # Development
npm start    # Production

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“ License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❀️ by Won Lee

About

Podcast streaming application - provides users with an easy-to-use platform for listening to their favorite podcasts..

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages