🔀 Merge some services from spotube

This commit is contained in:
2024-08-26 23:21:22 +08:00
parent 80771e84ce
commit 84d66fbc4b
28 changed files with 2517 additions and 4 deletions

View File

@ -0,0 +1,12 @@
enum SearchMode {
youtube._("YouTube"),
youtubeMusic._("YouTube Music");
final String label;
const SearchMode._(this.label);
factory SearchMode.fromString(String key) {
return SearchMode.values.firstWhere((e) => e.name == key);
}
}

View File

@ -0,0 +1,33 @@
import 'package:json_annotation/json_annotation.dart';
part 'source_info.g.dart';
@JsonSerializable()
class SourceInfo {
final String id;
final String title;
final String artist;
final String artistUrl;
final String? album;
final String thumbnail;
final String pageUrl;
final Duration duration;
SourceInfo({
required this.id,
required this.title,
required this.artist,
required this.thumbnail,
required this.pageUrl,
required this.duration,
required this.artistUrl,
this.album,
});
factory SourceInfo.fromJson(Map<String, dynamic> json) =>
_$SourceInfoFromJson(json);
Map<String, dynamic> toJson() => _$SourceInfoToJson(this);
}

View File

@ -0,0 +1,30 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'source_info.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
SourceInfo _$SourceInfoFromJson(Map json) => SourceInfo(
id: json['id'] as String,
title: json['title'] as String,
artist: json['artist'] as String,
thumbnail: json['thumbnail'] as String,
pageUrl: json['pageUrl'] as String,
duration: Duration(microseconds: json['duration'] as int),
artistUrl: json['artistUrl'] as String,
album: json['album'] as String?,
);
Map<String, dynamic> _$SourceInfoToJson(SourceInfo instance) =>
<String, dynamic>{
'id': instance.id,
'title': instance.title,
'artist': instance.artist,
'artistUrl': instance.artistUrl,
'album': instance.album,
'thumbnail': instance.thumbnail,
'pageUrl': instance.pageUrl,
'duration': instance.duration.inMicroseconds,
};

View File

@ -0,0 +1,58 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:rhythm_box/services/sourced_track/enums.dart';
part 'source_map.g.dart';
@JsonSerializable()
class SourceQualityMap {
final String high;
final String medium;
final String low;
const SourceQualityMap({
required this.high,
required this.medium,
required this.low,
});
factory SourceQualityMap.fromJson(Map<String, dynamic> json) =>
_$SourceQualityMapFromJson(json);
Map<String, dynamic> toJson() => _$SourceQualityMapToJson(this);
operator [](SourceQualities key) {
switch (key) {
case SourceQualities.high:
return high;
case SourceQualities.medium:
return medium;
case SourceQualities.low:
return low;
}
}
}
@JsonSerializable()
class SourceMap {
final SourceQualityMap? weba;
final SourceQualityMap? m4a;
const SourceMap({
this.weba,
this.m4a,
});
factory SourceMap.fromJson(Map<String, dynamic> json) =>
_$SourceMapFromJson(json);
Map<String, dynamic> toJson() => _$SourceMapToJson(this);
operator [](SourceCodecs key) {
switch (key) {
case SourceCodecs.weba:
return weba;
case SourceCodecs.m4a:
return m4a;
}
}
}

View File

@ -0,0 +1,36 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'source_map.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
SourceQualityMap _$SourceQualityMapFromJson(Map json) => SourceQualityMap(
high: json['high'] as String,
medium: json['medium'] as String,
low: json['low'] as String,
);
Map<String, dynamic> _$SourceQualityMapToJson(SourceQualityMap instance) =>
<String, dynamic>{
'high': instance.high,
'medium': instance.medium,
'low': instance.low,
};
SourceMap _$SourceMapFromJson(Map json) => SourceMap(
weba: json['weba'] == null
? null
: SourceQualityMap.fromJson(
Map<String, dynamic>.from(json['weba'] as Map)),
m4a: json['m4a'] == null
? null
: SourceQualityMap.fromJson(
Map<String, dynamic>.from(json['m4a'] as Map)),
);
Map<String, dynamic> _$SourceMapToJson(SourceMap instance) => <String, dynamic>{
'weba': instance.weba?.toJson(),
'm4a': instance.m4a?.toJson(),
};

