diff --git a/lib/services/sourced_track/enums.dart b/lib/services/sourced_track/enums.dart index 9ef6c080..d9ea079c 100644 --- a/lib/services/sourced_track/enums.dart +++ b/lib/services/sourced_track/enums.dart @@ -9,9 +9,20 @@ enum SourceCodecs { } enum SourceQualities { - high, - medium, - low, + high(2), + medium(1), + low(0); + + final int priority; + const SourceQualities(this.priority); + + bool operator <(SourceQualities other) { + return priority < other.priority; + } + + operator >(SourceQualities other) { + return priority > other.priority; + } } typedef SiblingType = ({ diff --git a/lib/services/sourced_track/sourced_track.dart b/lib/services/sourced_track/sourced_track.dart index b4e52767..d979c007 100644 --- a/lib/services/sourced_track/sourced_track.dart +++ b/lib/services/sourced_track/sourced_track.dart @@ -179,7 +179,7 @@ abstract class SourcedTrack extends BasicSourcedTrack { }).toList(); if (sameCodecSources.isNotEmpty) { - return preferences.audioQuality != SourceQualities.low + return preferences.audioQuality > SourceQualities.low ? sameCodecSources.first.url : sameCodecSources.last.url; } @@ -190,7 +190,7 @@ abstract class SourcedTrack extends BasicSourcedTrack { return aDiff != bDiff ? aDiff - bDiff : a.quality.index - b.quality.index; }); - return preferences.audioQuality != SourceQualities.low + return preferences.audioQuality > SourceQualities.low ? fallbackSource.firstOrNull?.url : fallbackSource.lastOrNull?.url; } diff --git a/lib/services/sourced_track/sources/youtube.dart b/lib/services/sourced_track/sources/youtube.dart index 66b4c5d6..83eba6af 100644 --- a/lib/services/sourced_track/sources/youtube.dart +++ b/lib/services/sourced_track/sources/youtube.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:dio/dio.dart'; import 'package:drift/drift.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotube/models/database/database.dart'; @@ -6,6 +7,7 @@ import 'package:spotube/models/playback/track_sources.dart'; import 'package:spotube/provider/database/database.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/youtube_engine/youtube_engine.dart'; +import 'package:spotube/services/dio/dio.dart'; import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/song_link/song_link.dart'; import 'package:spotube/services/sourced_track/enums.dart'; @@ -389,14 +391,32 @@ class YoutubeSourcedTrack extends SourcedTrack { @override Future refreshStream() async { - final manifest = - await ref.read(youtubeEngineProvider).getStreamManifest(info.id); + List validStreams = []; + + for (final source in sources) { + final res = await globalDio.head( + source.url, + options: + Options(validateStatus: (status) => status != null && status < 500), + ); + + if (res.statusCode! < 400) { + validStreams.add(source); + } + } + + if (validStreams.isEmpty) { + final manifest = + await ref.read(youtubeEngineProvider).getStreamManifest(info.id); + + validStreams = toTrackSources(manifest); + } final sourcedTrack = YoutubeSourcedTrack( ref: ref, siblings: siblings, source: source, - sources: toTrackSources(manifest), + sources: validStreams, info: info, query: query, ); diff --git a/lib/services/youtube_engine/youtube_explode_engine.dart b/lib/services/youtube_engine/youtube_explode_engine.dart index 2160c629..fa58314c 100644 --- a/lib/services/youtube_engine/youtube_explode_engine.dart +++ b/lib/services/youtube_engine/youtube_explode_engine.dart @@ -1,9 +1,6 @@ import 'dart:isolate'; -import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; -import 'package:spotube/services/dio/dio.dart'; -import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/youtube_engine/youtube_engine.dart'; import 'package:youtube_explode_dart/youtube_explode_dart.dart'; @@ -170,54 +167,31 @@ class YouTubeExplodeEngine implements YouTubeEngine { ], ); - final accessibleStreams = []; - final stringBuffer = StringBuffer(); + final audioStreams = streamManifest.audioOnly.where( + (stream) => stream.bitrate.bitsPerSecond >= 40960, + ); - for (final stream in streamManifest.audioOnly) { - // Call dio head request to check if the stream is accessible - final response = await globalDio.headUri( - stream.url, - options: Options( - followRedirects: true, - validateStatus: (status) { - return status != null && status < 500; + return StreamManifest( + audioStreams.map( + (stream) => AudioOnlyStreamInfo( + stream.videoId, + stream.tag, + stream.url, + stream.container, + stream.size, + stream.bitrate, + stream.audioCodec, + switch (stream.bitrate.bitsPerSecond) { + > 130 * 1024 => "high", + > 64 * 1024 => "medium", + _ => "low", }, + stream.fragments, + stream.codec, + stream.audioTrack, ), - ); - - stringBuffer.writeln( - "Stream $videoId Status ${response.statusCode} Codec ${stream.audioCodec} " - "Bitrate ${stream.bitrate} Container ${stream.container}", - ); - - if (response.statusCode != null && - response.statusCode! >= 200 && - response.statusCode! < 400) { - accessibleStreams.add( - AudioOnlyStreamInfo( - stream.videoId, - stream.tag, - stream.url, - stream.container, - stream.size, - stream.bitrate, - stream.audioCodec, - switch (stream.bitrate.bitsPerSecond) { - > 130 * 1024 => "high", - > 64 * 1024 => "medium", - _ => "low", - }, - stream.fragments, - stream.codec, - stream.audioTrack, - ), - ); - } - } - - AppLogger.log.d(stringBuffer.toString()); - - return StreamManifest(accessibleStreams); + ), + ); } @override