View File

@ -0,0 +1,115 @@
import 'package:piped_client/piped_client.dart';
import 'package:rhythm_box/services/sourced_track/models/search.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class YoutubeVideoInfo {
final SearchMode searchMode;
final String title;
final Duration duration;
final String thumbnailUrl;
final String id;
final int likes;
final int dislikes;
final int views;
final String channelName;
final String channelId;
final DateTime publishedAt;
YoutubeVideoInfo({
required this.searchMode,
required this.title,
required this.duration,
required this.thumbnailUrl,
required this.id,
required this.likes,
required this.dislikes,
required this.views,
required this.channelName,
required this.publishedAt,
required this.channelId,
});
YoutubeVideoInfo.fromJson(Map<String, dynamic> json)
: title = json['title'],
searchMode = SearchMode.fromString(json['searchMode']),
duration = Duration(seconds: json['duration']),
thumbnailUrl = json['thumbnailUrl'],
id = json['id'],
likes = json['likes'],
dislikes = json['dislikes'],
views = json['views'],
channelName = json['channelName'],
channelId = json['channelId'],
publishedAt = DateTime.tryParse(json['publishedAt']) ?? DateTime.now();
Map<String, dynamic> toJson() => {
'title': title,
'duration': duration.inSeconds,
'thumbnailUrl': thumbnailUrl,
'id': id,
'likes': likes,
'dislikes': dislikes,
'views': views,
'channelName': channelName,
'channelId': channelId,
'publishedAt': publishedAt.toIso8601String(),
'searchMode': searchMode.name,
};
factory YoutubeVideoInfo.fromVideo(Video video) {
return YoutubeVideoInfo(
searchMode: SearchMode.youtube,
title: video.title,
duration: video.duration ?? Duration.zero,
thumbnailUrl: video.thumbnails.mediumResUrl,
id: video.id.value,
likes: video.engagement.likeCount ?? 0,
dislikes: video.engagement.dislikeCount ?? 0,
views: video.engagement.viewCount,
channelName: video.author,
channelId: '/c/${video.channelId.value}',
publishedAt: video.uploadDate ?? DateTime(2003, 9, 9),
);
}
factory YoutubeVideoInfo.fromSearchItemStream(
PipedSearchItemStream searchItem,
SearchMode searchMode,
) {
return YoutubeVideoInfo(
searchMode: searchMode,
title: searchItem.title,
duration: searchItem.duration,
thumbnailUrl: searchItem.thumbnail,
id: searchItem.id,
likes: 0,
dislikes: 0,
views: searchItem.views,
channelName: searchItem.uploaderName,
channelId: searchItem.uploaderUrl ?? "",
publishedAt: searchItem.uploadedDate != null
? DateTime.tryParse(searchItem.uploadedDate!) ?? DateTime(2003, 9, 9)
: DateTime(2003, 9, 9),
);
}
factory YoutubeVideoInfo.fromStreamResponse(
PipedStreamResponse stream, SearchMode searchMode) {
return YoutubeVideoInfo(
searchMode: searchMode,
title: stream.title,
duration: stream.duration,
thumbnailUrl: stream.thumbnailUrl,
id: stream.id,
likes: stream.likes,
dislikes: stream.dislikes,
views: stream.views,
channelName: stream.uploader,
publishedAt: stream.uploadedDate != null
? DateTime.tryParse(stream.uploadedDate!) ?? DateTime(2003, 9, 9)
: DateTime(2003, 9, 9),
channelId: stream.uploaderUrl,
);
}
}